You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

493 lines
16 KiB

component = require("component")
event = require("event")
fs = require("filesystem")
process = require("process")
serial = require("serialization")
shell = require("shell")
term = require("term")
wget = loadfile("/bin/wget.lua")
gpu = component.gpu
if not component.isAvailable("internet")
io.stderr\write("This program requires an internet card to run.")
return
internet = require("internet")
githubContentRoot = "https://raw.githubusercontent.com/"
repoLocationConfig = "{URL_BASE}/repos.cfg"
localInstalledPackagesFile = "/etc/spdata.svd"
localConfigFile = "/etc/spm.cfg"
args, options = shell.parse(...)
printUsage = ->
print("Sothr Package Manager, use this to browse through and download sothr packaged programs easily")
print("Usage:")
print("'spm list [-i]' to get a list of all the available program packages")
print("'spm list [-i] <filter>' to get a list of available packages containing the specified substring")
print(" -i: Only list already installed packages")
print("'spm info <package>' to get further information about a program package")
print("'spm install|add [-f] [[-nm][-m]] <package> [path]' to download a package to a directory on your system (or /usr by default)")
print("'spm update|upgrade <package>' to update an already installed package")
print("'spm update|upgrade all' to update every already installed package")
print("'spm uninstall|remove <package>' to remove a package from your system")
print("'spm copy <package>' to copy package files to another location")
print(" -f: Force creation of directories and overwriting of existing files.")
-- Make an internet http/s request for data
getContent = (url) ->
sContent = ""
result, response = pcall(internet.request, url)
if not result
return nil
for chunk in response
sContent = sContent .. chunk
return sContent
-- Request repository information
getRepos ->
success, sRepos = pcall(getContent, repoLocationConfig)
if not success
io.stderr\write("Could not connect to the Internet. Please ensure you have an Internet connection.")
return -1
return serial.unserialize(sRepos)
-- Get Packages for src
getPackages = (src) ->
success, sPackages = pcall(getContent, src .. "/programs.cfg")
if not success or not sPackages
return -1
return serial.unserialize(sPackages)
getGithubPackages = (repo) ->
return getPackages(githubContentRoot .. repo .. "/master")
--For sorting table values by alphabet
compare = (a, b) ->
for i = 1, math.min(#a, #b)
if a:sub(i, i) ~= b:sub(i, i)
return a:sub(i, i) < b:sub(i, i)
return #a < #b
downloadFile = (url, path, force) ->
if options.f or force
wget("-fq", url, path)
else
wget("-q", url, path)
readFromFile = (path) ->
if not fs.exists(fs.path(path))
fs.makeDirectory(fs.path(path))
if not fs.exists(path)
return { -1 }
file, msg = io.open(path, "rb")
if not file
io.stderr\write("Error while trying to read file at " .. path .. ": " .. msg)
return
serializedTableData = file:read("*a")
file:close()
return serial.unserialize(serializedTableData) or { -1 }
readFromLocalInstallFile = ->
return readFromFile(localInstalledPackagesFile)
readLocalConfigFile = ->
path = localConfigFile
if not fs.exists(path)
return { -1 }
else
return readFromFile(path)
saveToInstalledPackagesFile = (installedPackages) ->
file, msg = io.open(localInstalledPackagesFile, "wb")
if not file
io.stderr\write("Error while trying to save package names: " .. msg)
return
sPacks = serial.serialize(installedPackages)
file:write(sPacks)
file:close()
listPackages = (filter) ->
filter = filter or false
if filter
filter = string.lower(filter)
packageTable = {}
print("Receiving Package list...")
if not options.i
success, repositories = pcall(getRepos)
if not success or repositories == -1
io.stderr\write("Unable to connect to the Internet.\n")
return
elseif repositories == nil
print("Error while trying to receive repository list")
return
for _, repository in pairs(repositories)
packages
target
if repository.src
print("Checking Repository " .. repository.src)
packages = getPackages(repository.src)
target = repository.src
elseif repository.repo
print("Checking Repository " .. repository.repo)
packages = getGithubPackages(repository.repo)
target = repository.repo
if packages == nil
io.stderr\write("Error while trying to receive package list for " .. target .. "\n")
return
elseif type(packages) == "table"
for package in pairs(packages)
if not package.hidden
table.insert(packageTable, package)
else
localPackageTable = {}
localPackages = readFromLocalInstallFile()
for localPackage in pairs(localPackages)
table.insert(localPackageTable, localPackage)
packageTable = localPackageTable
if filter
packages = {}
for _, package in ipairs(packageTable)
if (#package >= #filter) and string.find(package, filter, 1, true) ~= nil
table.insert(packages, package)
packageTable = packages
table.sort(packageTable, compare)
return packageTable
printPackages = (packages) ->
if packages == nil or not packages[1]
print("No package matching specified filter found.")
return
term.clear()
xRes, yRes = gpu.getResolution()
print("--SPM {ENV} Package list--")
xCur, yCur = term.getCursor()
for _, package in ipairs(packages)
term.write(package .. "\n")
yCur = yCur + 1
if yCur > yRes - 1
term.write("[Press any key to continue]")
event = event.pull("key_down")
if event
term.clear()
print("--SPM {ENV} Package list--")
xCur, yCur = term.getCursor()
getInformation = (requestedPackage) ->
success, repositories = pcall(getRepos)
if not success or repositories == -1
io.stderr\write("Unable to connect to the Internet.\n")
return
for _, repository in pairs(repositories)
packages
target
if repository.src
packages = getPackages(repository.src)
target = repository.src
elseif repository.repo
packages = getGithubPackages(repository.repo)
target = githubContentRoot .. repository.repo
if packages == nil
io.stderr\write("Error while trying to receive package list for " .. target .. "\n")
elseif type(packages) == "table"
for package in pairs(packages)
if package == requestedPackage
return packages[package], target
return nil
provideInfo = (pack) ->
if not pack
printUsage()
return
pack = string.lower(pack)
info = getInformation(pack)
if not info
print("Package does not exist")
return
done = false
print("--Information about package '" .. pack .. "'--")
if info.name
print("Name: " .. info.name)
done = true
if info.version
print("Version: " .. info.version)
done = true
if info.description
print("Description: " .. info.description)
done = true
if info.authors
print("Authors: " .. info.authors)
done = true
if info.note
print("Note: " .. info.note)
done = true
if not done
print("No information provided.")
installPackage = (requestedPackage, installPath, doUpdate) ->
doUpdate = doUpdate or false
if not requestedPackage
printUsage()
return
if not installPath and not doUpdate
localConfig = readLocalConfigFile()
installPath = localConfig.path or "/usr"
print("Installing package to " .. installPath .. "...")
elseif not doUpdate
installPath = shell.resolve(installPath)
print("Installing package to " .. installPath .. "...")
requestedPackage = string.lower(requestedPackage)
locallyInstalledPackages = readFromLocalInstallFile()
if not locallyInstalledPackages
io.stderr\write("Error while trying to read local package names")
return
elseif locallyInstalledPackages[1] == -1
table.remove(locallyInstalledPackages, 1)
packageInfo, repositoryPath = getInformation(requestedPackage)
if not packageInfo
print("Package does not exist")
return
if doUpdate
print("Updating package " .. requestedPackage)
installPath = nil
for filePath, fileInstallPath in pairs(packageInfo.files)
for installedFilePath, fileLocalInstallPath in pairs(locallyInstalledPackages[requestedPackage])
if installedFilePath == filePath
installPath = string.gsub(fs.path(fileLocalInstallPath), fileInstallPath .. "/?$", "/")
break
if installPath
break
installPath = shell.resolve(string.gsub(installPath, "^/?", "/"), nil)
if not doUpdate and fs.exists(installPath)
if not fs.isDirectory(installPath)
if options.f
installPath = fs.concat(fs.path(installPath), requestedPackage)
fs.makeDirectory(installPath)
else
print("Path points to a file, needs to be a directory.")
return
elseif not doUpdate
if options.f
fs.makeDirectory(installPath)
else
print("Directory does not exist.")
return
if locallyInstalledPackages[requestedPackage] and (not doUpdate)
print("Package has already been installed")
return
elseif not locallyInstalledPackages[requestedPackage] and doUpdate
print("Package has not been installed.")
print("If it has, uninstall it manually and reinstall it.")
return
if doUpdate
term.write("Removing old files...")
for installedFilePath, fileLocalInstallPath in pairs(locallyInstalledPackages[requestedPackage])
fs.remove(fileLocalInstallPath)
term.write("Done.\n")
locallyInstalledPackages[requestedPackage] = {}
term.write("Installing Files...")
for filePath, requestedInstallPath in pairs(packageInfo.files)
installedPath
if string.find(requestedInstallPath, "^//")
localInstalledPath = string.sub(requestedInstallPath, 2)
if not fs.exists(localInstalledPath)
fs.makeDirectory(localInstalledPath)
installedPath = fs.concat(localInstalledPath, string.gsub(filePath, ".+(/.-)$", "%1"), nil)
else
localInstalledPath = fs.concat(installPath, requestedInstallPath)
if not fs.exists(localInstalledPath)
fs.makeDirectory(localInstalledPath)
installedPath = fs.concat(installPath, requestedInstallPath, string.gsub(filePath, ".+(/.-)$", "%1"), nil)
downloadPath
if (packageInfo.minified or options.m) and not options.nm
downloadPath = repositoryPath .. "/" .. string.gsub(filePath, "(.+)%.lua$", "%1") .. ".min.lua"
else
downloadPath = repositoryPath .. "/" .. filePath
success = pcall(downloadFile, downloadPath, installedPath)
if success
locallyInstalledPackages[requestedPackage][filePath] = installedPath
if packageInfo.dependencies
term.write("Done.\nInstalling Dependencies...")
for packageDependency, dependencyPath in pairs(packageInfo.dependencies)
installedDependencyPath
if string.find(dependencyPath, "^//")
installedDependencyPath = string.sub(dependencyPath, 2)
else
installedDependencyPath = fs.concat(installPath, dependencyPath, string.gsub(packageDependency, ".+(/.-)$", "%1"), nil)
if string.lower(string.sub(packageDependency, 1, 4)) == "http"
success = pcall(downloadFile, packageDependency, installedDependencyPath)
if success
locallyInstalledPackages[requestedPackage][packageDependency] = installedDependencyPath
else
dependencyInfo = getInformation(string.lower(packageDependency))
if not dependencyInfo
term.write("\nDependency package " .. packageDependency .. " does not exist.")
installPackage(string.lower(packageDependency), fs.concat(installPath, dependencyPath))
term.write("Done.\n")
saveToInstalledPackagesFile(locallyInstalledPackages)
print("Successfully installed package " .. requestedPackage)
copyPackage = (requestedPackage, copyLocation) ->
packageInformation, repositoryLocation = getInformation(requestedPackage)
if not packageInformation
print("Package does not exist")
return
locallyInstalledFiles = readFromLocalInstallFile()
if not locallyInstalledFiles
io.stderr\write("Error while trying to read package names")
return
elseif locallyInstalledFiles[1] == -1
table.remove(locallyInstalledFiles, 1)
if not locallyInstalledFiles[requestedPackage]
print("Package has not been installed.")
print("It must be installed to be copied.")
return
term.write("Copying package files...")
for installedFilePath, fileLocalInstallPath in pairs(locallyInstalledFiles[requestedPackage])
fs.copy(fileLocalInstallPath, copyLocation .. fileLocalInstallPath)
if packageInfo.dependencies
term.write("Done\nCopying Dependencies...")
for packageDependency, _ in pairs(packageInfo.dependencies)
if not locallyInstalledFiles[packageDependency]
print("Dependency has not been installed.")
print("If it has, it is not recognized. Reinstall the parent package: " .. requestedPackage)
return
copyPackage(packageDependency, copyLocation)
term.write("Done.\n")
print("Successfully copied package " .. requestedPackage)
uninstallPackage = (requestedPackage) ->
packageInformation, repositoryLocation = getInformation(requestedPackage)
if not packageInformation
print("Package does not exist")
return
locallyInstalledFiles = readFromLocalInstallFile()
if not locallyInstalledFiles
io.stderr\write("Error while trying to read package names")
return
elseif locallyInstalledFiles[1] == -1
table.remove(locallyInstalledFiles, 1)
if not locallyInstalledFiles[requestedPackage]
print("Package has not been installed.")
print("If it has, you have to remove it manually.")
return
term.write("Removing package files...")
for installedFilePath, fileLocalInstallPath in pairs(locallyInstalledFiles[requestedPackage])
fs.remove(fileLocalInstallPath)
term.write("Done\nRemoving references...")
locallyInstalledFiles[requestedPackage] = nil
saveToInstalledPackagesFile(locallyInstalledFiles)
term.write("Done.\n")
print("Successfully uninstalled package " .. requestedPackage)
updatePackage = (requestedPackage) ->
if requestedPackage == "all"
print("Updating everything...")
locallyInstalledPackages = readFromLocalInstallFile()
if not locallyInstalledPackages
io.stderr\write("Error while trying to read package names")
return
elseif locallyInstalledPackages[1] == -1
table.remove(locallyInstalledPackages, 1)
done = false
for localPackage in pairs(locallyInstalledPackages)
installPackage(localPackage, nil, true)
done = true
if not done
print("No package has been installed so far.")
else
installPackage(args[2], nil, true)
if args[1] == "list"
tPacks = listPackages(args[2])
printPackages(tPacks)
elseif args[1] == "info"
provideInfo(args[2])
elseif args[1] == "install" or args[1] == "add"
installPackage(args[2], args[3], false)
elseif args[1] == "update" or args[1] == "upgrade"
updatePackage(args[2])
elseif args[1] == "uninstall" or args[1] == "remove"
uninstallPackage(args[2])
elseif args[1] == "copy"
copyPackage(args[2], args[3])
else
printUsage()
return