diff --git a/sys/apis/fs/gitfs.lua b/sys/apis/fs/gitfs.lua index 2bd4aac..b38388b 100644 --- a/sys/apis/fs/gitfs.lua +++ b/sys/apis/fs/gitfs.lua @@ -2,12 +2,12 @@ local git = require('git') local gitfs = { } -function gitfs.mount(dir, user, repo, branch) - if not user or not repo then - error('gitfs syntax: user, repo, [branch]') +function gitfs.mount(dir, repo) + if not repo then + error('gitfs syntax: repo') end - local list = git.list(user, repo, branch) + local list = git.list(repo) for path, entry in pairs(list) do if not fs.exists(fs.combine(dir, path)) then local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url) diff --git a/sys/apis/git.lua b/sys/apis/git.lua index e72199c..7bfe148 100644 --- a/sys/apis/git.lua +++ b/sys/apis/git.lua @@ -6,8 +6,13 @@ local FILE_URL = 'https://raw.github.com/%s/%s/%s/%s' local git = { } -function git.list(user, repo, branch) - branch = branch or 'master' +function git.list(repo) + + local t = Util.split(repo, '(.-)/') + + local user = t[1] + local repo = t[2] + local branch = t[3] or 'master' local dataUrl = string.format(TREE_URL, user, repo, branch) local contents = Util.download(dataUrl) diff --git a/sys/apis/util.lua b/sys/apis/util.lua index d14a779..2f38aa2 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -379,8 +379,15 @@ function Util.download(url, filename) end function Util.loadUrl(url, env) -- loadfile equivalent - local c = Util.download(url) - return load(c, url, nil, env) + local s, m = pcall(function() + local c = Util.download(url) + return load(c, url, nil, env) + end) + + if s then + return m + end + return s, m end function Util.runUrl(env, url, ...) -- os.run equivalent @@ -390,7 +397,7 @@ function Util.runUrl(env, url, ...) -- os.run equivalent fn, m = pcall(function() fn(unpack(args)) end) end if not fn and m and m ~= '' then - printError(m) +-- printError(m) end return fn, m end diff --git a/sys/apps/Appstore.lua b/sys/apps/Appstore.lua deleted file mode 100644 index f36dda8..0000000 --- a/sys/apps/Appstore.lua +++ /dev/null @@ -1,414 +0,0 @@ -requireInjector(getfenv(1)) - -local Config = require('config') -local SHA1 = require('sha1') -local UI = require('ui') -local Util = require('util') - --- scrap this entire file. don't muck with standard apis - -local REGISTRY_DIR = 'usr/.registry' - - --- FIX SOMEDAY -function os.registerApp(app, key) - - app.key = SHA1.sha1(key) - Util.writeTable(fs.combine(REGISTRY_DIR, app.key), app) - os.queueEvent('os_register_app') -end - -function os.unregisterApp(key) - - local filename = fs.combine(REGISTRY_DIR, SHA1.sha1(key)) - if fs.exists(filename) then - fs.delete(filename) - os.queueEvent('os_register_app') - end -end - - -local sandboxEnv = Util.shallowCopy(getfenv(1)) -setmetatable(sandboxEnv, { __index = _G }) - -multishell.setTitle(multishell.getCurrent(), 'App Store') -UI:configure('Appstore', ...) - -local APP_DIR = 'usr/apps' - -local sources = { - - { text = "STD Default", - event = 'source', - url = "http://pastebin.com/raw/zVws7eLq" }, --stock ---[[ - { text = "Discover", - event = 'source', - generateName = true, - url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95 - - { text = "Opus", - event = 'source', - url = "http://pastebin.com/raw/ajQ91Rmn" }, -]] -} - -shell.setDir(APP_DIR) - -function downloadApp(app) - local h - - if type(app.url) == "table" then - h = contextualGet(app.url[1]) - else - h = http.get(app.url) - end - - if h then - local contents = h.readAll() - h:close() - return contents - end -end - -function runApp(app, checkExists, ...) - - local path, fn - local args = { ... } - - if checkExists and fs.exists(fs.combine(APP_DIR, app.name)) then - path = fs.combine(APP_DIR, app.name) - else - local program = downloadApp(app) - - fn = function() - - if not program then - error('Failed to download') - end - - local fn = loadstring(program, app.name) - - if not fn then - error('Failed to download') - end - - setfenv(fn, sandboxEnv) - fn(unpack(args)) - end - end - - multishell.openTab({ - title = app.name, - env = sandboxEnv, - path = path, - fn = fn, - focused = true, - }) - - return true, 'Running program' -end - -local installApp = function(app) - - local program = downloadApp(app) - if not program then - return false, "Failed to download" - end - - local fullPath = fs.combine(APP_DIR, app.name) - Util.writeFile(fullPath, program) - return true, 'Installed as ' .. fullPath -end - -local viewApp = function(app) - - local program = downloadApp(app) - if not program then - return false, "Failed to download" - end - - Util.writeFile('/.source', program) - shell.openForegroundTab('edit /.source') - fs.delete('/.source') - return true -end - -local getSourceListing = function(source) - local contents = http.get(source.url) - if contents then - - local fn = loadstring(contents.readAll(), source.text) - contents.close() - - local env = { std = { } } - setmetatable(env, { __index = _G }) - setfenv(fn, env) - fn() - - if env.contextualGet then - contextualGet = env.contextualGet - end - - source.storeURLs = env.std.storeURLs - source.storeCatagoryNames = env.std.storeCatagoryNames - - if source.storeURLs and source.storeCatagoryNames then - for k,v in pairs(source.storeURLs) do - if source.generateName then - v.name = v.title:match('(%w+)') - if not v.name or #v.name == 0 then - v.name = tostring(k) - else - v.name = v.name:lower() - end - else - v.name = k - end - v.categoryName = source.storeCatagoryNames[v.catagory] - v.ltitle = v.title:lower() - end - end - end -end - -local appPage = UI.Page({ - backgroundColor = UI.ViewportWindow.defaults.backgroundColor, - menuBar = UI.MenuBar({ - showBackButton = not pocket, - buttons = { - { text = 'Install', event = 'install' }, - { text = 'Run', event = 'run' }, - { text = 'View', event = 'view' }, - { text = 'Remove', event = 'uninstall', name = 'removeButton' }, - }, - }), - container = UI.Window({ - x = 2, - y = 3, - height = UI.term.height - 3, - width = UI.term.width - 2, - viewport = UI.ViewportWindow(), - }), - notification = UI.Notification(), - accelerators = { - q = 'back', - backspace = 'back', - }, -}) - -function appPage.container.viewport:draw() - local app = self.parent.parent.app - local str = string.format( - 'By: %s\nCategory: %s\nFile name: %s\n\n%s', - app.creator, app.categoryName, app.name, app.description) - - self:clear() - local y = self:wrappedWrite(1, 1, app.title, self.width, nil, colors.yellow) - self.height = self:wrappedWrite(1, y, str, self.width) - - if appPage.notification.enabled then - appPage.notification:draw() - end -end - -function appPage:enable(source, app) - self.source = source - self.app = app - UI.Page.enable(self) - - self.container.viewport:setScrollPosition(0) - if fs.exists(fs.combine(APP_DIR, app.name)) then - self.menuBar.removeButton:enable('Remove') - else - self.menuBar.removeButton:disable('Remove') - end -end - -function appPage:eventHandler(event) - if event.type == 'back' then - UI:setPreviousPage() - - elseif event.type == 'run' then - self.notification:info('Running program', 3) - self:sync() - runApp(self.app, true) - - elseif event.type == 'view' then - self.notification:info('Downloading program', 3) - self:sync() - viewApp(self.app) - - elseif event.type == 'uninstall' then - if self.app.runOnly then - s,m = runApp(self.app, false, 'uninstall') - else - fs.delete(fs.combine(APP_DIR, self.app.name)) - self.notification:success("Uninstalled " .. self.app.name, 3) - self:focusFirst(self) - self.menuBar.removeButton:disable('Remove') - self.menuBar:draw() - - os.unregisterApp(self.app.creator .. '.' .. self.app.name) - end - - elseif event.type == 'install' then - self.notification:info("Installing", 3) - self:sync() - local s, m - if self.app.runOnly then - s,m = runApp(self.app, false) - else - s,m = installApp(self.app) - end - if s then - self.notification:success(m, 3) - - if not self.app.runOnly then - self.menuBar.removeButton:enable('Remove') - self.menuBar:draw() - - local category = 'Apps' - if self.app.catagoryName == 'Game' then - category = 'Games' - end - - os.registerApp({ - run = fs.combine(APP_DIR, self.app.name), - title = self.app.title, - category = category, - icon = self.app.icon, - }, self.app.creator .. '.' .. self.app.name) - end - else - self.notification:error(m, 3) - end - else - return UI.Page.eventHandler(self, event) - end - return true -end - -local categoryPage = UI.Page({ - menuBar = UI.MenuBar({ - buttons = { - { text = 'Catalog', event = 'dropdown', dropdown = 'sourceMenu' }, - { text = 'Category', event = 'dropdown', dropdown = 'categoryMenu' }, - }, - }), - sourceMenu = UI.DropMenu({ - buttons = sources, - }), - grid = UI.ScrollingGrid({ - y = 2, - height = UI.term.height - 2, - columns = { - { heading = 'Title', key = 'title' }, - }, - sortColumn = 'title', - autospace = true, - }), - statusBar = UI.StatusBar(), - accelerators = { - l = 'lua', - q = 'quit', - }, -}) - -function categoryPage:setCategory(source, name, index) - self.grid.values = { } - for k,v in pairs(source.storeURLs) do - if index == 0 or index == v.catagory then - table.insert(self.grid.values, v) - end - end - self.statusBar:setStatus(string.format('%s: %s', source.text, name)) - self.grid:update() - self.grid:setIndex(1) -end - -function categoryPage:setSource(source) - - if not source.categoryMenu then - - self.statusBar:setStatus('Loading...') - self.statusBar:draw() - self:sync() - - getSourceListing(source) - - if not source.storeURLs then - error('Unable to download application list') - end - - local buttons = { } - for k,v in Util.spairs(source.storeCatagoryNames, - function(a, b) return a:lower() < b:lower() end) do - - if v ~= 'Operating System' then - table.insert(buttons, { - text = v, - event = 'category', - index = k, - }) - end - end - - source.categoryMenu = UI.DropMenu({ - y = 2, - x = 1, - buttons = buttons, - }) - source.index, source.name = Util.first(source.storeCatagoryNames) - - categoryPage:add({ - categoryMenu = source.categoryMenu - }) - end - - self.source = source - self.categoryMenu = source.categoryMenu - categoryPage:setCategory(source, source.name, source.index) -end - -function categoryPage.grid:sortCompare(a, b) - return a.ltitle < b.ltitle -end - -function categoryPage.grid:getRowTextColor(row, selected) - if fs.exists(fs.combine(APP_DIR, row.name)) then - return colors.orange - end - return UI.Grid:getRowTextColor(row, selected) -end - -function categoryPage:eventHandler(event) - - if event.type == 'grid_select' or event.type == 'select' then - UI:setPage(appPage, self.source, self.grid:getSelected()) - - elseif event.type == 'category' then - self:setCategory(self.source, event.button.text, event.button.index) - self:setFocus(self.grid) - self:draw() - - elseif event.type == 'source' then - self:setFocus(self.grid) - self:setSource(event.button) - self:draw() - - elseif event.type == 'quit' then - UI:exitPullEvents() - - else - return UI.Page.eventHandler(self, event) - end - return true -end - -print("Retrieving catalog list") -categoryPage:setSource(sources[1]) - -UI:setPage(categoryPage) -UI:pullEvents() -UI.term:reset() diff --git a/sys/apps/Events.lua b/sys/apps/Events.lua deleted file mode 100644 index 178e96d..0000000 --- a/sys/apps/Events.lua +++ /dev/null @@ -1,132 +0,0 @@ -requireInjector(getfenv(1)) - -local Event = require('event') -local UI = require('ui') -local Util = require('util') - -multishell.setTitle(multishell.getCurrent(), 'Events') -UI:configure('Events', ...) - -local page = UI.Page({ - menuBar = UI.MenuBar({ - buttons = { - { text = 'Filter', event = 'filter' }, - { text = 'Reset', event = 'reset' }, - { text = 'Pause ', event = 'toggle', name = 'pauseButton' }, - }, - }), - grid = UI.Grid({ - y = 2, - columns = { - { heading = 'Event', key = 'event' }, - { key = 'p1' }, - { key = 'p2' }, - { key = 'p3' }, - { key = 'p4' }, - { key = 'p5' }, - }, - autospace = true, - }), - accelerators = { - f = 'filter', - p = 'toggle', - r = 'reset', - c = 'clear', - q = 'quit', - }, - filtered = { }, -}) - -function page:eventHandler(event) - - if event.type == 'filter' then - local entry = self.grid:getSelected() - self.filtered[entry.event] = true - - elseif event.type == 'toggle' then - self.paused = not self.paused - if self.paused then - self.menuBar.pauseButton.text = 'Resume' - else - self.menuBar.pauseButton.text = 'Pause ' - end - self.menuBar:draw() - - elseif event.type == 'grid_select' then - multishell.openTab({ path = 'sys/apps/Lua.lua', args = { event.selected }, focused = true }) - - elseif event.type == 'reset' then - self.filtered = { } - self.grid:setValues({ }) - self.grid:draw() - if self.paused then - self:emit({ type = 'toggle' }) - end - - elseif event.type == 'clear' then - self.grid:setValues({ }) - self.grid:draw() - - elseif event.type == 'quit' then - Event.exitPullEvents() - - elseif event.type == 'focus_change' then - if event.focused == self.grid then - if not self.paused then - self:emit({ type = 'toggle' }) - end - end - - else - return UI.Page.eventHandler(self, event) - end - return true -end - -function page.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - - local function tovalue(s) - if type(s) == 'table' then - return 'table' - end - return s - end - - for k,v in pairs(row) do - row[k] = tovalue(v) - end - - return row -end - -function page.grid:draw() - self:adjustWidth() - UI.Grid.draw(self) -end - -Event.addRoutine(function() - - while true do - local e = { os.pullEvent() } - if not page.paused and not page.filtered[e[1]] then - table.insert(page.grid.values, 1, { - event = e[1], - p1 = e[2], - p2 = e[3], - p3 = e[4], - p4 = e[5], - p5 = e[6], - }) - if #page.grid.values > page.grid.height - 1 then - table.remove(page.grid.values, #page.grid.values) - end - page.grid:update() - page.grid:draw() - page:sync() - end - end -end) - -UI:setPage(page) -UI:pullEvents() diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index c24b11f..cc38b38 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -288,7 +288,7 @@ function Browser:eventHandler(event) Event.exitPullEvents() elseif event.type == 'edit' and file then - self:run('sys/apps/shell', '/sys/apps/edit.lua', file.name) + self:run('sys/apps/shell', 'edit', file.name) elseif event.type == 'shell' then self:run('sys/apps/shell') diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 70af4ac..0f195da 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -36,6 +36,15 @@ Config.load('Overview', config) local applications = { } local function loadApplications() + + local requirements = { + turtle = function() return turtle end, + advancedTurtle = function() return turtle and term.isColor() end, + pocket = function() return pocket end, + advancedPocket = function() return pocket and term.isColor() end, + advancedComputer = function() return not turtle and not pocket and term.isColor() end, + } + applications = Util.readTable('sys/etc/app.db') if fs.exists(REGISTRY_DIR) then @@ -50,12 +59,20 @@ local function loadApplications() end Util.each(applications, function(v, k) v.key = k end) - applications = Util.filter(applications, function(_, a) return not a.disabled end) + applications = Util.filter(applications, function(_, a) + if a.disabled then + return false + end - applications = Util.filter(applications, function(_, a) - return Util.startsWidth(a.run, 'http') or shell.resolveProgram(a.run) + if a.requires then + local fn = requirements[a.requires] + if fn and not fn() then + return false + end + end + + return Util.startsWidth(a.run, 'http') or shell.resolveProgram(a.run) end) - end loadApplications() diff --git a/sys/apps/Peripherals.lua b/sys/apps/Peripherals.lua deleted file mode 100644 index b0f9df4..0000000 --- a/sys/apps/Peripherals.lua +++ /dev/null @@ -1,218 +0,0 @@ -requireInjector(getfenv(1)) - -local Event = require('event') -local UI = require('ui') -local Util = require('util') - -multishell.setTitle(multishell.getCurrent(), 'Devices') - ---[[ -- PeripheralsPage -- ]] -- -local peripheralsPage = UI.Page { - grid = UI.ScrollingGrid { - columns = { - { heading = 'Type', key = 'type' }, - { heading = 'Side', key = 'side' }, - }, - sortColumn = 'type', - height = UI.term.height - 1, - autospace = true, - }, - statusBar = UI.StatusBar { - status = 'Select peripheral' - }, - accelerators = { - q = 'quit', - }, -} - -function peripheralsPage.grid:draw() - local sides = peripheral.getNames() - - Util.clear(self.values) - for _,side in pairs(sides) do - table.insert(self.values, { - type = peripheral.getType(side), - side = side - }) - end - self:update() - self:adjustWidth() - UI.Grid.draw(self) -end - -function peripheralsPage:updatePeripherals() - if UI:getCurrentPage() == self then - self.grid:draw() - self:sync() - end -end - -function peripheralsPage:eventHandler(event) - if event.type == 'quit' then - Event.exitPullEvents() - - elseif event.type == 'grid_select' then - UI:setPage('methods', event.selected) - - end - return UI.Page.eventHandler(self, event) -end - ---[[ -- MethodsPage -- ]] -- -local methodsPage = UI.Page { - grid = UI.ScrollingGrid { - columns = { - { heading = 'Name', key = 'name', width = UI.term.width } - }, - sortColumn = 'name', - height = 7, - }, - viewportConsole = UI.ViewportWindow { - y = 8, - height = UI.term.height - 8, - backgroundColor = colors.brown, - }, - statusBar = UI.StatusBar { - status = 'q to return', - }, - accelerators = { - q = 'back', - backspace = 'back', - }, -} - -function methodsPage:enable(p) - - self.peripheral = p or self.peripheral - - local p = peripheral.wrap(self.peripheral.side) - if p.getDocs then - self.grid.values = { } - for k,v in pairs(p.getDocs()) do - table.insert(self.grid.values, { - name = k, - doc = v, - }) - end - elseif not p.getAdvancedMethodsData then - self.grid.values = { } - for name,f in pairs(p) do - table.insert(self.grid.values, { - name = name, - noext = true, - }) - end - else - self.grid.values = p.getAdvancedMethodsData() - for name,f in pairs(self.grid.values) do - f.name = name - end - end - - self.viewportConsole.offy = 0 - - self.grid:update() - self.grid:setIndex(1) - - self.statusBar:setStatus(self.peripheral.type) - UI.Page.enable(self) -end - -function methodsPage:eventHandler(event) - if event.type == 'back' then - UI:setPage(peripheralsPage) - return true - elseif event.type == 'grid_focus_row' then - self.viewportConsole.offy = 0 - self.viewportConsole:draw() - end - return UI.Page.eventHandler(self, event) -end - -function methodsPage.viewportConsole:draw() - local c = self - local method = methodsPage.grid:getSelected() - - c:clear() - c:setCursorPos(1, 1) - - if method.noext then - c.cursorY = 2 - c:print('No extended Information') - return 2 - end - - if method.doc then - c:print(method.doc, nil, colors.yellow) - c.ymax = c.cursorY + 1 - return - end - - if method.description then - c:print(method.description) - end - - c.cursorY = c.cursorY + 2 - c.cursorX = 1 - - if method.returnTypes ~= '()' then - c:print(method.returnTypes .. ' ', nil, colors.yellow) - end - c:print(method.name, nil, colors.black) - c:print('(') - - local maxArgLen = 1 - - for k,arg in ipairs(method.args) do - if #arg.description > 0 then - maxArgLen = math.max(#arg.name, maxArgLen) - end - local argName = arg.name - local fg = colors.green - if arg.optional then - argName = string.format('[%s]', arg.name) - fg = colors.orange - end - c:print(argName, nil, fg) - if k < #method.args then - c:print(', ') - end - end - c:print(')') - - c.cursorY = c.cursorY + 1 - - if #method.args > 0 then - for _,arg in ipairs(method.args) do - if #arg.description > 0 then - c.cursorY = c.cursorY + 1 - c.cursorX = 1 - local fg = colors.green - if arg.optional then - fg = colors.orange - end - c:print(arg.name .. ': ', nil, fg) - c.cursorX = maxArgLen + 3 - c:print(arg.description, nil, nil, maxArgLen + 3) - end - end - end - - c.ymax = c.cursorY + 1 -end - -Event.on('peripheral', function() - peripheralsPage:updatePeripherals() -end) - -Event.on('peripheral_detach', function() - peripheralsPage:updatePeripherals() -end) - -UI:setPage(peripheralsPage) - -UI:setPages({ - methods = methodsPage, -}) - -UI:pullEvents() diff --git a/sys/apps/Script.lua b/sys/apps/Script.lua deleted file mode 100644 index c90545e..0000000 --- a/sys/apps/Script.lua +++ /dev/null @@ -1,563 +0,0 @@ -requireInjector(getfenv(1)) - -local Config = require('config') -local Event = require('event') -local Socket = require('socket') -local UI = require('ui') -local Util = require('util') - -local GROUPS_PATH = 'usr/groups' -local SCRIPTS_PATH = 'usr/etc/scripts' - -multishell.setTitle(multishell.getCurrent(), 'Script') -UI:configure('script', ...) - -local config = { - showGroups = false, - variables = [[{ - COMPUTER_ID = os.getComputerID(), - }]], -} - -Config.load('script', config) - -local width = math.floor(UI.term.width / 2) - 1 -if UI.term.width % 2 ~= 0 then - width = width + 1 -end - -function processVariables(script) - - local fn = loadstring('return ' .. config.variables) - if fn then - local variables = fn() - - for k,v in pairs(variables) do - local token = string.format('{%s}', k) - script = script:gsub(token, v) - end - end - return script -end - -function invokeScript(computer, scriptName) - - local script = Util.readFile(scriptName) - if not script then - print('Unable to read script file') - end - - local socket = Socket.connect(computer.id, 161) - if not socket then - print('Unable to connect to ' .. computer.id) - return - end - - script = processVariables(script) - - Util.print('Running %s on %s', scriptName, computer.label) - socket:write({ type = 'script', args = script }) - --[[ - local response = socket:read(2) - - if response and response.result then - if type(response.result) == 'table' then - print(textutils.serialize(response.result)) - else - print(tostring(response.result)) - end - else - printError('No response') - end - --]] - - socket:close() -end - -function runScript(computerOrGroup, scriptName) - if computerOrGroup.id then - invokeScript(computerOrGroup, scriptName) - else - local list = computerOrGroup.list - if computerOrGroup.path then - list = Util.readTable(computerOrGroup.path) - end - if list then - for _,computer in pairs(list) do - invokeScript(computer, scriptName) - end - end - end -end - -local function getActiveComputers(t) - t = t or { } - Util.clear(t) - for k,computer in pairs(_G.network) do - if computer.active then - t[k] = computer - end - end - return t -end - -local function getTurtleList() - local turtles = { - label = 'Turtles', - list = { }, - } - for k,computer in pairs(getActiveComputers()) do - if computer.fuel then - turtles.list[k] = computer - end - end - return turtles -end - -local args = { ... } -if #args == 2 then - local key = args[1] - local script = args[2] - local target - if tonumber(key) then - target = _G.network[tonumber(key)] - elseif key == 'All' then - target = { - list = Util.shallowCopy(getActiveComputers()), - } - elseif key == 'Localhost' then - target = { id = os.getComputerID() } - elseif key == 'Turtles' then - target = getTurtleList() - else - target = Util.readTable(fs.combine(GROUPS_PATH, key)) - end - - if not target then - error('Syntax: Script