Created
January 10, 2016 12:27
-
-
Save panda2134/8aac9a1923e088e6f1bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
OpenPrograms package manager, browser and downloader, for easy access to many programs | |
Author: Vexatos | |
Warning! This file is just an auto-installer for OPPM! | |
DO NOT EVER TRY TO INSTALL A PACKAGE WITH THIS! | |
Once you ran OPPM once, you can remove the floppy disk | |
and run the installed OPPM version just fine. | |
]] | |
local component = require("component") | |
local event = require("event") | |
local fs = require("filesystem") | |
local process = require("process") | |
local serial = require("serialization") | |
local shell = require("shell") | |
local term = require("term") | |
local gpu = component.gpu | |
local internet | |
local wget | |
local args, options = shell.parse(...) | |
local function getInternet() | |
if not component.isAvailable("internet") then | |
io.stderr:write("This program requires an internet card to run.") | |
return false | |
end | |
internet = require("internet") | |
wget = loadfile("/bin/wget.lua") | |
return true | |
end | |
local function printUsage() | |
print("OpenPrograms Package Manager, use this to browse through and download OpenPrograms programs easily") | |
print("Usage:") | |
print("'oppm list [-i]' to get a list of all the available program packages") | |
print("'oppm list [-i] <filter>' to get a list of available packages containing the specified substring") | |
print(" -i: Only list already installed packages") | |
print("'oppm info <package>' to get further information about a program package") | |
print("'oppm install [-f] <package> [path]' to download a package to a directory on your system (or /usr by default)") | |
print("'oppm update <package>' to update an already installed package") | |
print("'oppm update all' to update every already installed package") | |
print("'oppm uninstall <package>' to remove a package from your system") | |
print(" -f: Force creation of directories and overwriting of existing files.") | |
end | |
local function getContent(url) | |
local sContent = "" | |
local result, response = pcall(internet.request, url) | |
if not result then | |
return nil | |
end | |
for chunk in response do | |
sContent = sContent..chunk | |
end | |
return sContent | |
end | |
local function getRepos() | |
local success, sRepos = pcall(getContent,"https://raw.githubusercontent.com/OpenPrograms/openprograms.github.io/master/repos.cfg") | |
if not success then | |
io.stderr:write("Could not connect to the Internet. Please ensure you have an Internet connection.") | |
return -1 | |
end | |
return serial.unserialize(sRepos) | |
end | |
local function getPackages(repo) | |
local success, sPackages = pcall(getContent,"https://raw.githubusercontent.com/"..repo.."/master/programs.cfg") | |
if not success or not sPackages then | |
return -1 | |
end | |
return serial.unserialize(sPackages) | |
end | |
--For sorting table values by alphabet | |
local function compare(a,b) | |
for i=1,math.min(#a,#b) do | |
if a:sub(i,i)~=b:sub(i,i) then | |
return a:sub(i,i) < b:sub(i,i) | |
end | |
end | |
return #a < #b | |
end | |
local function downloadFile(url,path,force) | |
if options.f or force then | |
return wget("-fq",url,path) | |
else | |
if fs.exists(path) then | |
error("file already exists and option -f is not enabled") | |
end | |
return wget("-q",url,path) | |
end | |
end | |
local function readFromFile(fNum) | |
local path | |
if fNum == 1 then | |
path = "/etc/opdata.svd" | |
elseif fNum == 2 then | |
path = "/etc/oppm.cfg" | |
if not fs.exists(path) then | |
local tProcess = process.running() | |
path = fs.concat(fs.path(shell.resolve(tProcess)),"/etc/oppm.cfg") | |
end | |
end | |
if not fs.exists(fs.path(path)) then | |
fs.makeDirectory(fs.path(path)) | |
end | |
if not fs.exists(path) then | |
return {-1} | |
end | |
local file,msg = io.open(path,"rb") | |
if not file then | |
io.stderr:write("Error while trying to read file at "..path..": "..msg) | |
return | |
end | |
local sPacks = file:read("*a") | |
file:close() | |
return serial.unserialize(sPacks) or {-1} | |
end | |
local function saveToFile(packs) | |
local file,msg = io.open("/etc/opdata.svd","wb") | |
if not file then | |
io.stderr:write("Error while trying to save package names: "..msg) | |
return | |
end | |
local sPacks = serial.serialize(packs) | |
file:write(sPacks) | |
file:close() | |
end | |
local function listPackages(filter) | |
filter = filter or false | |
if filter then | |
filter = string.lower(filter) | |
end | |
local packages = {} | |
print("Receiving Package list...") | |
if not options.i then | |
local success, repos = pcall(getRepos) | |
if not success or repos==-1 then | |
io.stderr:write("Unable to connect to the Internet.\n") | |
return | |
elseif repos==nil then | |
print("Error while trying to receive repository list") | |
return | |
end | |
for _,j in pairs(repos) do | |
if j.repo then | |
print("Checking Repository "..j.repo) | |
local lPacks = getPackages(j.repo) | |
if lPacks==nil then | |
io.stderr:write("Error while trying to receive package list for " .. j.repo.."\n") | |
return | |
elseif type(lPacks) == "table" then | |
for k in pairs(lPacks) do | |
if not k.hidden then | |
table.insert(packages,k) | |
end | |
end | |
end | |
end | |
end | |
local lRepos = readFromFile(2) | |
if lRepos and lRepos.repos then | |
for _,j in pairs(lRepos.repos) do | |
for k in pairs(j) do | |
if not k.hidden then | |
table.insert(packages,k) | |
end | |
end | |
end | |
end | |
else | |
local lPacks = {} | |
local packs = readFromFile(1) | |
for i in pairs(packs) do | |
table.insert(lPacks,i) | |
end | |
packages = lPacks | |
end | |
if filter then | |
local lPacks = {} | |
for i,j in ipairs(packages) do | |
if (#j>=#filter) and string.find(j,filter,1,true)~=nil then | |
table.insert(lPacks,j) | |
end | |
end | |
packages = lPacks | |
end | |
table.sort(packages,compare) | |
return packages | |
end | |
local function printPackages(packs) | |
if packs==nil or not packs[1] then | |
print("No package matching specified filter found.") | |
return | |
end | |
term.clear() | |
local xRes,yRes = gpu.getResolution() | |
print("--OpenPrograms Package list--") | |
local xCur,yCur = term.getCursor() | |
for _,j in ipairs(packs) do | |
term.write(j.."\n") | |
yCur = yCur+1 | |
if yCur>yRes-1 then | |
term.write("[Press any key to continue]") | |
local event = event.pull("key_down") | |
if event then | |
term.clear() | |
print("--OpenPrograms Package list--") | |
xCur,yCur = term.getCursor() | |
end | |
end | |
end | |
end | |
local function parseFolders(pack, repo, info) | |
local function getFolderTable(repo, namePath, branch) | |
local success, filestring = pcall(getContent,"https://api.github.com/repos/"..repo.."/contents/"..namePath.."?ref="..branch) | |
if not success or filestring:find('"message": "Not Found"') then | |
io.stderr:write("Error while trying to parse folder names in declaration of package "..pack..".\n") | |
if filestring:find('"message": "Not Found"') then | |
io.stderr:write("Folder "..namePath.." does not exist.\n") | |
else | |
io.stderr:write(filestring.."\n") | |
end | |
io.stderr:write("Please contact the author of that package.\n") | |
return nil | |
end | |
return serial.unserialize(filestring:gsub("%[", "{"):gsub("%]", "}"):gsub("(\"[^%s,]-\")%s?:", "[%1] = "), nil) | |
end | |
local function nonSpecial(text) | |
return text:gsub("([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") | |
end | |
local function unserializeFiles(files, repo, namePath, branch, relPath) | |
if not files then return nil end | |
local tFiles = {} | |
for _,v in pairs(files) do | |
if v["type"] == "file" then | |
local newPath = v["download_url"]:gsub("https?://raw.githubusercontent.com/"..nonSpecial(repo).."(.+)$", "%1"):gsub("/*$",""):gsub("^/*","") | |
tFiles[newPath] = relPath | |
elseif v["type"] == "dir" then | |
local newFiles = unserializeFiles(getFolderTable(repo, relPath.."/"..v["name"], branch), repo, branch, fs.concat(relPath, v["name"])) | |
for p,q in pairs(newFiles) do | |
tFiles[p] = q | |
end | |
end | |
end | |
return tFiles | |
end | |
local newInfo = info | |
for i,j in pairs(info.files) do | |
if string.find(i,"^:") then | |
local iPath = i:gsub("^:","") | |
local branch = string.gsub(iPath,"^(.-)/.+","%1"):gsub("/*$",""):gsub("^/*","") | |
local namePath = string.gsub(iPath,".-(/.+)$","%1"):gsub("/*$",""):gsub("^/*","") | |
local absolutePath = j:find("^//") | |
local files = unserializeFiles(getFolderTable(repo, namePath, branch), repo, namePath, branch, j:gsub("^//","/")) | |
if not files then return nil end | |
for p,q in pairs(files) do | |
if absolutePath then | |
newInfo.files[p] = "/"..q | |
else | |
newInfo.files[p] = q | |
end | |
end | |
newInfo.files[i] = nil | |
end | |
end | |
return newInfo | |
end | |
local function getInformation(pack) | |
local success, repos = pcall(getRepos) | |
if not success or repos==-1 then | |
io.stderr:write("Unable to connect to the Internet.\n") | |
return | |
end | |
for _,j in pairs(repos) do | |
if j.repo then | |
local lPacks = getPackages(j.repo) | |
if lPacks==nil then | |
io.stderr:write("Error while trying to receive package list for "..j.repo.."\n") | |
elseif type(lPacks) == "table" then | |
for k in pairs(lPacks) do | |
if k==pack then | |
return parseFolders(pack, j.repo, lPacks[k]),j.repo | |
end | |
end | |
end | |
end | |
end | |
local lRepos = readFromFile(2) | |
if lRepos then | |
for i,j in pairs(lRepos.repos) do | |
for k in pairs(j) do | |
if k==pack then | |
return parseFolders(pack, i, j[k]),i | |
end | |
end | |
end | |
end | |
return nil | |
end | |
local function provideInfo(pack) | |
if not pack then | |
printUsage() | |
return | |
end | |
pack = string.lower(pack) | |
local info = getInformation(pack) | |
if not info then | |
print("Package does not exist") | |
return | |
end | |
local done = false | |
print("--Information about package '"..pack.."'--") | |
if info.name then | |
print("Name: "..info.name) | |
done = true | |
end | |
if info.description then | |
print("Description: "..info.description) | |
done = true | |
end | |
if info.authors then | |
print("Authors: "..info.authors) | |
done = true | |
end | |
if info.note then | |
print("Note: "..info.note) | |
done = true | |
end | |
if info.files then | |
print("Number of files: "..tostring(#info.files)) | |
done = true | |
end | |
if not done then | |
print("No information provided.") | |
end | |
end | |
local function installPackage(pack,path,update) | |
local tPacks = readFromFile(1) | |
update = update or false | |
if not pack then | |
printUsage() | |
return | |
end | |
if not path and not update then | |
local lConfig = readFromFile(2) | |
path = lConfig.path or "/usr" | |
print("Installing package to "..path.."...") | |
elseif not update then | |
path = shell.resolve(path) | |
print("Installing package to "..path.."...") | |
end | |
pack = string.lower(pack) | |
if not tPacks then | |
io.stderr:write("Error while trying to read local package names") | |
return | |
elseif tPacks[1]==-1 then | |
table.remove(tPacks,1) | |
end | |
local info,repo = getInformation(pack) | |
if not info then | |
print("Package does not exist") | |
return | |
end | |
if update then | |
print("Updating package "..pack) | |
path = nil | |
if not tPacks[pack] then | |
io.stderr:write("error while checking update path") | |
return | |
end | |
for i,j in pairs(info.files) do | |
if not string.find(j,"^//") then | |
for k,v in pairs(tPacks[pack]) do | |
if k==i then | |
path = string.gsub(fs.path(v),j.."/?$","/") | |
break | |
end | |
end | |
if path then | |
break | |
end | |
end | |
end | |
path = shell.resolve(string.gsub(path,"^/?","/"),nil) | |
end | |
if not update and fs.exists(path) then | |
if not fs.isDirectory(path) then | |
if options.f then | |
path = fs.concat(fs.path(path),pack) | |
fs.makeDirectory(path) | |
else | |
print("Path points to a file, needs to be a directory.") | |
return | |
end | |
end | |
elseif not update then | |
if options.f then | |
fs.makeDirectory(path) | |
else | |
print("Directory does not exist.") | |
return | |
end | |
end | |
if tPacks[pack] and (not update) then | |
print("Package has already been installed") | |
return | |
elseif not tPacks[pack] and update then | |
print("Package has not been installed.") | |
print("If it has, uninstall it manually and reinstall it.") | |
return | |
end | |
if update then | |
term.write("Removing old files...") | |
for _,j in pairs(tPacks[pack]) do | |
fs.remove(j) | |
end | |
term.write("Done.\n") | |
end | |
tPacks[pack] = {} | |
term.write("Installing Files...") | |
for i,j in pairs(info.files) do | |
local nPath | |
if string.find(j,"^//") then | |
local lPath = string.sub(j,2) | |
if not fs.exists(lPath) then | |
fs.makeDirectory(lPath) | |
end | |
nPath = fs.concat(lPath,string.gsub(i,".+(/.-)$","%1"),nil) | |
else | |
local lPath = fs.concat(path,j) | |
if not fs.exists(lPath) then | |
fs.makeDirectory(lPath) | |
end | |
nPath = fs.concat(path,j,string.gsub(i,".+(/.-)$","%1"),nil) | |
end | |
local success,response = pcall(downloadFile,"https://raw.githubusercontent.com/"..repo.."/"..i,nPath) | |
if success and response then | |
tPacks[pack][i] = nPath | |
else | |
response = response or "no error message" | |
term.write("Error while installing files for package '"..pack.."': "..response..". Reverting installation... ") | |
fs.remove(nPath) | |
for o,p in pairs(tPacks[pack]) do | |
fs.remove(p) | |
tPacks[pack][o]=nil | |
end | |
print("Done.\nPlease contact the package author about this problem.") | |
return | |
end | |
end | |
if info.dependencies then | |
term.write("Done.\nInstalling Dependencies...\n") | |
for i,j in pairs(info.dependencies) do | |
local nPath | |
if string.find(j,"^//") then | |
nPath = string.sub(j,2) | |
else | |
nPath = fs.concat(path,j,string.gsub(i,".+(/.-)$","%1"),nil) | |
end | |
if string.lower(string.sub(i,1,4))=="http" then | |
local success,response = pcall(downloadFile,i,nPath) | |
if success and response then | |
tPacks[pack][i] = nPath | |
else | |
response = response or "no error message" | |
term.write("Error while installing files for package '"..pack.."': "..response..". Reverting installation... ") | |
fs.remove(nPath) | |
for o,p in pairs(tPacks[pack]) do | |
fs.remove(p) | |
tPacks[pack][o]=nil | |
end | |
print("Done.\nPlease contact the package author about this problem.") | |
return | |
end | |
else | |
local depInfo = getInformation(string.lower(i)) | |
if not depInfo then | |
term.write("\nDependency package "..i.." does not exist.") | |
end | |
installPackage(string.lower(i),fs.concat(path,j),update) | |
end | |
end | |
end | |
term.write("Done.\n") | |
saveToFile(tPacks) | |
print("Successfully installed package "..pack) | |
return true | |
end | |
local function uninstallPackage(pack) | |
local tFiles = readFromFile(1) | |
if not tFiles then | |
io.stderr:write("Error while trying to read package names") | |
return | |
elseif tFiles[1]==-1 then | |
table.remove(tFiles,1) | |
end | |
if not tFiles[pack] then | |
print("Package has not been installed.") | |
print("If it has, the package could not be identified.") | |
print("In this case you have to remove it manually.") | |
return | |
end | |
term.write("Removing package files...") | |
for i,j in pairs(tFiles[pack]) do | |
fs.remove(j) | |
end | |
term.write("Done\nRemoving references...") | |
tFiles[pack]=nil | |
saveToFile(tFiles) | |
term.write("Done.\n") | |
print("Successfully uninstalled package "..pack) | |
end | |
local function updatePackage(pack) | |
if pack=="all" then | |
print("Updating everything...") | |
local tFiles = readFromFile(1) | |
if not tFiles then | |
io.stderr:write("Error while trying to read package names") | |
return | |
elseif tFiles[1]==-1 then | |
table.remove(tFiles,1) | |
end | |
local done = false | |
for i in pairs(tFiles) do | |
installPackage(i,nil,true) | |
done = true | |
end | |
if not done then | |
print("No package has been installed so far.") | |
end | |
else | |
installPackage(args[2],nil,true) | |
end | |
end | |
if options.iKnowWhatIAmDoing then | |
if args[1] == "list" then | |
if not getInternet() then return end | |
local packs = listPackages(args[2]) | |
printPackages(packs) | |
elseif args[1] == "info" then | |
if not getInternet() then return end | |
provideInfo(args[2]) | |
elseif args[1] == "install" then | |
if not getInternet() then return end | |
return installPackage(args[2],args[3],false) | |
elseif args[1] == "update" then | |
if not getInternet() then return end | |
updatePackage(args[2]) | |
elseif args[1] == "uninstall" then | |
uninstallPackage(args[2]) | |
else | |
printUsage() | |
return | |
end | |
return | |
end | |
--Very much not stolen from Sangar's install.lua | |
local computer = require("computer") | |
local unicode = require("unicode") | |
local candidates = {} | |
for address in component.list("filesystem") do | |
local dev = component.proxy(address) | |
if not dev.isReadOnly() and dev.address ~= computer.tmpAddress() then | |
table.insert(candidates, dev) | |
end | |
end | |
if #candidates == 0 then | |
print("No writable disks found, aborting.") | |
return | |
end | |
for i = 1, #candidates do | |
local label = candidates[i].getLabel() | |
if label then | |
label = label .. " (" .. candidates[i].address:sub(1, 8) .. "...)" | |
else | |
label = candidates[i].address | |
end | |
print(i .. ") " .. label) | |
end | |
print("To select the device to install to, please enter a number between 1 and " .. #candidates .. ".") | |
print("Press 'q' to cancel the installation.") | |
local choice | |
while not choice do | |
result = io.read() | |
if result:sub(1, 1):lower() == "q" then | |
return | |
end | |
local number = tonumber(result) | |
if number and number > 0 and number <= #candidates then | |
choice = candidates[number] | |
else | |
print("Invalid input, please try again.") | |
end | |
end | |
candidates = nil | |
print("Installing OPPM to device " .. (choice.getLabel() or choice.address)) | |
os.sleep(0.25) | |
local mnt = choice.address:sub(1, 3) | |
local result, reason = shell.execute("oppm", nil, "install", "-f", "oppm", "/mnt/" .. mnt .. "/usr/", "--iKnowWhatIAmDoing") | |
if not result then | |
error(reason, 0) | |
end | |
if not reason then | |
return | |
end | |
print("All done! Please remove the Floppy Disk used for installation! Reboot now? [Y/n]") | |
local result = io.read() | |
if not result or result == "" or result:sub(1, 1):lower() == "y" then | |
print("\nRebooting now!") | |
computer.shutdown(true) | |
end | |
print("Returning to shell.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment