mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-14 09:46:53 -04:00
memory, sandbox, devfs, signals, log, rc, terminal, oppm
A big update final push before 1.6 release. Here is the detailed rundown of the changes /lib/term.lua 1. delay calling gpu.getViewport() when creating a terminal window. this fixes scenarios where the gpu could be a proxy object that doesn't have a viewport defined yet. 2. big blink cleanup to minimize the potential number of gpu calls during blink, and simplify the code /lib/sh.lua, /lib/process.lua 1. moving shell sandboxing code to process library. This actually simplifies creating the sandbox for processes and handling process crash (process lib and sh lib shared common crash code) /bin/rc.lua 1. found a bug in restart, fixed /lib/pipes.lua 1. required update to be compatible with internal(private) methods to the sh library /lib/package.lua 1. just aesthetic cleanup of error reporting /bin/mktmp.lua 1. use existing os.tmpname() helper method /init.lua and (added) /lib/tools/boot.lua 1. moving all the code I can to tools file to allow the memory to unload after boot completes. /bin/ls.lua 1. fixing symbolic links to directory display (had extra /) /lib/event.lua 1. refactor listeners and timers into common registers (fully backwards compatible). Provides a mechanism for drivers to know about ALL events without stealing them from the main process. Will document it later when the api is hardened (new api, event.register) 2. memory savings due to refactor 3. protecting computer.pullSignal! this is critical, user scripts that were previously pulling diirection from the computer api were able to put the kernel in a bad state. computer.pullSignal is still available, but it calls the event api to correctly dispatch events as drivers expect. devfs 1. eeprom and eeprom-data are now dynamically available. This is significant, now when a user removes the eeprom, it will not be listed in /dev 2. devfs upgrade, support built for future dynamic folders (such as /dev/filesystems/) autorun 1. code cleanup oppm 1. fixing cwd expected by oppm.lua during initial install (if using .install via `install oppm`)
This commit is contained in:
parent
9dcc6070e0
commit
917befcd0e
@ -1,12 +1,11 @@
|
|||||||
local fs = require("filesystem")
|
local fs = require("filesystem")
|
||||||
local uuid = require("uuid")
|
|
||||||
local shell = require("shell")
|
local shell = require("shell")
|
||||||
local sh = require("sh")
|
local sh = require("sh")
|
||||||
|
|
||||||
local touch = loadfile(shell.resolve("touch", "lua"))
|
local touch = loadfile(shell.resolve("touch", "lua"))
|
||||||
local mkdir = loadfile(shell.resolve("mkdir", "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"
|
local errorMessage = "missing tools for mktmp"
|
||||||
io.stderr:write(errorMessage .. '\n')
|
io.stderr:write(errorMessage .. '\n')
|
||||||
return false, errorMessage
|
return false, errorMessage
|
||||||
@ -57,24 +56,14 @@ if not fs.exists(prefix) then
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
local tmp = os.tmpname()
|
||||||
local tmp = prefix .. uuid.next()
|
local ok, reason = (directory and mkdir or touch)(tmp)
|
||||||
if not fs.exists(tmp) then
|
|
||||||
|
|
||||||
local ok, reason
|
if sh.internal.command_passed(ok) then
|
||||||
if directory then
|
|
||||||
ok, reason = mkdir(tmp)
|
|
||||||
else
|
|
||||||
ok, reason = touch(tmp)
|
|
||||||
end
|
|
||||||
|
|
||||||
if sh.internal.command_passed(ok) then
|
|
||||||
if verbose then
|
if verbose then
|
||||||
print(tmp)
|
print(tmp)
|
||||||
end
|
end
|
||||||
return tmp
|
return tmp
|
||||||
else
|
|
||||||
return ok, reason
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return ok, reason
|
||||||
|
@ -65,9 +65,10 @@ local function rawRunCommand(conf, name, cmd, args, ...)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then
|
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
|
if result then
|
||||||
result, what = xpcall(result["start"], debug.traceback, ...)
|
result, what = xpcall(daemon["start"], debug.traceback, ...)
|
||||||
if result then
|
if result then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -13,7 +13,7 @@ local function onInit()
|
|||||||
local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log")
|
local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log")
|
||||||
local log = io.open(path, "a")
|
local log = io.open(path, "a")
|
||||||
if log then
|
if log then
|
||||||
log:write(reason .. "\n")
|
log:write(tostring(result) .. ":" .. tostring(reason) .. "\n")
|
||||||
log:close()
|
log:close()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -34,15 +34,11 @@ local function onComponentAdded(_, address, componentType)
|
|||||||
name = fs.concat("/mnt", name)
|
name = fs.concat("/mnt", name)
|
||||||
fs.mount(proxy, name)
|
fs.mount(proxy, name)
|
||||||
if fs.isAutorunEnabled() then
|
if fs.isAutorunEnabled() then
|
||||||
local function run()
|
|
||||||
local file = shell.resolve(fs.concat(name, "autorun"), "lua") or
|
local file = shell.resolve(fs.concat(name, "autorun"), "lua") or
|
||||||
shell.resolve(fs.concat(name, ".autorun"), "lua")
|
shell.resolve(fs.concat(name, ".autorun"), "lua")
|
||||||
if file then
|
if file then
|
||||||
local result, reason = shell.execute(file, _ENV, proxy)
|
local run = function()
|
||||||
if not result then
|
assert(shell.execute(file, _ENV, proxy))
|
||||||
error(reason, 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
if isInitialized then
|
if isInitialized then
|
||||||
run()
|
run()
|
||||||
@ -52,6 +48,7 @@ local function onComponentAdded(_, address, componentType)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function onComponentRemoved(_, address, componentType)
|
local function onComponentRemoved(_, address, componentType)
|
||||||
|
@ -1,167 +1,21 @@
|
|||||||
do
|
do
|
||||||
_G._OSVERSION = "OpenOS 1.6"
|
local loadfile = load([[return function(file)
|
||||||
|
local handle, reason = invoke(addr, "open", file)
|
||||||
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)
|
|
||||||
if not handle then
|
if not handle then
|
||||||
error(reason)
|
error(reason)
|
||||||
end
|
end
|
||||||
local buffer = ""
|
local buffer = ""
|
||||||
repeat
|
repeat
|
||||||
local data, reason = rom.read(handle)
|
local data, reason = invoke(addr, "read", handle, math.huge)
|
||||||
if not data and reason then
|
if not data and reason then
|
||||||
error(reason)
|
error(reason)
|
||||||
end
|
end
|
||||||
buffer = buffer .. (data or "")
|
buffer = buffer .. (data or "")
|
||||||
until not data
|
until not data
|
||||||
rom.close(handle)
|
invoke(addr, "close", handle)
|
||||||
return load(buffer, "=" .. file)
|
return load(buffer, "=" .. file, "bt", _G)
|
||||||
end
|
end]], "=loadfile", "bt", {load=load,math=math,addr=computer.getBootAddress(), invoke=component.invoke})()
|
||||||
|
loadfile("/lib/tools/boot.lua")(loadfile)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
local fs = require("filesystem")
|
local fs = require("filesystem")
|
||||||
local comp = require("component")
|
local comp = require("component")
|
||||||
|
local text = require("text")
|
||||||
|
|
||||||
|
local sys = {} -- base class
|
||||||
|
|
||||||
local function new_node(parent, name, is_dir, proxy)
|
local function new_node(parent, name, is_dir, proxy)
|
||||||
local node = {parent=parent, name=name, is_dir=is_dir, proxy=proxy}
|
local node = {parent=parent, name=name, is_dir=is_dir, proxy=proxy}
|
||||||
@ -9,24 +12,30 @@ local function new_node(parent, name, is_dir, proxy)
|
|||||||
return node
|
return node
|
||||||
end
|
end
|
||||||
|
|
||||||
local function new_devfs_dir(name)
|
-- node may support isAvailable, and may choose to not be available
|
||||||
local sys = {}
|
local function isAvailable(node)
|
||||||
sys.mtab = new_node(nil, name or "/", true)
|
return node and not (node.proxy and node.proxy.isAvailable and not node.proxy.isAvailable())
|
||||||
|
end
|
||||||
|
|
||||||
-- returns: dir, point or path
|
-- returns: dir, point or path
|
||||||
-- node (table): the handler responsible for the path
|
-- node (table): the handler responsible for the path
|
||||||
-- this is essentially the device filesystem that is registered for the given path
|
-- this is essentially the device filesystem that is registered for the given path
|
||||||
-- point (string): the point name (like a file name)
|
-- point (string): the point name (like a file name)
|
||||||
function sys.findNode(path, create)
|
function sys:findNode(path, create)
|
||||||
checkArg(1, path, "string")
|
checkArg(1, path, "string")
|
||||||
local segments = fs.segments(path)
|
local segments = fs.segments(path)
|
||||||
local node = sys.mtab
|
local node = self.mtab
|
||||||
while #segments > 0 do
|
while #segments > 0 do
|
||||||
local name = table.remove(segments, 1)
|
local name = table.remove(segments, 1)
|
||||||
local prev_path = path
|
local prev_path = path
|
||||||
path = table.concat(segments, "/")
|
path = table.concat(segments, "/")
|
||||||
|
|
||||||
if not node.children[name] then
|
local next_node = node.children[name]
|
||||||
|
if not isAvailable(next_node) then
|
||||||
|
next_node = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if not next_node then
|
||||||
if not create then
|
if not create then
|
||||||
path = prev_path
|
path = prev_path
|
||||||
break
|
break
|
||||||
@ -46,10 +55,10 @@ local function new_devfs_dir(name)
|
|||||||
if path == "" or node.is_dir and node.proxy then
|
if path == "" or node.is_dir and node.proxy then
|
||||||
return node, path
|
return node, path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.invoke(method, path, ...)
|
function sys:invoke(method, path, ...)
|
||||||
local node, rest = sys.findNode(path)
|
local node, rest = self.findNode(path)
|
||||||
if not node or -- not found
|
if not node or -- not found
|
||||||
rest == "" and node.is_dir or -- path is dir
|
rest == "" and node.is_dir or -- path is dir
|
||||||
not node.proxy[method] then -- optional method
|
not node.proxy[method] then -- optional method
|
||||||
@ -57,18 +66,18 @@ local function new_devfs_dir(name)
|
|||||||
end
|
end
|
||||||
-- proxy could be a file, which doesn't take an argument, but it can be ignored if passed
|
-- proxy could be a file, which doesn't take an argument, but it can be ignored if passed
|
||||||
return node.proxy[method](rest)
|
return node.proxy[method](rest)
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.size(path)
|
function sys:size(path)
|
||||||
return sys.invoke("size", path)
|
return self.invoke("size", path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.lastModified(path)
|
function sys:lastModified(path)
|
||||||
return sys.invoke("lastModified", path)
|
return self.invoke("lastModified", path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.isDirectory(path)
|
function sys:isDirectory(path)
|
||||||
local node, rest = sys.findNode(path)
|
local node, rest = self.findNode(path)
|
||||||
if not node then
|
if not node then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@ -78,15 +87,15 @@ local function new_devfs_dir(name)
|
|||||||
elseif node.proxy then
|
elseif node.proxy then
|
||||||
return node.proxy.isDirectory(rest)
|
return node.proxy.isDirectory(rest)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.open(path, mode)
|
function sys:open(path, mode)
|
||||||
checkArg(1, path, "string")
|
checkArg(1, path, "string")
|
||||||
checkArg(2, mode, "string", "nil")
|
checkArg(2, mode, "string", "nil")
|
||||||
|
|
||||||
if not sys.exists(path) then
|
if not self.exists(path) then
|
||||||
return nil, path.." file not found"
|
return nil, path.." file not found"
|
||||||
elseif sys.isDirectory(path) then
|
elseif self.isDirectory(path) then
|
||||||
return nil, path.." is a directory"
|
return nil, path.." is a directory"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -98,7 +107,7 @@ local function new_devfs_dir(name)
|
|||||||
return nil, "invalid mode"
|
return nil, "invalid mode"
|
||||||
end
|
end
|
||||||
|
|
||||||
local node, rest = sys.findNode(path)
|
local node, rest = self.findNode(path)
|
||||||
-- there must be a node, else exists would have failed
|
-- there must be a node, else exists would have failed
|
||||||
|
|
||||||
local args = {}
|
local args = {}
|
||||||
@ -109,10 +118,10 @@ local function new_devfs_dir(name)
|
|||||||
args[#args+1] = mode
|
args[#args+1] = mode
|
||||||
|
|
||||||
return node.proxy.open(table.unpack(args))
|
return node.proxy.open(table.unpack(args))
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.list(path)
|
function sys:list(path)
|
||||||
local node, rest = sys.findNode(path)
|
local node, rest = self.findNode(path)
|
||||||
if not node or (rest ~= "" and not node.is_dir) then-- not found
|
if not node or (rest ~= "" and not node.is_dir) then-- not found
|
||||||
return {}
|
return {}
|
||||||
elseif rest == "" and not node.is_dir then -- path is file
|
elseif rest == "" and not node.is_dir then -- path is file
|
||||||
@ -124,13 +133,15 @@ local function new_devfs_dir(name)
|
|||||||
|
|
||||||
-- rest == "" and node.is_dir
|
-- rest == "" and node.is_dir
|
||||||
local keys = {}
|
local keys = {}
|
||||||
for k in pairs(node.children) do
|
for k,node in pairs(node.children) do
|
||||||
|
if isAvailable(node) then
|
||||||
table.insert(keys, k)
|
table.insert(keys, k)
|
||||||
end
|
end
|
||||||
return keys
|
|
||||||
end
|
end
|
||||||
|
return keys
|
||||||
|
end
|
||||||
|
|
||||||
function sys.remove(path)
|
function sys:remove(path)
|
||||||
return nil, "cannot remove devfs files or directories"
|
return nil, "cannot remove devfs files or directories"
|
||||||
--checkArg(1, path, "string")
|
--checkArg(1, path, "string")
|
||||||
|
|
||||||
@ -138,22 +149,22 @@ local function new_devfs_dir(name)
|
|||||||
-- return nil, "no such file or directory"
|
-- return nil, "no such file or directory"
|
||||||
--end
|
--end
|
||||||
|
|
||||||
--if not sys.exists(path) then
|
--if not self.exists(path) then
|
||||||
-- return nil, path.." file not found"
|
-- return nil, path.." file not found"
|
||||||
--end
|
--end
|
||||||
|
|
||||||
--local node, rest = sys.findNode(path)
|
--local node, rest = self.findNode(path)
|
||||||
|
|
||||||
--if rest ~= "" then -- if rest is not resolved, this isn't our path
|
--if rest ~= "" then -- if rest is not resolved, this isn't our path
|
||||||
-- return node.proxy.remove(rest)
|
-- return node.proxy.remove(rest)
|
||||||
--end
|
--end
|
||||||
|
|
||||||
--node.parent.children[node.name] = nil
|
--node.parent.children[node.name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.exists(path)
|
function sys:exists(path)
|
||||||
checkArg(1, path, "string")
|
checkArg(1, path, "string")
|
||||||
local node, rest = sys.findNode(path)
|
local node, rest = self.findNode(path)
|
||||||
|
|
||||||
if not node then
|
if not node then
|
||||||
return false
|
return false
|
||||||
@ -162,10 +173,10 @@ local function new_devfs_dir(name)
|
|||||||
else
|
else
|
||||||
return node.proxy.exists(rest)
|
return node.proxy.exists(rest)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function sys.create(path, handler)
|
function sys:create(path, handler)
|
||||||
if sys.exists(path) then
|
if self.exists(path) then
|
||||||
return nil, "path already exists"
|
return nil, "path already exists"
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -177,21 +188,55 @@ local function new_devfs_dir(name)
|
|||||||
return nil, "missing argument"
|
return nil, "missing argument"
|
||||||
end
|
end
|
||||||
|
|
||||||
local node, rest = sys.findNode(path, true)
|
local node, rest = self.findNode(path, true)
|
||||||
if rest ~= "" then
|
if rest ~= "" then
|
||||||
return node.proxy.create(rest, handler)
|
return node.proxy.create(rest, handler)
|
||||||
end
|
end
|
||||||
node.children[target] = new_node(node, target, not not handler.list, handler)
|
node.children[target] = new_node(node, target, not not handler.list, handler)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
return sys
|
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
|
end
|
||||||
|
|
||||||
local devfs = new_devfs_dir()
|
local devfs = new_devfs_dir()
|
||||||
|
|
||||||
local bfd = "bad file descriptor"
|
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)
|
function devfs.setLabel(value)
|
||||||
error("drive does not support labeling")
|
error("drive does not support labeling")
|
||||||
end
|
end
|
||||||
@ -268,12 +313,15 @@ end
|
|||||||
-- /dev is a special handler
|
-- /dev is a special handler
|
||||||
|
|
||||||
local function devfs_load(key)
|
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
|
end
|
||||||
|
|
||||||
devfs.create("null", devfs_load("null"))
|
devfs_load("random")
|
||||||
devfs.create("random", devfs_load("random"))
|
devfs_load("null")
|
||||||
devfs.create("eeprom", devfs_load("eeprom"))
|
devfs_load("eeprom")
|
||||||
devfs.create("eeprom-data", devfs_load("eeprom-data"))
|
devfs_load("eeprom-data")
|
||||||
|
--devfs_load("filesystems")
|
||||||
|
|
||||||
return devfs
|
return devfs
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
local computer = require("computer")
|
local computer = require("computer")
|
||||||
local keyboard = require("keyboard")
|
local keyboard = require("keyboard")
|
||||||
|
|
||||||
local event, listeners, timers = {}, {}, {}
|
local event = {}
|
||||||
|
local handlers = {}
|
||||||
local lastInterrupt = -math.huge
|
local lastInterrupt = -math.huge
|
||||||
|
|
||||||
|
event.handlers = handlers
|
||||||
|
|
||||||
local function call(callback, ...)
|
local function call(callback, ...)
|
||||||
local result, message = pcall(callback, ...)
|
local result, message = pcall(callback, ...)
|
||||||
if not result and type(event.onError) == "function" then
|
if not result and type(event.onError) == "function" then
|
||||||
@ -13,42 +16,78 @@ local function call(callback, ...)
|
|||||||
return message
|
return message
|
||||||
end
|
end
|
||||||
|
|
||||||
local function dispatch(signal, ...)
|
function event.register(key, callback, interval, times)
|
||||||
if listeners[signal] then
|
local handler =
|
||||||
local function callbacks()
|
{
|
||||||
local list = {}
|
key = key,
|
||||||
for index, listener in ipairs(listeners[signal]) do
|
times = times or 0,
|
||||||
list[index] = listener
|
callback = callback,
|
||||||
|
interval = interval or math.huge,
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.timeout = computer.uptime() + handler.interval
|
||||||
|
|
||||||
|
if not interval then
|
||||||
|
handler.times = math.huge
|
||||||
end
|
end
|
||||||
return list
|
|
||||||
end
|
local id = 0
|
||||||
for _, callback in ipairs(callbacks()) do
|
repeat
|
||||||
if call(callback, signal, ...) == false then
|
id = id + 1
|
||||||
event.ignore(signal, callback) -- alternative method of removing a listener
|
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
|
end
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
local function tick()
|
local _pullSignal = computer.pullSignal
|
||||||
local function elapsed()
|
computer.pullSignal = function(...)
|
||||||
local list = {}
|
return (function(...)
|
||||||
for id, timer in pairs(timers) do
|
dispatch(...)
|
||||||
if timer.after <= computer.uptime() then
|
return ...
|
||||||
table.insert(list, timer.callback)
|
end)(_pullSignal(...))
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createPlainFilter(name, ...)
|
local function createPlainFilter(name, ...)
|
||||||
@ -94,8 +133,8 @@ end
|
|||||||
|
|
||||||
function event.cancel(timerId)
|
function event.cancel(timerId)
|
||||||
checkArg(1, timerId, "number")
|
checkArg(1, timerId, "number")
|
||||||
if timers[timerId] then
|
if handlers[timerId] then
|
||||||
timers[timerId] = nil
|
handlers[timerId] = nil
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
@ -104,34 +143,24 @@ end
|
|||||||
function event.ignore(name, callback)
|
function event.ignore(name, callback)
|
||||||
checkArg(1, name, "string")
|
checkArg(1, name, "string")
|
||||||
checkArg(2, callback, "function")
|
checkArg(2, callback, "function")
|
||||||
if listeners[name] then
|
for id, handler in pairs(handlers) do
|
||||||
for i = 1, #listeners[name] do
|
if handler.key == name and handler.callback == callback then
|
||||||
if listeners[name][i] == callback then
|
handlers[id] = nil
|
||||||
table.remove(listeners[name], i)
|
|
||||||
if #listeners[name] == 0 then
|
|
||||||
listeners[name] = nil
|
|
||||||
end
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function event.listen(name, callback)
|
function event.listen(name, callback)
|
||||||
checkArg(1, name, "string")
|
checkArg(1, name, "string")
|
||||||
checkArg(2, callback, "function")
|
checkArg(2, callback, "function")
|
||||||
if listeners[name] then
|
for id, handler in pairs(handlers) do
|
||||||
for i = 1, #listeners[name] do
|
if handler.key == name and handler.callback == callback then
|
||||||
if listeners[name][i] == callback then
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
return event.register(name, callback, nil, nil)
|
||||||
listeners[name] = {}
|
|
||||||
end
|
|
||||||
table.insert(listeners[name], callback)
|
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function event.onError(message)
|
function event.onError(message)
|
||||||
@ -169,7 +198,6 @@ function event.pullMultiple(...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
|
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function event.pullFiltered(...)
|
function event.pullFiltered(...)
|
||||||
@ -187,15 +215,8 @@ function event.pullFiltered(...)
|
|||||||
|
|
||||||
local deadline = seconds and (computer.uptime() + seconds) or math.huge
|
local deadline = seconds and (computer.uptime() + seconds) or math.huge
|
||||||
repeat
|
repeat
|
||||||
local closest = deadline
|
local closest = math.min(deadline, time_to_nearest())
|
||||||
for _, timer in pairs(timers) do
|
|
||||||
closest = math.min(closest, timer.after)
|
|
||||||
end
|
|
||||||
local signal = table.pack(computer.pullSignal(closest - computer.uptime()))
|
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
|
if event.shouldInterrupt() then
|
||||||
lastInterrupt = computer.uptime()
|
lastInterrupt = computer.uptime()
|
||||||
error("interrupted", 0)
|
error("interrupted", 0)
|
||||||
@ -230,20 +251,12 @@ function event.timer(interval, callback, times)
|
|||||||
checkArg(1, interval, "number")
|
checkArg(1, interval, "number")
|
||||||
checkArg(2, callback, "function")
|
checkArg(2, callback, "function")
|
||||||
checkArg(3, times, "number", "nil")
|
checkArg(3, times, "number", "nil")
|
||||||
local id
|
return event.register(false, callback, interval, times)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- users may expect to find event.push to exist
|
-- users may expect to find event.push to exist
|
||||||
event.push = computer.pushSignal
|
event.push = computer.pushSignal
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
return event
|
return event
|
||||||
|
@ -137,7 +137,7 @@ function require(module)
|
|||||||
error(table.concat(errorMsg, "\n"), 2)
|
error(table.concat(errorMsg, "\n"), 2)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
error("already loading: " .. module .. debug.traceback(), 2)
|
error("already loading: " .. module .. "\n" .. debug.traceback(), 2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -126,13 +126,13 @@ function plib.internal.redirectRead(pm)
|
|||||||
return reader
|
return reader
|
||||||
end
|
end
|
||||||
|
|
||||||
function plib.internal.create(fp)
|
function plib.internal.create(fp, init, name)
|
||||||
local _co = process.info().data.coroutine_handler
|
local _co = process.info().data.coroutine_handler
|
||||||
|
|
||||||
local pco = setmetatable(
|
local pco = setmetatable(
|
||||||
{
|
{
|
||||||
stack = {},
|
stack = {},
|
||||||
next = nil,
|
next = false,
|
||||||
create = _co.create,
|
create = _co.create,
|
||||||
wrap = _co.wrap,
|
wrap = _co.wrap,
|
||||||
previous_handler = _co
|
previous_handler = _co
|
||||||
@ -161,12 +161,12 @@ function plib.internal.create(fp)
|
|||||||
return _co.yield(...)
|
return _co.yield(...)
|
||||||
end
|
end
|
||||||
function pco.set_unwind(from)
|
function pco.set_unwind(from)
|
||||||
pco.next = nil
|
pco.next = false
|
||||||
if from then
|
if from then
|
||||||
local index = pco.index_of(from)
|
local index = pco.index_of(from)
|
||||||
if index then
|
if index then
|
||||||
pco.stack = tx.sub(pco.stack, 1, index-1)
|
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
|
end
|
||||||
end
|
end
|
||||||
@ -212,7 +212,7 @@ function plib.internal.create(fp)
|
|||||||
return true, _co.yield(...) -- pass args to resume next
|
return true, _co.yield(...) -- pass args to resume next
|
||||||
else
|
else
|
||||||
-- the stack is not running
|
-- the stack is not running
|
||||||
pco.next = nil
|
pco.next = false
|
||||||
local yield_args = table.pack(_co.resume(thread, ...))
|
local yield_args = table.pack(_co.resume(thread, ...))
|
||||||
if #pco.stack > 0 then
|
if #pco.stack > 0 then
|
||||||
-- thread may have crashed (crash unwinds as well)
|
-- 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
|
-- in such a case, yield out first, then resume where we left off
|
||||||
if pco.next and pco.next ~= thread then
|
if pco.next and pco.next ~= thread then
|
||||||
local next = pco.next
|
local next = pco.next
|
||||||
pco.next = nil
|
pco.next = false
|
||||||
return pco.resume(next, table.unpack(yield_args,2,yield_args.n))
|
return pco.resume(next, table.unpack(yield_args,2,yield_args.n))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -251,7 +251,7 @@ function plib.internal.create(fp)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if fp then
|
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
|
process.info(pco.stack[1]).data.coroutine_handler = pco
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -329,10 +329,8 @@ function pipeManager.new(prog, mode, env)
|
|||||||
pm.commands[pm.self_id] = {pm.handler, {}}
|
pm.commands[pm.self_id] = {pm.handler, {}}
|
||||||
|
|
||||||
pm.root = function()
|
pm.root = function()
|
||||||
local startup_args = {}
|
|
||||||
|
|
||||||
local reason
|
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
|
if not pm.threads then
|
||||||
pm.dead = true
|
pm.dead = true
|
||||||
@ -340,7 +338,6 @@ function pipeManager.new(prog, mode, env)
|
|||||||
end
|
end
|
||||||
|
|
||||||
pm.pipe = process.info(pm.threads[1]).data.io[1]
|
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 we are the writer, we need args to resume prog
|
||||||
if pm.mode == "w" then
|
if pm.mode == "w" then
|
||||||
|
@ -20,6 +20,12 @@ function process.findProcess(co)
|
|||||||
end
|
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)
|
function process.load(path, env, init, name)
|
||||||
checkArg(1, path, "string", "function")
|
checkArg(1, path, "string", "function")
|
||||||
@ -28,6 +34,7 @@ function process.load(path, env, init, name)
|
|||||||
checkArg(4, name, "string", "nil")
|
checkArg(4, name, "string", "nil")
|
||||||
|
|
||||||
assert(type(path) == "string" or env == nil, "process cannot load function environemnts")
|
assert(type(path) == "string" or env == nil, "process cannot load function environemnts")
|
||||||
|
name = name or ""
|
||||||
|
|
||||||
local p = process.findProcess()
|
local p = process.findProcess()
|
||||||
if p then
|
if p then
|
||||||
@ -37,31 +44,42 @@ function process.load(path, env, init, name)
|
|||||||
|
|
||||||
local code = nil
|
local code = nil
|
||||||
if type(path) == 'string' then
|
if type(path) == 'string' then
|
||||||
local f, reason = io.open(path)
|
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
|
||||||
|
os.setenv("_", program)
|
||||||
|
local f, reason = io.open(program)
|
||||||
if not f then
|
if not f then
|
||||||
return nil, reason
|
io.stderr:write("could not read '" .. program .. "': " .. tostring(reason) .. "\n")
|
||||||
|
return 1
|
||||||
end
|
end
|
||||||
local reason
|
local shabang = f:read(2)
|
||||||
if f:read(2) == "#!" then
|
|
||||||
local command = f:read()
|
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
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
code, reason = loadfile(path, "t", env)
|
|
||||||
end
|
|
||||||
f:close()
|
f:close()
|
||||||
if not code then
|
if shabang == "#!" then
|
||||||
return nil, reason
|
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
|
end
|
||||||
else -- path is code
|
else -- path is code
|
||||||
code = path
|
code = path
|
||||||
@ -69,43 +87,46 @@ function process.load(path, env, init, name)
|
|||||||
|
|
||||||
local thread = nil
|
local thread = nil
|
||||||
thread = coroutine.create(function(...)
|
thread = coroutine.create(function(...)
|
||||||
if init then
|
|
||||||
init()
|
|
||||||
end
|
|
||||||
-- pcall code so that we can remove it from the process list on exit
|
-- pcall code so that we can remove it from the process list on exit
|
||||||
local result =
|
local result =
|
||||||
{
|
{
|
||||||
xpcall(code, function(msg)
|
xpcall(function(...)
|
||||||
if type(msg) == 'table' then return msg end
|
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')
|
local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1')
|
||||||
return string.format('%s:\n%s', msg or '', stack)
|
io.stderr:write(string.format('%s:\n%s', msg or '', stack))
|
||||||
|
return 128 -- syserr
|
||||||
end, ...)
|
end, ...)
|
||||||
}
|
}
|
||||||
process.internal.close(thread)
|
process.internal.close(thread)
|
||||||
if not result[1] then
|
--result[1] is false if the exception handler also crashed
|
||||||
-- msg can be a custom error object
|
if not result[1] and type(result[2]) ~= "number" then
|
||||||
local msg = result[2]
|
require("event").onError(string.format("process library exception handler crashed: %s", tostring(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
|
end
|
||||||
end
|
return select(2, table.unpack(result))
|
||||||
return select(2,table.unpack(result))
|
end, true)
|
||||||
end,true)
|
|
||||||
process.list[thread] = {
|
process.list[thread] = {
|
||||||
path = path,
|
path = path,
|
||||||
command = name,
|
command = name,
|
||||||
env = env,
|
env = env,
|
||||||
data = setmetatable(
|
data = parent_data(
|
||||||
{
|
{
|
||||||
handles = {},
|
handles = {},
|
||||||
io = setmetatable({}, {__index=p and p.data and p.data.io or nil}),
|
io = parent_data({}, p, "data", "io"),
|
||||||
coroutine_handler = setmetatable({}, {__index=p and p.data and p.data.coroutine_handler or nil}),
|
coroutine_handler = parent_data({}, p, "data", "coroutine_handler"),
|
||||||
}, {__index=p and p.data or nil}),
|
}, p, "data"),
|
||||||
parent = p,
|
parent = p,
|
||||||
instances = setmetatable({}, {__mode="v"})
|
instances = setmetatable({}, {__mode="v"}),
|
||||||
}
|
}
|
||||||
return thread
|
return thread
|
||||||
end
|
end
|
||||||
|
@ -23,7 +23,6 @@ local local_env = {event=event,fs=fs,process=process,shell=shell,term=term,text=
|
|||||||
sh.internal.globbers = {{"*",".*"},{"?","."}}
|
sh.internal.globbers = {{"*",".*"},{"?","."}}
|
||||||
sh.internal.ec = {}
|
sh.internal.ec = {}
|
||||||
sh.internal.ec.parseCommand = 127
|
sh.internal.ec.parseCommand = 127
|
||||||
sh.internal.ec.sysError = 128
|
|
||||||
sh.internal.ec.last = 0
|
sh.internal.ec.last = 0
|
||||||
|
|
||||||
function sh.getLastExitCode()
|
function sh.getLastExitCode()
|
||||||
@ -211,26 +210,20 @@ function sh.internal.parseCommand(words)
|
|||||||
if #words == 0 then
|
if #words == 0 then
|
||||||
return nil
|
return nil
|
||||||
end
|
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 i=1,#words do
|
||||||
for _, arg in ipairs(sh.internal.evaluate(words[i])) 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
|
||||||
end
|
end
|
||||||
local eword = evaluated_words[1]
|
return table.remove(ewords, 1), ewords, has_tokens
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function sh.internal.createThreads(commands, eargs, env)
|
function sh.internal.createThreads(commands, env, start_args)
|
||||||
-- Piping data between programs works like so:
|
-- Piping data between programs works like so:
|
||||||
-- program1 gets its output replaced with our custom stream.
|
-- program1 gets its output replaced with our custom stream.
|
||||||
-- program2 gets its input 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.
|
-- custom stream may have "redirect" entries for fallback/duplication.
|
||||||
local threads = {}
|
local threads = {}
|
||||||
for i = 1, #commands do
|
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 name, thread = tostring(program)
|
||||||
local thread_env = type(program) == "string" and env or nil
|
local thread_env = type(program) == "string" and env or nil
|
||||||
local thread, reason = process.load(program, thread_env, function()
|
local thread, reason = process.load(program, thread_env, function(...)
|
||||||
os.setenv("_", name)
|
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
|
-- popen expects each process to first write an empty string
|
||||||
-- this is required for proper thread order
|
-- this is required for proper thread order
|
||||||
io.write('')
|
io.write("")
|
||||||
|
return table.unpack(args, 1, args.n or #args)
|
||||||
end, name)
|
end, name)
|
||||||
|
|
||||||
threads[i] = thread
|
threads[i] = thread
|
||||||
@ -259,25 +261,20 @@ function sh.internal.createThreads(commands, eargs, env)
|
|||||||
return nil, reason
|
return nil, reason
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
if #threads > 1 then
|
if #threads > 1 then
|
||||||
sh.internal.buildPipeChain(threads)
|
sh.internal.buildPipeChain(threads)
|
||||||
end
|
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
|
return threads
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -285,25 +282,19 @@ function sh.internal.runThreads(threads)
|
|||||||
local result = {}
|
local result = {}
|
||||||
for i = 1, #threads do
|
for i = 1, #threads do
|
||||||
-- Emulate CC behavior by making yields a filtered event.pull()
|
-- 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
|
while coroutine.status(thread) ~= "dead" do
|
||||||
args = args or process.info(thread).data.args
|
|
||||||
result = table.pack(coroutine.resume(thread, table.unpack(args)))
|
result = table.pack(coroutine.resume(thread, table.unpack(args)))
|
||||||
if coroutine.status(thread) ~= "dead" then
|
if coroutine.status(thread) ~= "dead" then
|
||||||
args = sh.internal.handleThreadYield(result)
|
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
|
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
|
end
|
||||||
end
|
end
|
||||||
if not result[1] then
|
|
||||||
sh.internal.handleThreadCrash(thread, result)
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
return result[2]
|
||||||
return table.unpack(result)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function sh.internal.executePipes(pipe_parts, eargs, env)
|
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
|
return sh.internal.ec.parseCommand
|
||||||
end
|
end
|
||||||
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
|
if not threads then
|
||||||
io.stderr:write(reason,"\n")
|
io.stderr:write(reason,"\n")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
local result, cmd_result = sh.internal.runThreads(threads)
|
return 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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function sh.execute(env, command, ...)
|
function sh.execute(env, command, ...)
|
||||||
@ -347,7 +327,8 @@ function sh.execute(env, command, ...)
|
|||||||
return true, 0
|
return true, 0
|
||||||
end
|
end
|
||||||
|
|
||||||
local eargs = {...}
|
-- MUST be table.pack for non contiguous ...
|
||||||
|
local eargs = table.pack(...)
|
||||||
|
|
||||||
-- simple
|
-- simple
|
||||||
if reason then
|
if reason then
|
||||||
@ -358,6 +339,15 @@ function sh.execute(env, command, ...)
|
|||||||
return sh.internal.execute_complex(statements, eargs, env)
|
return sh.internal.execute_complex(statements, eargs, env)
|
||||||
end
|
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)
|
function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result)
|
||||||
local action = result[2]
|
local action = result[2]
|
||||||
if action == nil or type(action) == "number" then
|
if action == nil or type(action) == "number" then
|
||||||
@ -367,20 +357,7 @@ function --[[@delayloaded-start@]] sh.internal.handleThreadYield(result)
|
|||||||
end
|
end
|
||||||
end --[[@delayloaded-end@]]
|
end --[[@delayloaded-end@]]
|
||||||
|
|
||||||
function --[[@delayloaded-start@]] sh.internal.handleThreadCrash(thread, result)
|
function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(args, thread)
|
||||||
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)
|
|
||||||
local data = process.info(thread).data
|
local data = process.info(thread).data
|
||||||
local tokens, ios, handles = args, data.io, data.handles
|
local tokens, ios, handles = args, data.io, data.handles
|
||||||
args = {}
|
args = {}
|
||||||
@ -406,7 +383,7 @@ function --[[@delayloaded-start@]] sh.internal.buildCommandRedirects(thread, arg
|
|||||||
else
|
else
|
||||||
local file, reason = io.open(shell.resolve(token), mode)
|
local file, reason = io.open(shell.resolve(token), mode)
|
||||||
if not file then
|
if not file then
|
||||||
return nil, "could not open '" .. token .. "': " .. reason
|
error("could not open '" .. token .. "': " .. reason)
|
||||||
end
|
end
|
||||||
table.insert(handles, file)
|
table.insert(handles, file)
|
||||||
ios[from_io] = file
|
ios[from_io] = file
|
||||||
@ -447,7 +424,6 @@ function --[[@delayloaded-start@]] sh.internal.buildPipeChain(threads)
|
|||||||
|
|
||||||
prev_pipe = pipe
|
prev_pipe = pipe
|
||||||
end
|
end
|
||||||
|
|
||||||
end --[[@delayloaded-end@]]
|
end --[[@delayloaded-end@]]
|
||||||
|
|
||||||
function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern)
|
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
|
-- if that is the case, it is important to leave stream.buffer alone
|
||||||
return self.redirect[0]:read(n)
|
return self.redirect[0]:read(n)
|
||||||
elseif self.buffer == "" then
|
elseif self.buffer == "" then
|
||||||
process.info(self.next).data.args = table.pack(coroutine.yield(table.unpack(self.result)))
|
coroutine.yield()
|
||||||
end
|
end
|
||||||
local result = string.sub(self.buffer, 1, n)
|
local result = string.sub(self.buffer, 1, n)
|
||||||
self.buffer = string.sub(self.buffer, n + 1)
|
self.buffer = string.sub(self.buffer, n + 1)
|
||||||
@ -842,15 +818,13 @@ function --[[@delayloaded-start@]] sh.internal.newMemoryStream()
|
|||||||
return self.redirect[1]:write(value)
|
return self.redirect[1]:write(value)
|
||||||
elseif not self.closed then
|
elseif not self.closed then
|
||||||
self.buffer = self.buffer .. value
|
self.buffer = self.buffer .. value
|
||||||
local args = process.info(self.next).data.args
|
local result = table.pack(coroutine.resume(self.next))
|
||||||
self.result = table.pack(coroutine.resume(self.next, table.unpack(args)))
|
|
||||||
if coroutine.status(self.next) == "dead" then
|
if coroutine.status(self.next) == "dead" then
|
||||||
self:close()
|
self:close()
|
||||||
end
|
end
|
||||||
if not self.result[1] then
|
if not result[1] then
|
||||||
error(self.result[2], 0)
|
error(result[2], 0)
|
||||||
end
|
end
|
||||||
table.remove(self.result, 1)
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
os.exit(0) -- abort the current process: SIGPIPE
|
os.exit(0) -- abort the current process: SIGPIPE
|
||||||
|
@ -3,6 +3,7 @@ local event = require("event")
|
|||||||
local process = require("process")
|
local process = require("process")
|
||||||
local kb = require("keyboard")
|
local kb = require("keyboard")
|
||||||
local component = require("component")
|
local component = require("component")
|
||||||
|
local computer = require("computer")
|
||||||
local keys = kb.keys
|
local keys = kb.keys
|
||||||
|
|
||||||
local term = {}
|
local term = {}
|
||||||
@ -35,8 +36,11 @@ end
|
|||||||
function term.setViewport(w,h,dx,dy,x,y,window)
|
function term.setViewport(w,h,dx,dy,x,y,window)
|
||||||
window = window or W()
|
window = window or W()
|
||||||
|
|
||||||
|
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()
|
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
|
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=
|
window.w,window.h,window.dx,window.dy,window.x,window.y,window.gw,window.gh=
|
||||||
w,h,dx,dy,x,y, gw, 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())
|
return w and not not (w.gpu and w.gpu.getScreen())
|
||||||
end
|
end
|
||||||
|
|
||||||
function term.internal.pull(input, c, off, t, ...)
|
function term.internal.pull(input, timeout, ...)
|
||||||
t=t or math.huge
|
timeout = timeout or math.huge
|
||||||
if t < 0 then return end
|
|
||||||
local w,unpack=W(),table.unpack
|
local w = W()
|
||||||
local d,h,dx,dy,x,y=term.getViewport(w)
|
local d, h, dx, dy, x, y = term.getViewport(w)
|
||||||
local out = (x<1 or x>d or y<1 or y>h)
|
local out = (x<1 or x>d or y<1 or y>h)
|
||||||
|
|
||||||
if input and out then
|
if input and out then
|
||||||
input:move(0)
|
input:move(0)
|
||||||
y=w.y
|
y = w.y
|
||||||
input:scroll()
|
input:scroll()
|
||||||
end
|
end
|
||||||
x,y=w.x+dx,w.y+dy
|
|
||||||
local gpu
|
|
||||||
|
|
||||||
if input or not out then
|
x, y = w.x + dx, w.y + dy
|
||||||
gpu=w.gpu
|
local gpu = (input or not out) and w.gpu
|
||||||
local sf,sb=gpu.setForeground,gpu.setBackground
|
|
||||||
c=c or {{gpu.getBackground()},{gpu.getForeground()},gpu.get(x,y)}
|
local bgColor, bgIsPalette
|
||||||
local c11,c12 = unpack(c[1])
|
local fgColor, fgIsPalette
|
||||||
local c21,c22 = unpack(c[2])
|
local char_at_cursor
|
||||||
-- c can fail if gpu does not have a screen
|
local blinking
|
||||||
-- if can happen during a type of race condition when a screen is removed
|
if gpu then
|
||||||
if not c11 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"
|
return nil, "interrupted"
|
||||||
end
|
end
|
||||||
if not off then
|
|
||||||
sf(c11,c12)
|
fgColor, fgIsPalette = gpu.getForeground()
|
||||||
sb(c21,c22)
|
char_at_cursor = gpu.get(x, y)
|
||||||
|
|
||||||
|
blinking = w.blink
|
||||||
|
if input then
|
||||||
|
blinking = input.blink
|
||||||
end
|
end
|
||||||
gpu.set(x,y,c[3])
|
|
||||||
sb(c11,c12)
|
|
||||||
sf(c21,c22)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local a={event.pull(math.min(t,0.5),...)}
|
-- get the next event
|
||||||
|
local blinked = false
|
||||||
if #a>1 or t<.5 then
|
local done = false
|
||||||
|
local signal
|
||||||
|
while true do
|
||||||
if gpu then
|
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)
|
|
||||||
end
|
end
|
||||||
local blinking = w.blink
|
|
||||||
if input then blinking = input.blink end
|
if done then
|
||||||
return term.internal.pull(input,c,blinking and not off,t-0.5,...)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
function term.pull(p,...)
|
function term.pull(...)
|
||||||
local a,t = {p,...}
|
local args = table.pack(...)
|
||||||
if type(p) == "number" then t = table.remove(a,1) end
|
local timeout = nil
|
||||||
return term.internal.pull(nil,nil,nil,t,table.unpack(a))
|
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
|
end
|
||||||
|
|
||||||
function term.read(history,dobreak,hintHandler,pwchar,filter)
|
function term.read(history,dobreak,hintHandler,pwchar,filter)
|
||||||
|
@ -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
|
@ -1,15 +1,10 @@
|
|||||||
local comp = require("component")
|
local comp = require("component")
|
||||||
local text = require("text")
|
local devfs = ...
|
||||||
|
|
||||||
return
|
-- 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)
|
||||||
open = function(mode)
|
function node.isAvailable()
|
||||||
if ({r=true, rb=true})[mode] then
|
return comp.list("eeprom", true)()
|
||||||
return text.internal.reader(comp.eeprom.getData())
|
end
|
||||||
end
|
|
||||||
return text.internal.writer(comp.eeprom.setData, ({a=true,ab=true})[mode] and comp.eeprom.getData())
|
return node
|
||||||
end,
|
|
||||||
size = function()
|
|
||||||
return string.len(comp.eeprom.getData())
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
local comp = require("component")
|
local comp = require("component")
|
||||||
local text = require("text")
|
local devfs = ...
|
||||||
|
|
||||||
return
|
-- 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)
|
||||||
open = function(mode)
|
function node.isAvailable()
|
||||||
if ({r=true, rb=true})[mode] then
|
return comp.list("eeprom", true)()
|
||||||
return text.internal.reader(comp.eeprom.get())
|
end
|
||||||
end
|
|
||||||
return text.internal.writer(comp.eeprom.set, ({a=true,ab=true})[mode] and comp.eeprom.get())
|
return node
|
||||||
end,
|
|
||||||
size = function()
|
|
||||||
return string.len(comp.eeprom.get())
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
@ -162,7 +162,7 @@ end
|
|||||||
local function wide(n,i)
|
local function wide(n,i)
|
||||||
local t = _isLink(n,i) and 'l' or _isDir(n,i) and 'd' or 'f'
|
local t = _isLink(n,i) and 'l' or _isDir(n,i) and 'd' or 'f'
|
||||||
local link_target = _isLink(n,i) and
|
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 w = fs.get(_fullPath(n,i)).isReadOnly() and '-' or 'w'
|
||||||
local size = formatSize(_size(n,i))
|
local size = formatSize(_size(n,i))
|
||||||
local modDate = formatDate(_time(n,i))
|
local modDate = formatDate(_time(n,i))
|
||||||
|
@ -1 +1,3 @@
|
|||||||
os.execute(install.from.."/oppm.lua")
|
require("shell").setWorkingDirectory(install.from)
|
||||||
|
os.execute("oppm.lua")
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user