diff --git a/gruntfile.js b/gruntfile.js index e763c3b..265d44b 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -41,17 +41,25 @@ module.exports = function (grunt) { }); grunt.registerTask("minify-lua-modules", "minifies lua modules", function () { - var fs = require('fs'); - var luamin = require('luamin'); + const fs = require('fs'); + const luamin = require('luamin'); + const util = require('util'); grunt.file.expand({filter: 'isDirectory'}, "dist/*").forEach(function (dir) { grunt.file.expand({filter: 'isFile'}, dir + "/*.lua").forEach(function (file) { - var directory = file.substr(0, file.lastIndexOf('/')); - var file_name = file.substr(file.lastIndexOf('/') + 1, file.lastIndexOf('.')); - var lua = fs.readFileSync(file, 'utf8'); - var minified = luamin.minify(lua); - var minified_file_name = file_name + '.min.lua'; - fs.writeFileSync(directory + '/' + minified_file_name, minified); - console.log("Minified: " + file_name + ' -> ' + minified_file_name); + const directory = file.substr(0, file.lastIndexOf('/')); + const file_name = file.substr(file.lastIndexOf('/') + 1, file.lastIndexOf('.')); + + const lua = fs.readFileSync(file, 'utf8'); + const minified_contents = luamin.minify(lua); + + const minified_file_name = file_name + '.min.lua'; + const minified_file = directory + '/' + minified_file_name; + fs.writeFileSync(minified_file, minified_contents); + const file_stats = fs.statSync(file); + const minified_file_stats = fs.statSync(minified_file); + const saved_space = file_stats.size - minified_file_stats.size; + const saved_space_percent = (100 * (1 - minified_file_stats.size / file_stats.size)).toFixed(2); + console.log(util.format("Minified: %s: %f KB -> %s: %f KB. SAVED %d%% %d KB", file_name, file_stats.size / 1000, minified_file_name, minified_file_stats.size / 1000, saved_space_percent, saved_space / 1000)); }); }); }); diff --git a/src/spm/main.lua b/src/spm/main.lua new file mode 100644 index 0000000..e3970d8 --- /dev/null +++ b/src/spm/main.lua @@ -0,0 +1,475 @@ +--[[ +OpenPrograms package manager, browser and downloader, for easy access to many programs +Author: Vexatos +]] +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 wget = loadfile("/bin/wget.lua") + +local gpu = component.gpu + +if not component.isAvailable("internet") then + io.stderr:write("This program requires an internet card to run.") + return +end +local internet = require("internet") + +local repoLocationConfig="https://raw.githubusercontent.com/OpenPrograms/openprograms.github.io/master/repos.cfg" + +local args, options = shell.parse(...) + + +local function 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] ' to get a list of available packages containing the specified substring") + print(" -i: Only list already installed packages") + print("'spm info ' to get further information about a program package") + print("'spm install [-f] [path]' to download a package to a directory on your system (or /usr by default)") + print("'spm update ' to update an already installed package") + print("'spm update all' to update every already installed package") + print("'spm uninstall ' 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, repoLocationConfig) + 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 + wget("-fq",url,path) + else + 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(tPacks) + 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(tPacks) + 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 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 tPacks = readFromFile(1) + for i in pairs(tPacks) 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(tPacks) + if tPacks==nil or not tPacks[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(tPacks) 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 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 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 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 not done then + print("No information provided.") + end +end + +local function installPackage(pack,path,update) + 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) + + local tPacks = readFromFile(1) + 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 + for i,j in pairs(info.files) do + 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 + 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 i,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 = pcall(downloadFile,"https://raw.githubusercontent.com/"..repo.."/"..i,nPath) + if success then + tPacks[pack][i] = nPath + end + end + if info.dependencies then + term.write("Done.\nInstalling Dependencies...") + 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 = pcall(downloadFile,i,nPath) + if success then + tPacks[pack][i] = nPath + 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)) + end + end + end + term.write("Done.\n") + saveToFile(tPacks) + print("Successfully installed package "..pack) +end + +local function uninstallPackage(pack) + local info,repo = getInformation(pack) + if not info then + print("Package does not exist") + return + end + 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, 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 args[1] == "list" then + local tPacks = listPackages(args[2]) + printPackages(tPacks) +elseif args[1] == "info" then + provideInfo(args[2]) +elseif args[1] == "install" then + installPackage(args[2],args[3],false) +elseif args[1] == "update" then + updatePackage(args[2]) +elseif args[1] == "uninstall" then + uninstallPackage(args[2]) +else + printUsage() + return +end \ No newline at end of file