diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/mktmp.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/mktmp.lua index 7e6327f3c..abcc1ec18 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/mktmp.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/mktmp.lua @@ -1,12 +1,11 @@ local fs = require("filesystem") -local uuid = require("uuid") local shell = require("shell") local sh = require("sh") local touch = loadfile(shell.resolve("touch", "lua")) local mkdir = loadfile(shell.resolve("mkdir", "lua")) -if not uuid or not touch then +if not touch then local errorMessage = "missing tools for mktmp" io.stderr:write(errorMessage .. '\n') return false, errorMessage @@ -57,24 +56,14 @@ if not fs.exists(prefix) then return 1 end -while true do - local tmp = prefix .. uuid.next() - if not fs.exists(tmp) then +local tmp = os.tmpname() +local ok, reason = (directory and mkdir or touch)(tmp) - local ok, reason - if directory then - ok, reason = mkdir(tmp) - else - ok, reason = touch(tmp) - end - - if sh.internal.command_passed(ok) then - if verbose then - print(tmp) - end - return tmp - else - return ok, reason - end +if sh.internal.command_passed(ok) then + if verbose then + print(tmp) end + return tmp end + +return ok, reason diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua index 018f79efe..488553626 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua @@ -65,9 +65,10 @@ local function rawRunCommand(conf, name, cmd, args, ...) return true end elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then - result, what = xpcall(result["stop"], debug.traceback, ...) + local daemon = result + result, what = xpcall(daemon["stop"], debug.traceback, ...) if result then - result, what = xpcall(result["start"], debug.traceback, ...) + result, what = xpcall(daemon["start"], debug.traceback, ...) if result then return true end diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/90_filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/90_filesystem.lua index fd64516aa..5b0da6ca7 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/boot/90_filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/boot/90_filesystem.lua @@ -13,7 +13,7 @@ local function onInit() local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log") local log = io.open(path, "a") if log then - log:write(reason .. "\n") + log:write(tostring(result) .. ":" .. tostring(reason) .. "\n") log:close() end end @@ -34,20 +34,17 @@ local function onComponentAdded(_, address, componentType) name = fs.concat("/mnt", name) fs.mount(proxy, name) if fs.isAutorunEnabled() then - local function run() - local file = shell.resolve(fs.concat(name, "autorun"), "lua") or - shell.resolve(fs.concat(name, ".autorun"), "lua") - if file then - local result, reason = shell.execute(file, _ENV, proxy) - if not result then - error(reason, 0) - end + local file = shell.resolve(fs.concat(name, "autorun"), "lua") or + shell.resolve(fs.concat(name, ".autorun"), "lua") + if file then + local run = function() + assert(shell.execute(file, _ENV, proxy)) + end + if isInitialized then + run() + else + table.insert(pendingAutoruns, run) end - end - if isInitialized then - run() - else - table.insert(pendingAutoruns, run) end end end diff --git a/src/main/resources/assets/opencomputers/loot/openos/init.lua b/src/main/resources/assets/opencomputers/loot/openos/init.lua index 857b15e61..9c05bc193 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/init.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/init.lua @@ -1,167 +1,21 @@ do - _G._OSVERSION = "OpenOS 1.6" - - local component = component - local computer = computer - local unicode = unicode - - -- Runlevel information. - local runlevel, shutdown = "S", computer.shutdown - computer.runlevel = function() return runlevel end - computer.shutdown = function(reboot) - runlevel = reboot and 6 or 0 - if os.sleep then - computer.pushSignal("shutdown") - os.sleep(0.1) -- Allow shutdown processing. - end - shutdown(reboot) - end - - -- Low level dofile implementation to read filesystem libraries. - local rom = {} - function rom.invoke(method, ...) - return component.invoke(computer.getBootAddress(), method, ...) - end - function rom.open(file) return rom.invoke("open", file) end - function rom.read(handle) return rom.invoke("read", handle, math.huge) end - function rom.close(handle) return rom.invoke("close", handle) end - function rom.inits() return ipairs(rom.invoke("list", "boot")) end - function rom.isDirectory(path) return rom.invoke("isDirectory", path) end - - local screen = component.list('screen', true)() - for address in component.list('screen', true) do - if #component.invoke(address, 'getKeyboards') > 0 then - screen = address - break - end - end - - _G.boot_screen = screen - - -- Report boot progress if possible. - local gpu = component.list("gpu", true)() - local w, h - if gpu and screen then - component.invoke(gpu, "bind", screen) - w, h = component.invoke(gpu, "maxResolution") - component.invoke(gpu, "setResolution", w, h) - component.invoke(gpu, "setBackground", 0x000000) - component.invoke(gpu, "setForeground", 0xFFFFFF) - component.invoke(gpu, "fill", 1, 1, w, h, " ") - end - local y = 1 - local function status(msg) - if gpu and screen then - component.invoke(gpu, "set", 1, y, msg) - if y == h then - component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) - component.invoke(gpu, "fill", 1, h, w, 1, " ") - else - y = y + 1 - end - end - end - - status("Booting " .. _OSVERSION .. "...") - - -- Custom low-level loadfile/dofile implementation reading from our ROM. - local function loadfile(file) - status("> " .. file) - local handle, reason = rom.open(file) + local loadfile = load([[return function(file) + local handle, reason = invoke(addr, "open", file) if not handle then error(reason) end local buffer = "" repeat - local data, reason = rom.read(handle) + local data, reason = invoke(addr, "read", handle, math.huge) if not data and reason then error(reason) end buffer = buffer .. (data or "") until not data - rom.close(handle) - return load(buffer, "=" .. file) - end - - local function dofile(file) - local program, reason = loadfile(file) - if program then - local result = table.pack(pcall(program)) - if result[1] then - return table.unpack(result, 2, result.n) - else - error(result[2]) - end - else - error(reason) - end - end - - status("Initializing package management...") - - -- Load file system related libraries we need to load other stuff moree - -- comfortably. This is basically wrapper stuff for the file streams - -- provided by the filesystem components. - local package = dofile("/lib/package.lua") - - do - -- Unclutter global namespace now that we have the package module. - _G.component = nil - _G.computer = nil - _G.process = nil - _G.unicode = nil - - -- Initialize the package module with some of our own APIs. - package.loaded.component = component - package.loaded.computer = computer - package.loaded.unicode = unicode - package.preload["buffer"] = loadfile("/lib/buffer.lua") - package.preload["filesystem"] = loadfile("/lib/filesystem.lua") - - -- Inject the package and io modules into the global namespace, as in Lua. - _G.package = package - _G.io = loadfile("/lib/io.lua")() - - --mark modules for delay loaded api - package.delayed["text"] = true - package.delayed["sh"] = true - package.delayed["transforms"] = true - package.delayed["term"] = true - end - - status("Initializing file system...") - - -- Mount the ROM and temporary file systems to allow working on the file - -- system module from this point on. - require("filesystem").mount(computer.getBootAddress(), "/") - package.preload={} - - status("Running boot scripts...") - - -- Run library startup scripts. These mostly initialize event handlers. - local scripts = {} - for _, file in rom.inits() do - local path = "boot/" .. file - if not rom.isDirectory(path) then - table.insert(scripts, path) - end - end - table.sort(scripts) - for i = 1, #scripts do - dofile(scripts[i]) - end - - status("Initializing components...") - - for c, t in component.list() do - computer.pushSignal("component_added", c, t) - end - os.sleep(0.5) -- Allow signal processing by libraries. - status("Initializing system...") - - computer.pushSignal("init") -- so libs know components are initialized. - require("event").pull(1, "init") -- Allow init processing. - runlevel = 1 + invoke(addr, "close", handle) + return load(buffer, "=" .. file, "bt", _G) + end]], "=loadfile", "bt", {load=load,math=math,addr=computer.getBootAddress(), invoke=component.invoke})() + loadfile("/lib/tools/boot.lua")(loadfile) end while true do diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua index 832b26ec4..a6c7ba909 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/devfs.lua @@ -1,5 +1,8 @@ local fs = require("filesystem") local comp = require("component") +local text = require("text") + +local sys = {} -- base class local function new_node(parent, name, is_dir, proxy) local node = {parent=parent, name=name, is_dir=is_dir, proxy=proxy} @@ -9,189 +12,231 @@ local function new_node(parent, name, is_dir, proxy) return node end -local function new_devfs_dir(name) - local sys = {} - sys.mtab = new_node(nil, name or "/", true) +-- node may support isAvailable, and may choose to not be available +local function isAvailable(node) + return node and not (node.proxy and node.proxy.isAvailable and not node.proxy.isAvailable()) +end - -- returns: dir, point or path - -- node (table): the handler responsible for the path - -- this is essentially the device filesystem that is registered for the given path - -- point (string): the point name (like a file name) - function sys.findNode(path, create) - checkArg(1, path, "string") - local segments = fs.segments(path) - local node = sys.mtab - while #segments > 0 do - local name = table.remove(segments, 1) - local prev_path = path - path = table.concat(segments, "/") - - if not node.children[name] then - if not create then - path = prev_path - break - end - node.children[name] = new_node(node, name, true, false) - end - - node = node.children[name] - - if node.proxy then -- if there is a proxy handler we stop searching here - break - end - end - - -- only dirs can have trailing path - -- trailing path on a dev point (file) not allowed - if path == "" or node.is_dir and node.proxy then - return node, path - end - end - - function sys.invoke(method, path, ...) - local node, rest = sys.findNode(path) - if not node or -- not found - rest == "" and node.is_dir or -- path is dir - not node.proxy[method] then -- optional method - return 0 - end - -- proxy could be a file, which doesn't take an argument, but it can be ignored if passed - return node.proxy[method](rest) - end - - function sys.size(path) - return sys.invoke("size", path) - end - - function sys.lastModified(path) - return sys.invoke("lastModified", path) - end - - function sys.isDirectory(path) - local node, rest = sys.findNode(path) - if not node then - return - end - - if rest == "" then - return node.is_dir - elseif node.proxy then - return node.proxy.isDirectory(rest) - end - end - - function sys.open(path, mode) - checkArg(1, path, "string") - checkArg(2, mode, "string", "nil") - - if not sys.exists(path) then - return nil, path.." file not found" - elseif sys.isDirectory(path) then - return nil, path.." is a directory" - end - - mode = mode or "r" - -- everything at this level should be a binary open - mode = mode:gsub("b", "") - - if not ({a=true,w=true,r=true})[mode] then - return nil, "invalid mode" - end - - local node, rest = sys.findNode(path) - -- there must be a node, else exists would have failed - - local args = {} - if rest ~= "" then - -- having more rest means we expect the proxy fs to open the point - args[1] = rest - end - args[#args+1] = mode - - return node.proxy.open(table.unpack(args)) - end - - function sys.list(path) - local node, rest = sys.findNode(path) - if not node or (rest ~= "" and not node.is_dir) then-- not found - return {} - elseif rest == "" and not node.is_dir then -- path is file - return {path} - elseif node.proxy then - -- proxy could be a file, which doesn't take an argument, but it can be ignored if passed - return node.proxy.list(rest) - end - - -- rest == "" and node.is_dir - local keys = {} - for k in pairs(node.children) do - table.insert(keys, k) - end - return keys - end - - function sys.remove(path) - return nil, "cannot remove devfs files or directories" - --checkArg(1, path, "string") - - --if path == "" then - -- return nil, "no such file or directory" - --end - - --if not sys.exists(path) then - -- return nil, path.." file not found" - --end - - --local node, rest = sys.findNode(path) - - --if rest ~= "" then -- if rest is not resolved, this isn't our path - -- return node.proxy.remove(rest) - --end - - --node.parent.children[node.name] = nil - end - - function sys.exists(path) - checkArg(1, path, "string") - local node, rest = sys.findNode(path) - - if not node then - return false - elseif rest == "" then - return true - else - return node.proxy.exists(rest) - end - end - - function sys.create(path, handler) - if sys.exists(path) then - return nil, "path already exists" - end - - local segments = fs.segments(path) - local target = table.remove(segments) +-- returns: dir, point or path +-- node (table): the handler responsible for the path +-- this is essentially the device filesystem that is registered for the given path +-- point (string): the point name (like a file name) +function sys:findNode(path, create) + checkArg(1, path, "string") + local segments = fs.segments(path) + local node = self.mtab + while #segments > 0 do + local name = table.remove(segments, 1) + local prev_path = path path = table.concat(segments, "/") - if not target or target == "" then - return nil, "missing argument" + local next_node = node.children[name] + if not isAvailable(next_node) then + next_node = nil end - local node, rest = sys.findNode(path, true) - if rest ~= "" then - return node.proxy.create(rest, handler) + if not next_node then + if not create then + path = prev_path + break + end + node.children[name] = new_node(node, name, true, false) + end + + node = node.children[name] + + if node.proxy then -- if there is a proxy handler we stop searching here + break end - node.children[target] = new_node(node, target, not not handler.list, handler) - return true end - return sys + -- only dirs can have trailing path + -- trailing path on a dev point (file) not allowed + if path == "" or node.is_dir and node.proxy then + return node, path + end +end + +function sys:invoke(method, path, ...) + local node, rest = self.findNode(path) + if not node or -- not found + rest == "" and node.is_dir or -- path is dir + not node.proxy[method] then -- optional method + return 0 + end + -- proxy could be a file, which doesn't take an argument, but it can be ignored if passed + return node.proxy[method](rest) +end + +function sys:size(path) + return self.invoke("size", path) +end + +function sys:lastModified(path) + return self.invoke("lastModified", path) +end + +function sys:isDirectory(path) + local node, rest = self.findNode(path) + if not node then + return + end + + if rest == "" then + return node.is_dir + elseif node.proxy then + return node.proxy.isDirectory(rest) + end +end + +function sys:open(path, mode) + checkArg(1, path, "string") + checkArg(2, mode, "string", "nil") + + if not self.exists(path) then + return nil, path.." file not found" + elseif self.isDirectory(path) then + return nil, path.." is a directory" + end + + mode = mode or "r" + -- everything at this level should be a binary open + mode = mode:gsub("b", "") + + if not ({a=true,w=true,r=true})[mode] then + return nil, "invalid mode" + end + + local node, rest = self.findNode(path) + -- there must be a node, else exists would have failed + + local args = {} + if rest ~= "" then + -- having more rest means we expect the proxy fs to open the point + args[1] = rest + end + args[#args+1] = mode + + return node.proxy.open(table.unpack(args)) +end + +function sys:list(path) + local node, rest = self.findNode(path) + if not node or (rest ~= "" and not node.is_dir) then-- not found + return {} + elseif rest == "" and not node.is_dir then -- path is file + return {path} + elseif node.proxy then + -- proxy could be a file, which doesn't take an argument, but it can be ignored if passed + return node.proxy.list(rest) + end + + -- rest == "" and node.is_dir + local keys = {} + for k,node in pairs(node.children) do + if isAvailable(node) then + table.insert(keys, k) + end + end + return keys +end + +function sys:remove(path) + return nil, "cannot remove devfs files or directories" + --checkArg(1, path, "string") + + --if path == "" then + -- return nil, "no such file or directory" + --end + + --if not self.exists(path) then + -- return nil, path.." file not found" + --end + + --local node, rest = self.findNode(path) + + --if rest ~= "" then -- if rest is not resolved, this isn't our path + -- return node.proxy.remove(rest) + --end + + --node.parent.children[node.name] = nil +end + +function sys:exists(path) + checkArg(1, path, "string") + local node, rest = self.findNode(path) + + if not node then + return false + elseif rest == "" then + return true + else + return node.proxy.exists(rest) + end +end + +function sys:create(path, handler) + if self.exists(path) then + return nil, "path already exists" + end + + local segments = fs.segments(path) + local target = table.remove(segments) + path = table.concat(segments, "/") + + if not target or target == "" then + return nil, "missing argument" + end + + local node, rest = self.findNode(path, true) + if rest ~= "" then + return node.proxy.create(rest, handler) + end + node.children[target] = new_node(node, target, not not handler.list, handler) + return true +end + +local function new_devfs_dir(name) + local sys_child = setmetatable({}, {__index=function(tbl,key) + if sys[key] then + return function(...) + return sys[key](tbl, ...) + end + end + end}) + sys_child.mtab = new_node(nil, name or "/", true) + + return sys_child end local devfs = new_devfs_dir() local bfd = "bad file descriptor" +-- to allow sub dirs to act like sub devfs +devfs.new_dir = new_devfs_dir +devfs.new_node = new_node +function devfs.new_callback_proxy(read_callback, write_callback) + return + { + open = function(mode) + if ({r=true, rb=true})[mode] then + if not read_callback then + return nil, "file cannot be opened for read" + end + return text.internal.reader(read_callback()) + end + if not write_callback then + return nil, "file cannot be opened for write" + end + return text.internal.writer(write_callback, ({a=true,ab=true})[mode] and read_callback()) + end, + size = function() + return read_callback and string.len(read_callback()) or 0 + end + } +end + function devfs.setLabel(value) error("drive does not support labeling") end @@ -268,12 +313,15 @@ end -- /dev is a special handler local function devfs_load(key) - return require("tools/devfs/" .. key) + -- using loadfile to allow us to pass args + -- load order complication: some dev points are dirs that want to use devfs api, but can't require devfs + devfs.create(key, loadfile("/lib/tools/devfs/" .. key .. ".lua", "bt", _G)(devfs)) end -devfs.create("null", devfs_load("null")) -devfs.create("random", devfs_load("random")) -devfs.create("eeprom", devfs_load("eeprom")) -devfs.create("eeprom-data", devfs_load("eeprom-data")) +devfs_load("random") +devfs_load("null") +devfs_load("eeprom") +devfs_load("eeprom-data") +--devfs_load("filesystems") return devfs diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua index c22e753b2..eb887efd9 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua @@ -1,9 +1,12 @@ local computer = require("computer") local keyboard = require("keyboard") -local event, listeners, timers = {}, {}, {} +local event = {} +local handlers = {} local lastInterrupt = -math.huge +event.handlers = handlers + local function call(callback, ...) local result, message = pcall(callback, ...) if not result and type(event.onError) == "function" then @@ -13,42 +16,78 @@ local function call(callback, ...) return message end -local function dispatch(signal, ...) - if listeners[signal] then - local function callbacks() - local list = {} - for index, listener in ipairs(listeners[signal]) do - list[index] = listener - end - return list +function event.register(key, callback, interval, times) + local handler = + { + key = key, + times = times or 0, + callback = callback, + interval = interval or math.huge, + } + + handler.timeout = computer.uptime() + handler.interval + + if not interval then + handler.times = math.huge + end + + local id = 0 + repeat + id = id + 1 + until not handlers[id] + + handlers[id] = handler + return id +end + +local function time_to_nearest() + local timeout = math.huge + for _,handler in pairs(handlers) do + if timeout > handler.timeout then + timeout = handler.timeout end - for _, callback in ipairs(callbacks()) do - if call(callback, signal, ...) == false then - event.ignore(signal, callback) -- alternative method of removing a listener + end + return timeout +end + +local function dispatch(...) + local signal = (...) + local eligable = {} + local ids_to_remove = {} + local time = computer.uptime() + for id, handler in pairs(handlers) do + -- timers have false keys + -- nil keys match anything + local key = handler.key + key = (key == nil and signal) or key + if key == signal or time >= handler.timeout then + + -- push ticks to end of list (might be slightly faster to fire them last) + table.insert(eligable, select(handler.key and 1 or 2, 1, {handler.callback, id})) + + handler.times = handler.times - 1 + handler.timeout = computer.uptime() + handler.interval + if handler.times <= 0 then + table.insert(ids_to_remove, id) end end end + for _,pack in ipairs(eligable) do + if call(pack[1], ...) == false then + table.insert(ids_to_remove, pack[2]) + end + end + for _,id in ipairs(ids_to_remove) do + handlers[id] = nil + end end -local function tick() - local function elapsed() - local list = {} - for id, timer in pairs(timers) do - if timer.after <= computer.uptime() then - table.insert(list, timer.callback) - timer.times = timer.times - 1 - if timer.times <= 0 then - timers[id] = nil - else - timer.after = computer.uptime() + timer.interval - end - end - end - return list - end - for _, callback in ipairs(elapsed()) do - call(callback) - end +local _pullSignal = computer.pullSignal +computer.pullSignal = function(...) + return (function(...) + dispatch(...) + return ... + end)(_pullSignal(...)) end local function createPlainFilter(name, ...) @@ -94,8 +133,8 @@ end function event.cancel(timerId) checkArg(1, timerId, "number") - if timers[timerId] then - timers[timerId] = nil + if handlers[timerId] then + handlers[timerId] = nil return true end return false @@ -104,15 +143,10 @@ end function event.ignore(name, callback) checkArg(1, name, "string") checkArg(2, callback, "function") - if listeners[name] then - for i = 1, #listeners[name] do - if listeners[name][i] == callback then - table.remove(listeners[name], i) - if #listeners[name] == 0 then - listeners[name] = nil - end - return true - end + for id, handler in pairs(handlers) do + if handler.key == name and handler.callback == callback then + handlers[id] = nil + return true end end return false @@ -121,17 +155,12 @@ end function event.listen(name, callback) checkArg(1, name, "string") checkArg(2, callback, "function") - if listeners[name] then - for i = 1, #listeners[name] do - if listeners[name][i] == callback then - return false - end + for id, handler in pairs(handlers) do + if handler.key == name and handler.callback == callback then + return false end - else - listeners[name] = {} end - table.insert(listeners[name], callback) - return true + return event.register(name, callback, nil, nil) end function event.onError(message) @@ -169,7 +198,6 @@ function event.pullMultiple(...) end end return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n))) - end function event.pullFiltered(...) @@ -187,15 +215,8 @@ function event.pullFiltered(...) local deadline = seconds and (computer.uptime() + seconds) or math.huge repeat - local closest = deadline - for _, timer in pairs(timers) do - closest = math.min(closest, timer.after) - end + local closest = math.min(deadline, time_to_nearest()) local signal = table.pack(computer.pullSignal(closest - computer.uptime())) - if signal.n > 0 then - dispatch(table.unpack(signal, 1, signal.n)) - end - tick() if event.shouldInterrupt() then lastInterrupt = computer.uptime() error("interrupted", 0) @@ -230,20 +251,12 @@ function event.timer(interval, callback, times) checkArg(1, interval, "number") checkArg(2, callback, "function") checkArg(3, times, "number", "nil") - local id - repeat - id = math.floor(math.random(1, 0x7FFFFFFF)) - until not timers[id] - timers[id] = { - interval = interval, - after = computer.uptime() + interval, - callback = callback, - times = times or 1 - } - return id + return event.register(false, callback, interval, times) end + -- users may expect to find event.push to exist event.push = computer.pushSignal + ------------------------------------------------------------------------------- return event diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua index d06cbd796..ec1de9ebf 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua @@ -137,7 +137,7 @@ function require(module) error(table.concat(errorMsg, "\n"), 2) end else - error("already loading: " .. module .. debug.traceback(), 2) + error("already loading: " .. module .. "\n" .. debug.traceback(), 2) end end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua index 43568d218..3a54bf675 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua @@ -126,13 +126,13 @@ function plib.internal.redirectRead(pm) return reader end -function plib.internal.create(fp) +function plib.internal.create(fp, init, name) local _co = process.info().data.coroutine_handler local pco = setmetatable( { stack = {}, - next = nil, + next = false, create = _co.create, wrap = _co.wrap, previous_handler = _co @@ -161,19 +161,19 @@ function plib.internal.create(fp) return _co.yield(...) end function pco.set_unwind(from) - pco.next = nil + pco.next = false if from then local index = pco.index_of(from) if index then pco.stack = tx.sub(pco.stack, 1, index-1) - pco.next = pco.stack[index-1] + pco.next = pco.stack[index-1] or false end end end function pco.resume_all(...) local base = pco.stack[1] local top = pco.top() - if type(base) ~= "thread" or _co.status(base) ~= "suspended" or + if type(base) ~= "thread" or _co.status(base) ~= "suspended" or type(top) ~= "thread" or _co.status(top) ~= "suspended" then return false end @@ -193,7 +193,7 @@ function plib.internal.create(fp) checkArg(1, thread, "thread") local status = pco.status(thread) if status ~= "suspended" then - local msg = string.format("cannot resume %s coroutine", + local msg = string.format("cannot resume %s coroutine", status == "dead" and "dead" or "non-suspended") return false, msg end @@ -212,7 +212,7 @@ function plib.internal.create(fp) return true, _co.yield(...) -- pass args to resume next else -- the stack is not running - pco.next = nil + pco.next = false local yield_args = table.pack(_co.resume(thread, ...)) if #pco.stack > 0 then -- thread may have crashed (crash unwinds as well) @@ -226,7 +226,7 @@ function plib.internal.create(fp) -- in such a case, yield out first, then resume where we left off if pco.next and pco.next ~= thread then local next = pco.next - pco.next = nil + pco.next = false return pco.resume(next, table.unpack(yield_args,2,yield_args.n)) end end @@ -251,7 +251,7 @@ function plib.internal.create(fp) end if fp then - pco.stack = {process.load(fp,nil,nil--[[init]],"pco root")} + pco.stack = {process.load(fp,nil,init,name or "pco root")} process.info(pco.stack[1]).data.coroutine_handler = pco end @@ -320,7 +320,7 @@ function pipeManager.new(prog, mode, env) ) pm.prog_id = pm.mode == "r" and 1 or 2 pm.self_id = pm.mode == "r" and 2 or 1 - pm.handler = pm.mode == "r" and + pm.handler = pm.mode == "r" and function()return pipeManager.reader(pm)end or function()pm.dead=true end @@ -329,10 +329,8 @@ function pipeManager.new(prog, mode, env) pm.commands[pm.self_id] = {pm.handler, {}} pm.root = function() - local startup_args = {} - local reason - pm.threads, reason = sh.internal.createThreads(pm.commands, {}, pm.env) + pm.threads, reason = sh.internal.createThreads(pm.commands, pm.env, {[pm.prog_id]=table.pack(pm.env,pm.prog)}) if not pm.threads then pm.dead = true @@ -340,8 +338,7 @@ function pipeManager.new(prog, mode, env) end pm.pipe = process.info(pm.threads[1]).data.io[1] - process.info(pm.threads[pm.prog_id]).data.args = {pm.env,pm.prog} - + -- if we are the writer, we need args to resume prog if pm.mode == "w" then pm.pipe.stream.redirect[0] = plib.internal.redirectRead(pm) @@ -365,7 +362,7 @@ function plib.popen(prog, mode, env) end pm.pco=plib.internal.create(pm.root) - + local pfd = require("buffer").new(mode, pipeStream.new(pm)) pfd:setvbuf("no", 0) -- 2nd are to read chunk size diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua index f62b8b1b2..15f362d26 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua @@ -20,6 +20,12 @@ function process.findProcess(co) end ------------------------------------------------------------------------------- +local function parent_data(pre, tbl, k, ...) + if tbl and k then + return parent_data(pre, tbl[k], ...) + end + return setmetatable(pre, {__index=tbl}) +end function process.load(path, env, init, name) checkArg(1, path, "string", "function") @@ -28,6 +34,7 @@ function process.load(path, env, init, name) checkArg(4, name, "string", "nil") assert(type(path) == "string" or env == nil, "process cannot load function environemnts") + name = name or "" local p = process.findProcess() if p then @@ -37,31 +44,42 @@ function process.load(path, env, init, name) local code = nil if type(path) == 'string' then - local f, reason = io.open(path) - if not f then - return nil, reason - end - local reason - if f:read(2) == "#!" then - local command = f:read() - if require("text").trim(command) == "" then - reason = "no exec command" - else - code = function() - local result = table.pack(require("shell").execute(command, env, path)) - if not result[1] then - error(result[2], 0) - else - return table.unpack(result, 1, result.n) - end + code = function(...) + local fs, shell = require("filesystem"), require("shell") + local program, reason = shell.resolve(path, 'lua') + if not program then + if fs.isDirectory(shell.resolve(path)) then + io.stderr:write(path .. ": is a directory\n") + return 126 end + io.stderr:write(path .. ": " .. reason .. "\n") + return 127 end - else - code, reason = loadfile(path, "t", env) - end - f:close() - if not code then - return nil, reason + os.setenv("_", program) + local f, reason = io.open(program) + if not f then + io.stderr:write("could not read '" .. program .. "': " .. tostring(reason) .. "\n") + return 1 + end + local shabang = f:read(2) + local command = f:read() + f:close() + if shabang == "#!" then + if require("text").trim(command or "") == "" then + return -- nothing to do + end + local result = table.pack(require("shell").execute(command, env, program, ...)) + if not result[1] then + error(result[2]) + end + return table.unpack(result) + end + command, reason = loadfile(program, "bt", env) + if not command then + io.stderr:write(program..(reason or ""):gsub("^[^:]*", "").."\n") + return 128 + end + return command(...) end else -- path is code code = path @@ -69,43 +87,46 @@ function process.load(path, env, init, name) local thread = nil thread = coroutine.create(function(...) - if init then - init() - end -- pcall code so that we can remove it from the process list on exit - local result = + local result = { - xpcall(code, function(msg) - if type(msg) == 'table' then return msg end - local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1') - return string.format('%s:\n%s', msg or '', stack) - end, ...) + xpcall(function(...) + os.setenv("_", name) + init = init or function(...) return ... end + return code(init(...)) + end, + function(msg) + -- msg can be a custom error object + if type(msg) == 'table' then + if msg.reason ~= "terminated" then + io.stderr:write(msg.reason.."\n") + end + return msg.code + end + local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1') + io.stderr:write(string.format('%s:\n%s', msg or '', stack)) + return 128 -- syserr + end, ...) } process.internal.close(thread) - if not result[1] then - -- msg can be a custom error object - local msg = result[2] - if type(msg) == 'table' then - if msg.reason~="terminated" then error(msg.reason,2) end - result={0,msg.code} - else - error(msg,2) - end + --result[1] is false if the exception handler also crashed + if not result[1] and type(result[2]) ~= "number" then + require("event").onError(string.format("process library exception handler crashed: %s", tostring(result[2]))) end - return select(2,table.unpack(result)) - end,true) + return select(2, table.unpack(result)) + end, true) process.list[thread] = { path = path, command = name, env = env, - data = setmetatable( + data = parent_data( { handles = {}, - io = setmetatable({}, {__index=p and p.data and p.data.io or nil}), - coroutine_handler = setmetatable({}, {__index=p and p.data and p.data.coroutine_handler or nil}), - }, {__index=p and p.data or nil}), + io = parent_data({}, p, "data", "io"), + coroutine_handler = parent_data({}, p, "data", "coroutine_handler"), + }, p, "data"), parent = p, - instances = setmetatable({}, {__mode="v"}) + instances = setmetatable({}, {__mode="v"}), } return thread end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua index f6f6c48ba..fe3e48f05 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua @@ -23,7 +23,6 @@ local local_env = {event=event,fs=fs,process=process,shell=shell,term=term,text= sh.internal.globbers = {{"*",".*"},{"?","."}} sh.internal.ec = {} sh.internal.ec.parseCommand = 127 -sh.internal.ec.sysError = 128 sh.internal.ec.last = 0 function sh.getLastExitCode() @@ -211,26 +210,20 @@ function sh.internal.parseCommand(words) if #words == 0 then return nil end - local evaluated_words = {} + -- evaluated words + local ewords = {} + -- the arguments have < or > which require parsing for redirection + local has_tokens for i=1,#words do for _, arg in ipairs(sh.internal.evaluate(words[i])) do - table.insert(evaluated_words, arg) + table.insert(ewords, arg) + has_tokens = has_tokens or arg:find("[<>]") end end - local eword = evaluated_words[1] - local possible_dir_path = shell.resolve(eword) - if possible_dir_path and fs.isDirectory(possible_dir_path) then - return nil, string.format("%s: is a directory", eword) - end - local program, reason = shell.resolve(eword, "lua") - if not program then - return nil, eword .. ": " .. reason - end - evaluated_words = tx.sub(evaluated_words, 2) - return program, evaluated_words + return table.remove(ewords, 1), ewords, has_tokens end -function sh.internal.createThreads(commands, eargs, env) +function sh.internal.createThreads(commands, env, start_args) -- Piping data between programs works like so: -- program1 gets its output replaced with our custom stream. -- program2 gets its input replaced with our custom stream. @@ -240,14 +233,23 @@ function sh.internal.createThreads(commands, eargs, env) -- custom stream may have "redirect" entries for fallback/duplication. local threads = {} for i = 1, #commands do - local program, args = table.unpack(commands[i]) + local program, c_args, c_has_tokens = table.unpack(commands[i]) local name, thread = tostring(program) local thread_env = type(program) == "string" and env or nil - local thread, reason = process.load(program, thread_env, function() - os.setenv("_", name) + local thread, reason = process.load(program, thread_env, function(...) + local cdata = process.info().data.command + local args, has_tokens, start_args = cdata.args, cdata.has_tokens, cdata.start_args + if has_tokens then + args = sh.internal.buildCommandRedirects(args) + end + + sh.internal.concatn(args, start_args) + sh.internal.concatn(args, {...}, select('#', ...)) + -- popen expects each process to first write an empty string -- this is required for proper thread order - io.write('') + io.write("") + return table.unpack(args, 1, args.n or #args) end, name) threads[i] = thread @@ -259,25 +261,20 @@ function sh.internal.createThreads(commands, eargs, env) return nil, reason end - process.info(thread).data.args = args + local pdata = process.info(thread).data + pdata.command = + { + args = c_args, + has_tokens = c_has_tokens, + start_args = start_args and start_args[i] or {} + } + end if #threads > 1 then sh.internal.buildPipeChain(threads) end - for i = 1, #threads do - local thread = threads[i] - local args = process.info(thread).data.args - - -- smart check if ios should be loaded - if tx.first(args, function(token) return token == "<" or token:find(">") end) then - args, reason = sh.internal.buildCommandRedirects(thread, args) - end - - process.info(thread).data.args = tx.concat(args, eargs or {}) - end - return threads end @@ -285,25 +282,19 @@ function sh.internal.runThreads(threads) local result = {} for i = 1, #threads do -- Emulate CC behavior by making yields a filtered event.pull() - local thread, args = threads[i] + local thread, args = threads[i], {} while coroutine.status(thread) ~= "dead" do - args = args or process.info(thread).data.args result = table.pack(coroutine.resume(thread, table.unpack(args))) if coroutine.status(thread) ~= "dead" then args = sh.internal.handleThreadYield(result) - -- in case this was the end of the line, args is returned - result = args if table.remove(args, 1) then - break + -- in case this was the end of the line, args is returned + return args[2] end end end - if not result[1] then - sh.internal.handleThreadCrash(thread, result) - break - end end - return table.unpack(result) + return result[2] end function sh.internal.executePipes(pipe_parts, eargs, env) @@ -318,23 +309,12 @@ function sh.internal.executePipes(pipe_parts, eargs, env) return sh.internal.ec.parseCommand end end - local threads, reason = sh.internal.createThreads(commands, eargs, env) + local threads, reason = sh.internal.createThreads(commands, env, {[#commands]=eargs}) if not threads then io.stderr:write(reason,"\n") return false end - local result, cmd_result = sh.internal.runThreads(threads) - - if not result then - if cmd_result then - if type(cmd_result) == "string" then - cmd_result = cmd_result:gsub("^/lib/process%.lua:%d+: /", '/') - end - io.stderr:write(tostring(cmd_result),"\n") - end - return sh.internal.ec.sysError - end - return cmd_result + return sh.internal.runThreads(threads) end function sh.execute(env, command, ...) @@ -347,7 +327,8 @@ function sh.execute(env, command, ...) return true, 0 end - local eargs = {...} + -- MUST be table.pack for non contiguous ... + local eargs = table.pack(...) -- simple if reason then @@ -358,6 +339,15 @@ function sh.execute(env, command, ...) return sh.internal.execute_complex(statements, eargs, env) end +function sh.internal.concatn(apack, bpack, bn) + local an = (apack.n or #apack) + bn = bn or bpack.n or #bpack + for i=1,bn do + apack[an + i] = bpack[i] + end + apack.n = an + bn +end + function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result) local action = result[2] if action == nil or type(action) == "number" then @@ -367,20 +357,7 @@ function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result) end end --[[@delayloaded-end@]] -function --[[@delayloaded-start@]] sh.internal.handleThreadCrash(thread, result) - if type(result[2]) == "table" and result[2].reason == "terminated" then - if result[2].code then - result[1] = true - result.n = 1 - else - result[2] = "terminated" - end - elseif type(result[2]) == "string" then - result[2] = debug.traceback(thread, result[2]) - end -end --[[@delayloaded-end@]] - -function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(thread, args) +function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(args, thread) local data = process.info(thread).data local tokens, ios, handles = args, data.io, data.handles args = {} @@ -406,7 +383,7 @@ function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(thread, arg else local file, reason = io.open(shell.resolve(token), mode) if not file then - return nil, "could not open '" .. token .. "': " .. reason + error("could not open '" .. token .. "': " .. reason) end table.insert(handles, file) ios[from_io] = file @@ -447,7 +424,6 @@ function --[[@delayloaded-start@]] sh.internal.buildPipeChain(threads) prev_pipe = pipe end - end --[[@delayloaded-end@]] function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern) @@ -825,7 +801,7 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream() -- if that is the case, it is important to leave stream.buffer alone return self.redirect[0]:read(n) elseif self.buffer == "" then - process.info(self.next).data.args = table.pack(coroutine.yield(table.unpack(self.result))) + coroutine.yield() end local result = string.sub(self.buffer, 1, n) self.buffer = string.sub(self.buffer, n + 1) @@ -842,15 +818,13 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream() return self.redirect[1]:write(value) elseif not self.closed then self.buffer = self.buffer .. value - local args = process.info(self.next).data.args - self.result = table.pack(coroutine.resume(self.next, table.unpack(args))) + local result = table.pack(coroutine.resume(self.next)) if coroutine.status(self.next) == "dead" then self:close() end - if not self.result[1] then - error(self.result[2], 0) + if not result[1] then + error(result[2], 0) end - table.remove(self.result, 1) return self end os.exit(0) -- abort the current process: SIGPIPE diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua index bbca7e583..5c6b53d3d 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua @@ -3,6 +3,7 @@ local event = require("event") local process = require("process") local kb = require("keyboard") local component = require("component") +local computer = require("computer") local keys = kb.keys local term = {} @@ -35,8 +36,11 @@ end function term.setViewport(w,h,dx,dy,x,y,window) window = window or W() - local gw,gh = window.gpu.getViewport() - w,h,dx,dy,x,y = w or gw,h or gh,dx or 0,dy or 0,x or 1,y or 1 + dx,dy,x,y = dx or 0,dy or 0,x or 1,y or 1 + if not w or not h then + local gw,gh = window.gpu.getViewport() + w,h = w or gw, h or gh + end window.w,window.h,window.dx,window.dy,window.x,window.y,window.gw,window.gh= w,h,dx,dy,x,y, gw, gh @@ -60,57 +64,79 @@ function term.isAvailable(w) return w and not not (w.gpu and w.gpu.getScreen()) end -function term.internal.pull(input, c, off, t, ...) - t=t or math.huge - if t < 0 then return end - local w,unpack=W(),table.unpack - local d,h,dx,dy,x,y=term.getViewport(w) +function term.internal.pull(input, timeout, ...) + timeout = timeout or math.huge + + local w = W() + local d, h, dx, dy, x, y = term.getViewport(w) local out = (x<1 or x>d or y<1 or y>h) + if input and out then input:move(0) - y=w.y + y = w.y input:scroll() end - x,y=w.x+dx,w.y+dy - local gpu - if input or not out then - gpu=w.gpu - local sf,sb=gpu.setForeground,gpu.setBackground - c=c or {{gpu.getBackground()},{gpu.getForeground()},gpu.get(x,y)} - local c11,c12 = unpack(c[1]) - local c21,c22 = unpack(c[2]) - -- c can fail if gpu does not have a screen - -- if can happen during a type of race condition when a screen is removed - if not c11 then + x, y = w.x + dx, w.y + dy + local gpu = (input or not out) and w.gpu + + local bgColor, bgIsPalette + local fgColor, fgIsPalette + local char_at_cursor + local blinking + if gpu then + bgColor, bgIsPalette = gpu.getBackground() + -- it can happen during a type of race condition when a screen is removed + if not bgColor then return nil, "interrupted" end - if not off then - sf(c11,c12) - sb(c21,c22) + + fgColor, fgIsPalette = gpu.getForeground() + char_at_cursor = gpu.get(x, y) + + blinking = w.blink + if input then + blinking = input.blink end - gpu.set(x,y,c[3]) - sb(c11,c12) - sf(c21,c22) end - local a={event.pull(math.min(t,0.5),...)} - - if #a>1 or t<.5 then + -- get the next event + local blinked = false + local done = false + local signal + while true do if gpu then - gpu.set(x,y,c[3]) + if not blinked and not done then + gpu.setForeground(bgColor, bgIsPalette) + gpu.setBackground(fgColor, fgIsPalette) + gpu.set(x, y, char_at_cursor) + gpu.setForeground(fgColor, fgIsPalette) + gpu.setBackground(bgColor, bgIsPalette) + blinked = true + elseif blinked then + gpu.set(x, y, char_at_cursor) + blinked = false + end end - return unpack(a) + + if done then + return table.unpack(signal, 1, signal.n) + end + + signal = table.pack(event.pull(math.min(.5, timeout), ...)) + timeout = timeout - .5 + done = signal.n > 1 or timeout < .5 end - local blinking = w.blink - if input then blinking = input.blink end - return term.internal.pull(input,c,blinking and not off,t-0.5,...) end -function term.pull(p,...) - local a,t = {p,...} - if type(p) == "number" then t = table.remove(a,1) end - return term.internal.pull(nil,nil,nil,t,table.unpack(a)) +function term.pull(...) + local args = table.pack(...) + local timeout = nil + if type(args[1]) == "number" then + timeout = table.remove(args, 1) + args.n = args.n - 1 + end + return term.internal.pull(nil, timeout, table.unpack(args, 1, args.n)) end function term.read(history,dobreak,hintHandler,pwchar,filter) @@ -582,7 +608,7 @@ function --[[@delayloaded-start@]] term.internal.build_horizontal_reader(input) _.index,_.data,win.x=0,"",px gpu.fill(px+dx,y+dy,w-px+1-dx,1," ") end -end --[[@delayloaded-end@]] +end --[[@delayloaded-end@]] function --[[@delayloaded-start@]] term.clearLine(window) window = window or W() @@ -627,7 +653,7 @@ function --[[@delayloaded-start@]] term.internal.tab(input,hints) input:update(next) input:move(-tail) end -end --[[@delayloaded-end@]] +end --[[@delayloaded-end@]] function --[[@delayloaded-start@]] term.getGlobalArea(window) local w,h,dx,dy = term.getViewport(window) @@ -645,6 +671,6 @@ function --[[@delayloaded-start@]] term.internal.clipboard(char) char = char:sub(1, first_line - 1) end return char -end --[[@delayloaded-end@]] +end --[[@delayloaded-end@]] return term, local_env diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/boot.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/boot.lua new file mode 100644 index 000000000..04cb238d2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/boot.lua @@ -0,0 +1,146 @@ +-- called from /init.lua +local raw_loadfile = ... + +_G._OSVERSION = "OpenOS 1.6" + +local component = component +local computer = computer +local unicode = unicode + +-- Runlevel information. +local runlevel, shutdown = "S", computer.shutdown +computer.runlevel = function() return runlevel end +computer.shutdown = function(reboot) + runlevel = reboot and 6 or 0 + if os.sleep then + computer.pushSignal("shutdown") + os.sleep(0.1) -- Allow shutdown processing. + end + shutdown(reboot) +end + +local screen = component.list('screen', true)() +for address in component.list('screen', true) do + if #component.invoke(address, 'getKeyboards') > 0 then + screen = address + break + end +end + +_G.boot_screen = screen + +-- Report boot progress if possible. +local gpu = component.list("gpu", true)() +local w, h +if gpu and screen then + component.invoke(gpu, "bind", screen) + w, h = component.invoke(gpu, "maxResolution") + component.invoke(gpu, "setResolution", w, h) + component.invoke(gpu, "setBackground", 0x000000) + component.invoke(gpu, "setForeground", 0xFFFFFF) + component.invoke(gpu, "fill", 1, 1, w, h, " ") +end +local y = 1 +local function status(msg) + if gpu and screen then + component.invoke(gpu, "set", 1, y, msg) + if y == h then + component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) + component.invoke(gpu, "fill", 1, h, w, 1, " ") + else + y = y + 1 + end + end +end + +status("Booting " .. _OSVERSION .. "...") + +-- Custom low-level dofile implementation reading from our ROM. +local loadfile = function(file) + status("> " .. file) + return raw_loadfile(file) +end + +local function dofile(file) + local program, reason = loadfile(file) + if program then + local result = table.pack(pcall(program)) + if result[1] then + return table.unpack(result, 2, result.n) + else + error(result[2]) + end + else + error(reason) + end +end + +status("Initializing package management...") + +-- Load file system related libraries we need to load other stuff moree +-- comfortably. This is basically wrapper stuff for the file streams +-- provided by the filesystem components. +local package = dofile("/lib/package.lua") + +do + -- Unclutter global namespace now that we have the package module. + _G.component = nil + _G.computer = nil + _G.process = nil + _G.unicode = nil + + -- Initialize the package module with some of our own APIs. + package.loaded.component = component + package.loaded.computer = computer + package.loaded.unicode = unicode + package.preload["buffer"] = loadfile("/lib/buffer.lua") + package.preload["filesystem"] = loadfile("/lib/filesystem.lua") + + -- Inject the package and io modules into the global namespace, as in Lua. + _G.package = package + _G.io = loadfile("/lib/io.lua")() + + --mark modules for delay loaded api + package.delayed["text"] = true + package.delayed["sh"] = true + package.delayed["transforms"] = true + package.delayed["term"] = true +end + +status("Initializing file system...") + +-- Mount the ROM and temporary file systems to allow working on the file +-- system module from this point on. +require("filesystem").mount(computer.getBootAddress(), "/") +package.preload={} + +status("Running boot scripts...") + +-- Run library startup scripts. These mostly initialize event handlers. +local function rom_invoke(method, ...) + return component.invoke(computer.getBootAddress(), method, ...) +end + +local scripts = {} +for _, file in ipairs(rom_invoke("list", "boot")) do + local path = "boot/" .. file + if not rom_invoke("isDirectory", path) then + table.insert(scripts, path) + end +end +table.sort(scripts) +for i = 1, #scripts do + dofile(scripts[i]) +end + +status("Initializing components...") + +for c, t in component.list() do + computer.pushSignal("component_added", c, t) +end + +status("Initializing system...") + +computer.pushSignal("init") -- so libs know components are initialized. +require("event").pull(1, "init") -- Allow init processing. +_G.runlevel = 1 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom-data.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom-data.lua index c7da72c91..f1980408a 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom-data.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom-data.lua @@ -1,15 +1,10 @@ local comp = require("component") -local text = require("text") +local devfs = ... -return -{ - open = function(mode) - if ({r=true, rb=true})[mode] then - return text.internal.reader(comp.eeprom.getData()) - end - return text.internal.writer(comp.eeprom.setData, ({a=true,ab=true})[mode] and comp.eeprom.getData()) - end, - size = function() - return string.len(comp.eeprom.getData()) - end -} +-- eeprom get/set has to delayed because comp.eeprom may not be available +local node = devfs.new_callback_proxy(function() return comp.eeprom.getData() end, function(...) comp.eeprom.setData(...) end) +function node.isAvailable() + return comp.list("eeprom", true)() +end + +return node diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom.lua index 4197b9881..fbf208f13 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/devfs/eeprom.lua @@ -1,15 +1,10 @@ local comp = require("component") -local text = require("text") +local devfs = ... -return -{ - open = function(mode) - if ({r=true, rb=true})[mode] then - return text.internal.reader(comp.eeprom.get()) - end - return text.internal.writer(comp.eeprom.set, ({a=true,ab=true})[mode] and comp.eeprom.get()) - end, - size = function() - return string.len(comp.eeprom.get()) - end -} +-- eeprom get/set has to delayed because comp.eeprom may not be available +local node = devfs.new_callback_proxy(function() return comp.eeprom.get() end, function(...) comp.eeprom.set(...) end) +function node.isAvailable() + return comp.list("eeprom", true)() +end + +return node diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full-ls.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full-ls.lua index c9cf2e51f..6142a4537 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full-ls.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/full-ls.lua @@ -162,7 +162,7 @@ end local function wide(n,i) local t = _isLink(n,i) and 'l' or _isDir(n,i) and 'd' or 'f' local link_target = _isLink(n,i) and - string.format(" -> %s",_linkPath(n,i)..(_isDir(n,i)and"/"or""))or"" + string.format(" -> %s", _linkPath(n, i):gsub("/+$", "") .. (_isDir(n, i) and "/" or "")) or "" local w = fs.get(_fullPath(n,i)).isReadOnly() and '-' or 'w' local size = formatSize(_size(n,i)) local modDate = formatDate(_time(n,i)) diff --git a/src/main/resources/assets/opencomputers/loot/oppm/.install b/src/main/resources/assets/opencomputers/loot/oppm/.install index be8d0d05c..3b017c71e 100644 --- a/src/main/resources/assets/opencomputers/loot/oppm/.install +++ b/src/main/resources/assets/opencomputers/loot/oppm/.install @@ -1 +1,3 @@ -os.execute(install.from.."/oppm.lua") +require("shell").setWorkingDirectory(install.from) +os.execute("oppm.lua") +