mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-15 18:30:27 -04:00
improvements to guid, devfs, cat, cp, and system error clarity
renaming guid to uuid and removing useless method toHex adding text read and writer, compare to c++ stdlib stringstream. needed for advanced devfs fix globbing with relative paths and . and .. in path moving install confirmation before calling .install fix cp: cp -r . /another/path (should copy contents of current dir) fix cat when stdin is missing or closed improved devfs supporting custom dir handlers for dynamic file listing other non functional changes to clean up code and improved error messages
This commit is contained in:
parent
6837c7e02f
commit
e2331b67ff
@ -13,9 +13,14 @@ for i = 1, #args do
|
||||
io.stderr:write(string.format('cat %s: Is a directory\n', arg))
|
||||
ec = 1
|
||||
else
|
||||
local file, reason = args[i] == "-" and io.stdin or io.open(shell.resolve(args[i]))
|
||||
local file, reason
|
||||
if args[i] == "-" then
|
||||
file, reason = io.stdin, "missing stdin"
|
||||
else
|
||||
file, reason = io.open(shell.resolve(args[i]))
|
||||
end
|
||||
if not file then
|
||||
io.stderr:write(string.format("cat: %s: %s\n",args[i],tostring(reason)))
|
||||
io.stderr:write(string.format("cat: %s: %s\n", args[i], tostring(reason)))
|
||||
ec = 1
|
||||
else
|
||||
repeat
|
||||
|
@ -148,12 +148,14 @@ end
|
||||
local to = shell.resolve(args[#args])
|
||||
|
||||
for i = 1, #args - 1 do
|
||||
local fromPath, cuts = args[i]:gsub("(/%.%.?)$", "%1")
|
||||
fromPath = shell.resolve(fromPath)
|
||||
local arg = args[i]
|
||||
local fromPath = shell.resolve(arg)
|
||||
-- a "contents of" copy is where src path ends in . or ..
|
||||
-- a source path ending with . is not sufficient - could be the source filename
|
||||
local contents_of = arg:match("%.$") and not fromPath:match("%.$")
|
||||
local toPath = to
|
||||
-- fromPath ending with /. indicates copying the contents of fromPath
|
||||
-- in which case (cuts>0) we do not append fromPath name to toPath
|
||||
if cuts == 0 and fs.isDirectory(toPath) then
|
||||
-- we do not append fromPath name to toPath in case of contents_of copy
|
||||
if not contents_of and fs.isDirectory(toPath) then
|
||||
toPath = fs.concat(toPath, fs.name(fromPath))
|
||||
end
|
||||
result, reason = recurse(fromPath, toPath)
|
||||
|
@ -19,7 +19,7 @@ for i = 1, #args do
|
||||
reason = "unknown reason"
|
||||
end
|
||||
end
|
||||
io.stderr:write(path .. ": " .. reason .. "\n")
|
||||
io.stderr:write("mkdir: cannot create directory '" .. tostring(args[i]) .. "': " .. reason .. "\n")
|
||||
ec = 1
|
||||
end
|
||||
end
|
||||
|
@ -1,12 +1,12 @@
|
||||
local fs = require("filesystem")
|
||||
local guid = require("guid")
|
||||
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 guid or not touch then
|
||||
if not uuid or not touch then
|
||||
local errorMessage = "missing tools for mktmp"
|
||||
io.stderr:write(errorMessage .. '\n')
|
||||
return false, errorMessage
|
||||
@ -58,7 +58,7 @@ if not fs.exists(prefix) then
|
||||
end
|
||||
|
||||
while true do
|
||||
local tmp = prefix .. guid.next()
|
||||
local tmp = prefix .. uuid.next()
|
||||
if not fs.exists(tmp) then
|
||||
|
||||
local ok, reason
|
||||
|
@ -1,6 +1,7 @@
|
||||
require("filesystem").mount(
|
||||
setmetatable({
|
||||
isReadOnly = function()return false end
|
||||
isReadOnly = function()return false end,
|
||||
address = require("uuid").next()
|
||||
},
|
||||
{
|
||||
__index=function(tbl,key)return require("devfs")[key]end
|
||||
|
@ -1,111 +1,292 @@
|
||||
local fs = require("filesystem")
|
||||
local comp = require("component")
|
||||
|
||||
local proxy = {points={},address=require("guid").next()}
|
||||
local function new_node(parent, name, is_dir, proxy)
|
||||
local node = {parent=parent, name=name, is_dir=is_dir, proxy=proxy}
|
||||
if not proxy then
|
||||
node.children = {}
|
||||
end
|
||||
return node
|
||||
end
|
||||
|
||||
local nop = function()end
|
||||
local function new_devfs_dir(name)
|
||||
local sys = {}
|
||||
sys.mtab = new_node(nil, name or "/", true)
|
||||
|
||||
function proxy.getLabel()
|
||||
-- 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)
|
||||
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)
|
||||
path = table.concat(segments, "/")
|
||||
|
||||
if not target or target == "" then
|
||||
return nil, "missing argument"
|
||||
end
|
||||
|
||||
local node, rest = sys.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
|
||||
|
||||
return sys
|
||||
end
|
||||
|
||||
local devfs = new_devfs_dir()
|
||||
|
||||
local bfd = "bad file descriptor"
|
||||
|
||||
function devfs.getLabel()
|
||||
return "devfs"
|
||||
end
|
||||
|
||||
function proxy.setLabel(value)
|
||||
function devfs.setLabel(value)
|
||||
error("drive does not support labeling")
|
||||
end
|
||||
|
||||
function proxy.spaceTotal()
|
||||
function devfs.spaceTotal()
|
||||
return 0
|
||||
end
|
||||
|
||||
function proxy.spaceUsed()
|
||||
function devfs.spaceUsed()
|
||||
return 0
|
||||
end
|
||||
|
||||
function proxy.exists(path)
|
||||
return not not proxy.points[path]
|
||||
function devfs.makeDirectory(path)
|
||||
return false, "to create dirs in devfs use devfs.create"
|
||||
end
|
||||
|
||||
function proxy.size(path)
|
||||
return 0
|
||||
end
|
||||
|
||||
function proxy.isDirectory(path)
|
||||
return false
|
||||
end
|
||||
|
||||
function proxy.lastModified(path)
|
||||
return fs.lastModified("/dev/")
|
||||
end
|
||||
|
||||
function proxy.list()
|
||||
local keys = {}
|
||||
for k,v in pairs(proxy.points) do
|
||||
table.insert(keys, k)
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
function proxy.makeDirectory(path)
|
||||
return false
|
||||
end
|
||||
|
||||
function proxy.remove(path)
|
||||
if not proxy.exists(path) then return false end
|
||||
proxy.points[path] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
function proxy.rename(from, to)
|
||||
return false
|
||||
end
|
||||
|
||||
proxy.close = nop
|
||||
|
||||
function proxy.open(path, mode)
|
||||
checkArg(1, path, "string")
|
||||
|
||||
local handle = proxy.points[path]
|
||||
if not handle then return nil, "device point [" .. path .. "] does not exist" end
|
||||
|
||||
local msg = "device point [" .. path .. "] cannot be opened for "
|
||||
|
||||
if mode == "r" then
|
||||
if not handle.read then
|
||||
return nil, msg .. "read"
|
||||
end
|
||||
else
|
||||
if not handle.write then
|
||||
return nil, msg .. "write"
|
||||
end
|
||||
end
|
||||
|
||||
return handle
|
||||
end
|
||||
|
||||
function proxy.read(h,...)
|
||||
function devfs.read(h,...)
|
||||
if not h.read then return nil, bfd end
|
||||
return h:read(...)
|
||||
end
|
||||
|
||||
function proxy.seek(h,...)
|
||||
function devfs.seek(h,...)
|
||||
if not h.seek then return nil, bfd end
|
||||
return h:seek(...)
|
||||
end
|
||||
|
||||
function proxy.write(h,...)
|
||||
function devfs.write(h,...)
|
||||
if not h.write then return nil, bfd end
|
||||
return h:write(...)
|
||||
end
|
||||
|
||||
function proxy.create(path, handle)
|
||||
handle.close = handle.close or nop
|
||||
proxy.points[path] = handle
|
||||
return true
|
||||
function devfs.close(h, ...)
|
||||
if not h.close then return nil, bfd end
|
||||
return h:close(...)
|
||||
end
|
||||
|
||||
proxy.create("null", {write = nop})
|
||||
proxy.create("random", {read = function(_,n)
|
||||
local chars = {}
|
||||
for i=1,n do
|
||||
table.insert(chars,string.char(math.random(0,255)))
|
||||
end
|
||||
return table.concat(chars)
|
||||
end})
|
||||
-- devfs.create creates a new dev point at path
|
||||
-- devfs is mounted to /sys by default, and /dev is a symlink to /sys/dev. If you want a devfs point to show up in /dev, specify a path here as "/dev/your_path")
|
||||
-- the handler can be a single file dev file (called a point), or a devfs dir [which allows it to list its own dynamic list of points and dirs]
|
||||
-- note: devfs dirs that list their own child dirs will have to handle directory queries on their own, such as list() and open(path, mode)
|
||||
|
||||
return proxy
|
||||
-- A handler represents a directory IF it defines list(), which returns a string array of the point names
|
||||
-- a directory handler acts like simplified filesystem of its own.
|
||||
-- note: that when creating new devfs points or dirs, devfs.create will not traverse into dynamic directory children of dev mount points
|
||||
-- Meaning, if you create a devfs dir, which returns dirs children of its own, devfs.create does not support creating dev points
|
||||
-- on those children
|
||||
|
||||
-- see new_devfs_dir() -- it might work for you, /dev uses it
|
||||
|
||||
-- Also note, your own devfs dirs may implement open() however they like -- devfs points' open() is called by the devfs library but dynamic
|
||||
-- dir act as their own library for their own points
|
||||
|
||||
-- ### devfs point methods ###
|
||||
-- Required
|
||||
-- open(mode: string []) file: returns new file handle for point (see "devfs point handle methods")
|
||||
-- Optional
|
||||
-- size(path) number
|
||||
|
||||
-- ### devfs point handle instance methods ###
|
||||
-- Required
|
||||
-- + technicaly, one of the following is not required when the mode is for the other (e.g. no read when in write mode)
|
||||
-- write(self, value, ...) boolean: writes each value (params list) and returns success
|
||||
-- read(self, n: number) string: return string of n bytes, nil when no more bytes available
|
||||
-- Optional
|
||||
-- seek(self, whence [string], offset [number]) number: move file handle from whence by offset, return offset result
|
||||
-- close(self) boolean: close the file handle. Note that if your open method allocated resources, you'll need to release them in close
|
||||
|
||||
-- ### devfs dir methods ###
|
||||
-- Required
|
||||
-- list() string[]: return list of child point names
|
||||
-- if you use new_devfs_dir, set metatable on .points with __pairs and __index if you want a dynamic list of files
|
||||
-- open(path, mode) file (table): return a file handle to path (path is relative)
|
||||
-- it would be nice to make open() optional, but devfs doesn't know where you might store your point handlers, if you even have any
|
||||
-- Optional
|
||||
-- size(path) number
|
||||
-- lastModified(path) number
|
||||
-- isDirectory(path) boolean -- default returns false. Having dynamic dirs is considered advanced
|
||||
-- remove(path) boolean
|
||||
-- rename(path) boolean
|
||||
-- exists(path) boolean -- default checks path against list() results
|
||||
|
||||
-- /dev is a special handler
|
||||
|
||||
local function devfs_load(key)
|
||||
return require("tools/devfs/" .. key)
|
||||
end
|
||||
|
||||
devfs.create("null", devfs_load("null"))
|
||||
devfs.create("random", devfs_load("random"))
|
||||
if comp.isAvailable("eeprom") then
|
||||
devfs.create("eeprom", devfs_load("eeprom"))
|
||||
devfs.create("eeprom-data", devfs_load("eeprom-data"))
|
||||
end
|
||||
|
||||
return devfs
|
||||
|
@ -112,9 +112,7 @@ function filesystem.setAutorunEnabled(value)
|
||||
saveConfig()
|
||||
end
|
||||
|
||||
function filesystem.segments(path)
|
||||
return segments(path)
|
||||
end
|
||||
filesystem.segments = segments
|
||||
|
||||
function filesystem.canonical(path)
|
||||
local result = table.concat(segments(path), "/")
|
||||
@ -367,7 +365,11 @@ function filesystem.makeDirectory(path)
|
||||
end
|
||||
local node, rest = findNode(path)
|
||||
if node.fs and rest then
|
||||
return node.fs.makeDirectory(rest)
|
||||
local success, reason = node.fs.makeDirectory(rest)
|
||||
if not success and not reason and node.fs.isReadOnly() then
|
||||
reason = "filesystem is readonly"
|
||||
end
|
||||
return success, reason
|
||||
end
|
||||
if node.fs then
|
||||
return nil, "virtual directory with that name already exists"
|
||||
@ -502,11 +504,12 @@ function filesystem.open(path, mode)
|
||||
checkArg(1, path, "string")
|
||||
mode = tostring(mode or "r")
|
||||
checkArg(2, mode, "string")
|
||||
|
||||
assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode],
|
||||
"bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")")
|
||||
|
||||
local node, rest = findNode(path)
|
||||
if not node.fs or not rest then
|
||||
if not node.fs or not rest or (({r=true,rb=true})[mode] and not node.fs.exists(rest)) then
|
||||
return nil, "file not found"
|
||||
end
|
||||
|
||||
|
@ -1,47 +0,0 @@
|
||||
local guid = {}
|
||||
|
||||
function guid.toHex(n)
|
||||
if type(n) ~= 'number' then
|
||||
return nil, string.format("toHex only converts numbers to strings, %s is not a string, but a %s", tostring(n), type(n))
|
||||
end
|
||||
if n == 0 then
|
||||
return '0'
|
||||
end
|
||||
|
||||
local hexchars = "0123456789abcdef"
|
||||
local result = ""
|
||||
local prefix = "" -- maybe later allow for arg to request 0x prefix
|
||||
if n < 0 then
|
||||
prefix = "-"
|
||||
n = -n
|
||||
end
|
||||
|
||||
while n > 0 do
|
||||
local next = math.floor(n % 16) + 1 -- lua has 1 based array indices
|
||||
n = math.floor(n / 16)
|
||||
result = hexchars:sub(next, next) .. result
|
||||
end
|
||||
|
||||
return prefix .. result
|
||||
end
|
||||
|
||||
function guid.next()
|
||||
-- e.g. 3c44c8a9-0613-46a2-ad33-97b6ba2e9d9a
|
||||
-- 8-4-4-4-12
|
||||
local sets = {8, 4, 4, 4, 12}
|
||||
local result = ""
|
||||
|
||||
local i
|
||||
for _,set in ipairs(sets) do
|
||||
if result:len() > 0 then
|
||||
result = result .. "-"
|
||||
end
|
||||
for i = 1,set do
|
||||
result = result .. guid.toHex(math.random(0, 15))
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
return guid
|
@ -454,7 +454,7 @@ function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern)
|
||||
|
||||
local function magical(s)
|
||||
for _,glob_rule in ipairs(sh.internal.globbers) do
|
||||
if s:match("[^%%]-"..text.escapeMagic(glob_rule[2])) then
|
||||
if (" "..s):match("[^%%]"..text.escapeMagic(glob_rule[2])) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
@ -464,7 +464,6 @@ function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern)
|
||||
local root = is_abs and '' or shell.getWorkingDirectory():gsub("([^/])$","%1/")
|
||||
local paths = {is_abs and "/" or ''}
|
||||
local relative_separator = ''
|
||||
|
||||
for i,segment in ipairs(segments) do
|
||||
local enclosed_pattern = string.format("^(%s)/?$", segment)
|
||||
local next_paths = {}
|
||||
|
@ -243,14 +243,17 @@ function term.readKeyboard(ops)
|
||||
end
|
||||
|
||||
while true do
|
||||
local killed, name, address, char, code = term.internal.pull(input)
|
||||
local ok, name, address, char, code = term.internal.pull(input)
|
||||
if not term.isAvailable() then
|
||||
return
|
||||
end
|
||||
-- we have to keep checking what kb is active in case it is switching during use
|
||||
-- we could have multiple screens, each with keyboards active
|
||||
local main_kb = term.keyboard(w)
|
||||
local main_sc = term.screen(w)
|
||||
local c = nil
|
||||
local c
|
||||
local backup_cache = hints.cache
|
||||
if name == "interrupted" or name == "term_unavailable" then
|
||||
if name == "interrupted" then
|
||||
draw("^C\n",true)
|
||||
return ""
|
||||
elseif address == main_kb or address == main_sc then
|
||||
|
@ -288,4 +288,93 @@ function --[[@delayloaded-start@]] text.internal.normalize(words, omitQuotes)
|
||||
return norms
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function --[[@delayloaded-start@]] text.internal.seeker(handle, whence, to)
|
||||
if not handle.txt then
|
||||
return nil, "bad file descriptor"
|
||||
end
|
||||
to = to or 0
|
||||
if whence == "cur" then
|
||||
handle.offset = handle.offset + to
|
||||
elseif whence == "set" then
|
||||
handle.offset = to
|
||||
elseif whence == "end" then
|
||||
handle.offset = handle.len + to
|
||||
end
|
||||
handle.offset = math.max(0, math.min(handle.offset, handle.len))
|
||||
return handle.offset
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function --[[@delayloaded-start@]] text.internal.reader(txt)
|
||||
checkArg(1, txt, "string")
|
||||
local reader =
|
||||
{
|
||||
txt = txt,
|
||||
len = unicode.len(txt),
|
||||
offset = 0,
|
||||
read = function(_, n)
|
||||
checkArg(1, n, "number")
|
||||
if not _.txt then
|
||||
return nil, "bad file descriptor"
|
||||
end
|
||||
if _.offset >= _.len then
|
||||
return nil
|
||||
end
|
||||
local last_offset = _.offset
|
||||
_:seek("cur", n)
|
||||
local next = unicode.sub(_.txt, last_offset + 1, _.offset)
|
||||
return next
|
||||
end,
|
||||
seek = text.internal.seeker,
|
||||
close = function(_)
|
||||
if not _.txt then
|
||||
return nil, "bad file descriptor"
|
||||
end
|
||||
_.txt = nil
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
return reader
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
function --[[@delayloaded-start@]] text.internal.writer(ostream, append_txt)
|
||||
if type(ostream) == "table" then
|
||||
local mt = getmetatable(ostream) or {}
|
||||
checkArg(1, mt.__call, "function")
|
||||
end
|
||||
checkArg(1, ostream, "function", "table")
|
||||
checkArg(2, append_txt, "string", "nil")
|
||||
local writer =
|
||||
{
|
||||
txt = "",
|
||||
offset = 0,
|
||||
len = 0,
|
||||
write = function(_, ...)
|
||||
if not _.txt then
|
||||
return nil, "bad file descriptor"
|
||||
end
|
||||
local pre, vs, pos = unicode.sub(_.txt, 1, _.offset), {}, unicode.sub(_.txt, _.offset + 1)
|
||||
for i,v in ipairs({...}) do
|
||||
table.insert(vs, v)
|
||||
end
|
||||
vs = table.concat(vs)
|
||||
_:seek("cur", unicode.len(vs))
|
||||
_.txt = pre .. vs .. pos
|
||||
_.len = unicode.len(_.txt)
|
||||
return true
|
||||
end,
|
||||
seek = text.internal.seeker,
|
||||
close = function(_)
|
||||
if not _.txt then
|
||||
return nil, "bad file descriptor"
|
||||
end
|
||||
ostream((append_txt or "") .. _.txt)
|
||||
_.txt = nil
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
return writer
|
||||
end --[[@delayloaded-end@]]
|
||||
|
||||
return text, local_env
|
||||
|
@ -0,0 +1,19 @@
|
||||
local comp = require("component")
|
||||
local text = require("text")
|
||||
|
||||
if not comp.isAvailable("eeprom") then
|
||||
return nil
|
||||
end
|
||||
|
||||
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
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
local comp = require("component")
|
||||
local text = require("text")
|
||||
|
||||
if not comp.isAvailable("eeprom") then
|
||||
return nil
|
||||
end
|
||||
|
||||
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
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
return
|
||||
{
|
||||
open = function(mode)
|
||||
if not mode or not mode:match("[wa]") then
|
||||
return nil, "write only"
|
||||
end
|
||||
return
|
||||
{
|
||||
write = function() end
|
||||
}
|
||||
end
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
return
|
||||
{
|
||||
open = function(mode)
|
||||
if mode and not mode:match("r") then
|
||||
return nil, "read only"
|
||||
end
|
||||
return
|
||||
{
|
||||
read = function(self, n)
|
||||
local chars = {}
|
||||
for i=1,n do
|
||||
table.insert(chars,string.char(math.random(0,255)))
|
||||
end
|
||||
return table.concat(chars)
|
||||
end
|
||||
}
|
||||
end,
|
||||
size = function()
|
||||
return math.huge
|
||||
end
|
||||
}
|
@ -171,16 +171,11 @@ options.setboot = source.prop.setboot and not options.nosetboot
|
||||
options.reboot = source.prop.reboot and not options.noreboot
|
||||
options.source_dir = fs.canonical(source.prop.fromDir or options.fromDir or "") .. '/.'
|
||||
|
||||
local installer_path = options.source_root .. "/.install"
|
||||
if fs.exists(installer_path) then
|
||||
os.exit(loadfile("/lib/tools/install_utils.lua", "bt", _G)('install', options))
|
||||
end
|
||||
|
||||
local cp_args =
|
||||
{
|
||||
"-vrx" .. (options.update and "ui" or ""),
|
||||
options.source_root .. options.source_dir,
|
||||
options.target_root .. options.target_dir
|
||||
options.target_root:gsub("//","/") .. options.target_dir
|
||||
}
|
||||
|
||||
local source_display = (source.prop or {}).label or source.dev.getLabel() or source.path
|
||||
@ -194,6 +189,11 @@ if not ((io.read() or "n").."y"):match("^%s*[Yy]") then
|
||||
os.exit()
|
||||
end
|
||||
|
||||
local installer_path = options.source_root .. "/.install"
|
||||
if fs.exists(installer_path) then
|
||||
os.exit(loadfile("/lib/tools/install_utils.lua", "bt", _G)('install', options))
|
||||
end
|
||||
|
||||
return
|
||||
{
|
||||
setlabel = options.setlabel,
|
||||
|
@ -41,18 +41,18 @@ end
|
||||
if cmd == 'select' then
|
||||
if #options.sources == 0 then
|
||||
if options.source_label then
|
||||
io.stderr:write("No install source matched given label: " .. options.source_label .. '\n')
|
||||
io.stderr:write("Nothing to install labeled: " .. options.source_label .. '\n')
|
||||
elseif options.from then
|
||||
io.stderr:write("No install source found: " .. options.from .. '\n')
|
||||
io.stderr:write("Nothing to install from: " .. options.from .. '\n')
|
||||
else
|
||||
io.stderr:write("Could not find any available installations\n")
|
||||
io.stderr:write("Nothing to install\n")
|
||||
end
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
if #options.targets == 0 then
|
||||
if options.to then
|
||||
io.stderr:write("No such filesystem to install to: " .. options.to .. '\n')
|
||||
io.stderr:write("No such target to install to: " .. options.to .. '\n')
|
||||
else
|
||||
io.stderr:write("No writable disks found, aborting\n")
|
||||
end
|
||||
|
@ -0,0 +1,22 @@
|
||||
local uuid = {}
|
||||
|
||||
function uuid.next()
|
||||
-- e.g. 3c44c8a9-0613-46a2-ad33-97b6ba2e9d9a
|
||||
-- 8-4-4-4-12 (halved sizes because bytes make hex pairs)
|
||||
local sets = {4, 2, 2, 2, 6}
|
||||
local result = ""
|
||||
|
||||
local i
|
||||
for _,set in ipairs(sets) do
|
||||
if result:len() > 0 then
|
||||
result = result .. "-"
|
||||
end
|
||||
for i = 1,set do
|
||||
result = result .. string.format("%02x", math.random(0, 255))
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
return uuid
|
@ -9,10 +9,10 @@ DESCRIPTION
|
||||
|
||||
OPTIONS
|
||||
--from=ADDR
|
||||
Specifies the source filesystem or its root path. ADDR can be the device guid or a directory path. If this is a directory path, it represents a root path to install from. This option can also be used to specify source paths that would otherwise be ignored, those being devfs, tmpfs, and the rootfs. e.g. --from=/tmp . Note that if both --from and a [name] is given, install expects the source path to have a .prop defining the same name. See .prop for more details.
|
||||
Specifies the source filesystem or its root path. ADDR can be the device uuid or a directory path. If this is a directory path, it represents a root path to install from. This option can also be used to specify source paths that would otherwise be ignored, those being devfs, tmpfs, and the rootfs. e.g. --from=/tmp . Note that if both --from and a [name] is given, install expects the source path to have a .prop defining the same name. See .prop for more details.
|
||||
|
||||
--to=ADDR
|
||||
Same as --from but specifies the target filesystem by guid or its root path. This option can also be used to specify filesystems that are otherwise ignored including tmpfs. i.e. --to=ADDR where ADDR matches the tmpfs device address or its mount point path. e.g. --to=/tmp
|
||||
Same as --from but specifies the target filesystem by uuid or its root path. This option can also be used to specify filesystems that are otherwise ignored including tmpfs. i.e. --to=ADDR where ADDR matches the tmpfs device address or its mount point path. e.g. --to=/tmp
|
||||
|
||||
--fromDir=PATH
|
||||
Install PATH from source. PATH is relative to the root of the source filesystem or path given by --from. The default is .
|
||||
|
Loading…
x
Reference in New Issue
Block a user