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:
payonel 2016-08-26 23:33:20 -07:00
parent 9dcc6070e0
commit 917befcd0e
16 changed files with 703 additions and 645 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -1 +1,3 @@
os.execute(install.from.."/oppm.lua")
require("shell").setWorkingDirectory(install.from)
os.execute("oppm.lua")