mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-16 02:39:48 -04:00
Merge branch master-MC1.11 into master-MC1.12
This commit is contained in:
commit
18592726c4
@ -86,6 +86,8 @@ to create an IntelliJ IDEA project.
|
|||||||
|
|
||||||
Open the project and you will be asked to *import the Gradle project* (check your Event Log if you missed the pop-up). **Do so**. This will configure additionally referenced libraries.
|
Open the project and you will be asked to *import the Gradle project* (check your Event Log if you missed the pop-up). **Do so**. This will configure additionally referenced libraries.
|
||||||
|
|
||||||
|
For more specific instructions, read [Steps to run master MC1.7.10 from IDEA][idea_1.7.10]
|
||||||
|
|
||||||
In the case you wish to use Eclipse rather than IntelliJ IDEA, the process is mostly the same, except you must run `gradlew eclipse` rather than `gradlew idea`.
|
In the case you wish to use Eclipse rather than IntelliJ IDEA, the process is mostly the same, except you must run `gradlew eclipse` rather than `gradlew idea`.
|
||||||
|
|
||||||
|
|
||||||
@ -108,3 +110,4 @@ In the case you wish to use Eclipse rather than IntelliJ IDEA, the process is mo
|
|||||||
[wiki]: http://ocdoc.cil.li/
|
[wiki]: http://ocdoc.cil.li/
|
||||||
[integration]: https://github.com/MightyPirates/OpenComputers/tree/master-MC1.7.10/src/main/scala/li/cil/oc/integration
|
[integration]: https://github.com/MightyPirates/OpenComputers/tree/master-MC1.7.10/src/main/scala/li/cil/oc/integration
|
||||||
[ingame manual]: https://github.com/MightyPirates/OpenComputers/tree/master-MC1.7.10/src/main/resources/assets/opencomputers/doc
|
[ingame manual]: https://github.com/MightyPirates/OpenComputers/tree/master-MC1.7.10/src/main/resources/assets/opencomputers/doc
|
||||||
|
[idea_1.7.10]: http://ocdoc.cil.li/tutorial:debug_1.7.10
|
@ -1,15 +1,34 @@
|
|||||||
local fs = require("filesystem")
|
local fs = require("filesystem")
|
||||||
local shell = require("shell")
|
local shell = require("shell")
|
||||||
|
|
||||||
local args, ops = shell.parse(...)
|
local function usage()
|
||||||
local argc = #args
|
io.stderr:write([==[
|
||||||
|
Usage: mount [OPTIONS] [device path]")
|
||||||
|
If no args are given, all current mount points are printed.
|
||||||
|
<Options> Note that multiple options can be used together
|
||||||
|
-r, --ro Mount the filesystem read only
|
||||||
|
--bind Create a mount bind point, folder to folder
|
||||||
|
<Args>
|
||||||
|
device Specify filesystem device by one of:
|
||||||
|
a. label
|
||||||
|
b. address (can be abbreviated)
|
||||||
|
c. folder path (requires --bind)
|
||||||
|
path Target folder path to mount to
|
||||||
|
|
||||||
if ops and (ops.h or ops.help) then
|
See `man mount` for more details
|
||||||
print("see `man mount` for help");
|
]==])
|
||||||
os.exit(1)
|
os.exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
if argc == 0 then
|
-- smart parse, follow arg after -o
|
||||||
|
local args, opts = shell.parse(...)
|
||||||
|
opts.readonly = opts.r or opts.readonly
|
||||||
|
|
||||||
|
if opts.h or opts.help then
|
||||||
|
usage()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function print_mounts()
|
||||||
-- for each mount
|
-- for each mount
|
||||||
local mounts = {}
|
local mounts = {}
|
||||||
|
|
||||||
@ -38,29 +57,40 @@ if argc == 0 then
|
|||||||
local rw_ro = "(" .. device.rw_ro .. ")"
|
local rw_ro = "(" .. device.rw_ro .. ")"
|
||||||
local fs_label = "\"" .. device.fs_label .. "\""
|
local fs_label = "\"" .. device.fs_label .. "\""
|
||||||
|
|
||||||
io.write(string.format("%s on %-10s %s %s\n",
|
io.write(string.format("%-8s on %-10s %s %s\n",
|
||||||
dev_path:sub(1,8),
|
dev_path:sub(1,8),
|
||||||
device.mount_path,
|
device.mount_path,
|
||||||
rw_ro,
|
rw_ro,
|
||||||
fs_label))
|
fs_label))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif argc ~= 2 then
|
end
|
||||||
print("Usage: mount [<label|address> <path>]")
|
|
||||||
print("Note that the address may be abbreviated.")
|
local function do_mount()
|
||||||
return 1 -- error code
|
-- bind converts a path to a proxy
|
||||||
else
|
local proxy, reason = fs.proxy(args[1], opts)
|
||||||
local proxy, reason = fs.proxy(args[1])
|
|
||||||
if not proxy then
|
if not proxy then
|
||||||
io.stderr:write(reason,"\n")
|
io.stderr:write("Failed to mount: ", tostring(reason), "\n")
|
||||||
return 1
|
os.exit(1)
|
||||||
elseif ops.r then
|
|
||||||
proxy = dofile("/lib/core/ro_wrapper.lua").wrap(proxy)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local result, mount_failure = fs.mount(proxy, shell.resolve(args[2]))
|
local result, mount_failure = fs.mount(proxy, shell.resolve(args[2]))
|
||||||
if not result then
|
if not result then
|
||||||
io.stderr:write(mount_failure, "\n")
|
io.stderr:write(mount_failure, "\n")
|
||||||
return 2 -- error code
|
os.exit(2) -- error code
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if #args == 0 then
|
||||||
|
if next(opts) then
|
||||||
|
io.stderr:write("Missing argument\n")
|
||||||
|
usage()
|
||||||
|
else
|
||||||
|
print_mounts()
|
||||||
|
end
|
||||||
|
elseif #args == 2 then
|
||||||
|
do_mount()
|
||||||
|
else
|
||||||
|
io.stderr:write("wrong number of arguments: ", #args, "\n")
|
||||||
|
usage()
|
||||||
|
end
|
||||||
|
@ -17,13 +17,20 @@ if not file then
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local current_data = process.info().data
|
local lines = file:lines()
|
||||||
|
|
||||||
local source_proc = process.load((assert(os.getenv("SHELL"), "no $SHELL set")))
|
while true do
|
||||||
local source_data = process.list[source_proc].data
|
local line = lines()
|
||||||
source_data.aliases = current_data.aliases -- hacks to propogate sub shell env changes
|
if not line then
|
||||||
source_data.vars = current_data.vars
|
break
|
||||||
source_data.io[0] = file -- set stdin to the file
|
end
|
||||||
process.internal.continue(source_proc, "-c")
|
local current_data = process.info().data
|
||||||
|
|
||||||
file:close() -- should have closed when the process closed, but just to be sure
|
local source_proc = process.load((assert(os.getenv("SHELL"), "no $SHELL set")))
|
||||||
|
local source_data = process.list[source_proc].data
|
||||||
|
source_data.aliases = current_data.aliases -- hacks to propogate sub shell env changes
|
||||||
|
source_data.vars = current_data.vars
|
||||||
|
process.internal.continue(source_proc, _ENV, line)
|
||||||
|
end
|
||||||
|
|
||||||
|
file:close()
|
||||||
|
@ -62,5 +62,5 @@ os.setenv("TMP", "/tmp") -- Deprecated
|
|||||||
os.setenv("TMPDIR", "/tmp")
|
os.setenv("TMPDIR", "/tmp")
|
||||||
|
|
||||||
if computer.tmpAddress() then
|
if computer.tmpAddress() then
|
||||||
fs.mount(computer.tmpAddress(), os.getenv("TMPDIR") or "/tmp")
|
fs.mount(computer.tmpAddress(), "/tmp")
|
||||||
end
|
end
|
||||||
|
@ -8,7 +8,7 @@ local core_stderr = buffer.new("w", setmetatable(
|
|||||||
write = function(_, str)
|
write = function(_, str)
|
||||||
return tty_stream:write("\27[31m"..str.."\27[37m")
|
return tty_stream:write("\27[31m"..str.."\27[37m")
|
||||||
end
|
end
|
||||||
}, {__index=tty_stream}))
|
}, {__index=tty_stream}))
|
||||||
|
|
||||||
core_stdout:setvbuf("no")
|
core_stdout:setvbuf("no")
|
||||||
core_stderr:setvbuf("no")
|
core_stderr:setvbuf("no")
|
||||||
|
@ -1,29 +1,13 @@
|
|||||||
local component = require("component")
|
|
||||||
local event = require("event")
|
local event = require("event")
|
||||||
local fs = require("filesystem")
|
local fs = require("filesystem")
|
||||||
local shell = require("shell")
|
local shell = require("shell")
|
||||||
|
local tmp = require("computer").tmpAddress()
|
||||||
|
|
||||||
local isInitialized, pendingAutoruns = false, {}
|
local pendingAutoruns = {}
|
||||||
|
|
||||||
local function onInit()
|
|
||||||
isInitialized = true
|
|
||||||
for _, run in ipairs(pendingAutoruns) do
|
|
||||||
local result, reason = pcall(run)
|
|
||||||
if not result then
|
|
||||||
local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log")
|
|
||||||
local log = io.open(path, "a")
|
|
||||||
if log then
|
|
||||||
log:write(tostring(result) .. ":" .. tostring(reason) .. "\n")
|
|
||||||
log:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
pendingAutoruns = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function onComponentAdded(_, address, componentType)
|
local function onComponentAdded(_, address, componentType)
|
||||||
if componentType == "filesystem" and require("computer").tmpAddress() ~= address then
|
if componentType == "filesystem" and tmp ~= address then
|
||||||
local proxy = component.proxy(address)
|
local proxy = fs.proxy(address)
|
||||||
if proxy then
|
if proxy then
|
||||||
local name = address:sub(1, 3)
|
local name = address:sub(1, 3)
|
||||||
while fs.exists(fs.concat("/mnt", name)) and
|
while fs.exists(fs.concat("/mnt", name)) and
|
||||||
@ -37,13 +21,11 @@ local function onComponentAdded(_, address, componentType)
|
|||||||
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 run = function()
|
local run = {file, _ENV, proxy}
|
||||||
assert(shell.execute(file, _ENV, proxy))
|
if pendingAutoruns then
|
||||||
end
|
|
||||||
if isInitialized then
|
|
||||||
run()
|
|
||||||
else
|
|
||||||
table.insert(pendingAutoruns, run)
|
table.insert(pendingAutoruns, run)
|
||||||
|
else
|
||||||
|
xpcall(shell.execute, event.onError, table.unpack(run))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -60,7 +42,14 @@ local function onComponentRemoved(_, address, componentType)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
event.listen("init", onInit)
|
event.listen("init", function()
|
||||||
|
for _, run in ipairs(pendingAutoruns) do
|
||||||
|
xpcall(shell.execute, event.onError, table.unpack(run))
|
||||||
|
end
|
||||||
|
pendingAutoruns = nil
|
||||||
|
return false
|
||||||
|
end)
|
||||||
|
|
||||||
event.listen("component_added", onComponentAdded)
|
event.listen("component_added", onComponentAdded)
|
||||||
event.listen("component_removed", onComponentRemoved)
|
event.listen("component_removed", onComponentRemoved)
|
||||||
|
|
||||||
|
@ -1,35 +1,38 @@
|
|||||||
local component = require("component")
|
local component = require("component")
|
||||||
local computer = require("computer")
|
local computer = require("computer")
|
||||||
local text = require("text")
|
|
||||||
local unicode = require("unicode")
|
local unicode = require("unicode")
|
||||||
local tty = require("tty")
|
local tty = require("tty")
|
||||||
|
|
||||||
if not component.isAvailable("gpu") then
|
if not component.isAvailable("gpu") then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local lines = {_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)"}
|
|
||||||
local maxWidth = unicode.len(lines[1])
|
|
||||||
local f = io.open("/usr/misc/greetings.txt")
|
local f = io.open("/usr/misc/greetings.txt")
|
||||||
|
local lines = {_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)"}
|
||||||
|
local greeting = ""
|
||||||
if f then
|
if f then
|
||||||
local greetings = {}
|
local greetings = {}
|
||||||
pcall(function()
|
pcall(function()
|
||||||
for line in f:lines() do table.insert(greetings, line) end
|
for line in f:lines() do table.insert(greetings, line) end
|
||||||
end)
|
end)
|
||||||
f:close()
|
f:close()
|
||||||
local greeting = greetings[math.random(1, #greetings)]
|
greeting = greetings[math.random(1, math.max(#greetings, 1))] or ""
|
||||||
if greeting then
|
|
||||||
local width = math.max(10, tty.getViewport())
|
|
||||||
for line in text.wrappedLines(greeting, width - 4, width - 4) do
|
|
||||||
table.insert(lines, line)
|
|
||||||
maxWidth = math.max(maxWidth, unicode.len(line))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
local width = math.min(#greeting, tty.getViewport() - 5)
|
||||||
|
local maxLine = #lines[1]
|
||||||
|
while #greeting > 0 do
|
||||||
|
local si, ei = greeting:sub(1, width):find("%s%S*$")
|
||||||
|
local line = #greeting <= width and greeting or greeting:sub(1, si or width)
|
||||||
|
lines[#lines + 1] = line
|
||||||
|
maxLine = math.max(maxLine, #line)
|
||||||
|
greeting = greeting:sub(#line + 1)
|
||||||
|
end
|
||||||
|
|
||||||
local borders = {{unicode.char(0x2552), unicode.char(0x2550), unicode.char(0x2555)},
|
local borders = {{unicode.char(0x2552), unicode.char(0x2550), unicode.char(0x2555)},
|
||||||
{unicode.char(0x2502), nil, unicode.char(0x2502)},
|
{unicode.char(0x2502), nil, unicode.char(0x2502)},
|
||||||
{unicode.char(0x2514), unicode.char(0x2500), unicode.char(0x2518)}}
|
{unicode.char(0x2514), unicode.char(0x2500), unicode.char(0x2518)}}
|
||||||
io.write(borders[1][1] .. string.rep(borders[1][2], maxWidth + 2) .. borders[1][3] .. "\n")
|
io.write(borders[1][1], string.rep(borders[1][2], maxLine + 2), borders[1][3], "\n")
|
||||||
for _, line in ipairs(lines) do
|
for _,line in ipairs(lines) do
|
||||||
io.write(borders[2][1] .. " " .. text.padRight(line, maxWidth) .. " " .. borders[2][3] .. "\n")
|
io.write(borders[2][1], " ", line, (" "):rep(maxLine - #line + 1), borders[2][3], " \n")
|
||||||
end
|
end
|
||||||
io.write(borders[3][1] .. string.rep(borders[3][2], maxWidth + 2) .. borders[3][3] .. "\n")
|
io.write(borders[3][1] .. string.rep(borders[3][2], maxLine + 2) .. borders[3][3] .. "\n")
|
||||||
|
@ -39,14 +39,8 @@ function buffer:flush()
|
|||||||
local tmp = self.bufferWrite
|
local tmp = self.bufferWrite
|
||||||
self.bufferWrite = ""
|
self.bufferWrite = ""
|
||||||
local result, reason = self.stream:write(tmp)
|
local result, reason = self.stream:write(tmp)
|
||||||
if result then
|
if not result then
|
||||||
self.bufferWrite = ""
|
return nil, reason or "bad file descriptor"
|
||||||
else
|
|
||||||
if reason then
|
|
||||||
return nil, reason
|
|
||||||
else
|
|
||||||
return nil, "bad file descriptor"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
-- called from /init.lua
|
-- called from /init.lua
|
||||||
local raw_loadfile = ...
|
local raw_loadfile = ...
|
||||||
|
|
||||||
_G._OSVERSION = "OpenOS 1.6.8"
|
_G._OSVERSION = "OpenOS 1.7.1"
|
||||||
|
|
||||||
local component = component
|
local component = component
|
||||||
local computer = computer
|
local computer = computer
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
local event = require("event")
|
||||||
|
|
||||||
|
local function createMultipleFilter(...)
|
||||||
|
local filter = table.pack(...)
|
||||||
|
if filter.n == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return function(...)
|
||||||
|
local signal = table.pack(...)
|
||||||
|
if type(signal[1]) ~= "string" then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
for i = 1, filter.n do
|
||||||
|
if filter[i] ~= nil and signal[1]:match(filter[i]) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function event.pullMultiple(...)
|
||||||
|
local seconds
|
||||||
|
local args
|
||||||
|
if type(...) == "number" then
|
||||||
|
seconds = ...
|
||||||
|
args = table.pack(select(2,...))
|
||||||
|
for i=1,args.n do
|
||||||
|
checkArg(i+1, args[i], "string", "nil")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
args = table.pack(...)
|
||||||
|
for i=1,args.n do
|
||||||
|
checkArg(i, args[i], "string", "nil")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
|
||||||
|
end
|
||||||
|
|
||||||
|
function event.cancel(timerId)
|
||||||
|
checkArg(1, timerId, "number")
|
||||||
|
if handlers[timerId] then
|
||||||
|
handlers[timerId] = nil
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function event.ignore(name, callback)
|
||||||
|
checkArg(1, name, "string")
|
||||||
|
checkArg(2, callback, "function")
|
||||||
|
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
|
||||||
|
end
|
||||||
|
|
@ -1,4 +1,5 @@
|
|||||||
local filesystem = require("filesystem")
|
local filesystem = require("filesystem")
|
||||||
|
local component = require("component")
|
||||||
|
|
||||||
function filesystem.makeDirectory(path)
|
function filesystem.makeDirectory(path)
|
||||||
if filesystem.exists(path) then
|
if filesystem.exists(path) then
|
||||||
@ -136,3 +137,102 @@ function filesystem.copy(fromPath, toPath)
|
|||||||
return data == nil, reason
|
return data == nil, reason
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function readonly_wrap(proxy)
|
||||||
|
checkArg(1, proxy, "table")
|
||||||
|
if proxy.isReadOnly() then
|
||||||
|
return proxy
|
||||||
|
end
|
||||||
|
|
||||||
|
local function roerr() return nil, "filesystem is readonly" end
|
||||||
|
return setmetatable({
|
||||||
|
rename = roerr,
|
||||||
|
open = function(path, mode)
|
||||||
|
checkArg(1, path, "string")
|
||||||
|
checkArg(2, mode, "string")
|
||||||
|
if mode:match("[wa]") then
|
||||||
|
return roerr()
|
||||||
|
end
|
||||||
|
return proxy.open(path, mode)
|
||||||
|
end,
|
||||||
|
isReadOnly = function()
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
write = roerr,
|
||||||
|
setLabel = roerr,
|
||||||
|
makeDirectory = roerr,
|
||||||
|
remove = roerr,
|
||||||
|
}, {__index=proxy})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function bind_proxy(path)
|
||||||
|
local real, reason = filesystem.realPath(path)
|
||||||
|
if not real then
|
||||||
|
return nil, reason
|
||||||
|
end
|
||||||
|
if not filesystem.isDirectory(real) then
|
||||||
|
return nil, "must bind to a directory"
|
||||||
|
end
|
||||||
|
local real_fs, real_fs_path = filesystem.get(real)
|
||||||
|
if real == real_fs_path then
|
||||||
|
return real_fs
|
||||||
|
end
|
||||||
|
-- turn /tmp/foo into foo
|
||||||
|
local rest = real:sub(#real_fs_path + 1)
|
||||||
|
local function wrap_relative(fp)
|
||||||
|
return function(path, ...)
|
||||||
|
return fp(filesystem.concat(rest, path), ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local bind = {
|
||||||
|
type = "filesystem_bind",
|
||||||
|
address = real,
|
||||||
|
isReadOnly = real_fs.isReadOnly,
|
||||||
|
list = wrap_relative(real_fs.list),
|
||||||
|
isDirectory = wrap_relative(real_fs.isDirectory),
|
||||||
|
size = wrap_relative(real_fs.size),
|
||||||
|
lastModified = wrap_relative(real_fs.lastModified),
|
||||||
|
exists = wrap_relative(real_fs.exists),
|
||||||
|
open = wrap_relative(real_fs.open),
|
||||||
|
remove = wrap_relative(real_fs.remove),
|
||||||
|
read = real_fs.read,
|
||||||
|
write = real_fs.write,
|
||||||
|
close = real_fs.close,
|
||||||
|
getLabel = function() return "" end,
|
||||||
|
setLabel = function() return nil, "cannot set the label of a bind point" end,
|
||||||
|
}
|
||||||
|
return bind
|
||||||
|
end
|
||||||
|
|
||||||
|
filesystem.internal = {}
|
||||||
|
function filesystem.internal.proxy(filter, options)
|
||||||
|
checkArg(1, filter, "string")
|
||||||
|
checkArg(2, options, "table", "nil")
|
||||||
|
options = options or {}
|
||||||
|
local address, proxy, reason
|
||||||
|
if options.bind then
|
||||||
|
proxy, reason = bind_proxy(filter)
|
||||||
|
else
|
||||||
|
-- no options: filter should be a label or partial address
|
||||||
|
for c in component.list("filesystem", true) do
|
||||||
|
if component.invoke(c, "getLabel") == filter then
|
||||||
|
address = c
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if c:sub(1, filter:len()) == filter then
|
||||||
|
address = c
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not address then
|
||||||
|
return nil, "no such file system"
|
||||||
|
end
|
||||||
|
proxy, reason = component.proxy(address)
|
||||||
|
end
|
||||||
|
if not proxy then
|
||||||
|
return proxy, reason
|
||||||
|
end
|
||||||
|
if options.readonly then
|
||||||
|
proxy = readonly_wrap(proxy)
|
||||||
|
end
|
||||||
|
return proxy
|
||||||
|
end
|
||||||
|
@ -244,3 +244,43 @@ function text.padLeft(value, length)
|
|||||||
return string.rep(" ", length - unicode.wlen(value)) .. value
|
return string.rep(" ", length - unicode.wlen(value)) .. value
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function text.padRight(value, length)
|
||||||
|
checkArg(1, value, "string", "nil")
|
||||||
|
checkArg(2, length, "number")
|
||||||
|
if not value or unicode.wlen(value) == 0 then
|
||||||
|
return string.rep(" ", length)
|
||||||
|
else
|
||||||
|
return value .. string.rep(" ", length - unicode.wlen(value))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function text.wrap(value, width, maxWidth)
|
||||||
|
checkArg(1, value, "string")
|
||||||
|
checkArg(2, width, "number")
|
||||||
|
checkArg(3, maxWidth, "number")
|
||||||
|
local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline
|
||||||
|
if unicode.wlen(line) > width then -- do we even need to wrap?
|
||||||
|
local partial = unicode.wtrunc(line, width)
|
||||||
|
local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])")
|
||||||
|
if wrapped or unicode.wlen(line) > maxWidth then
|
||||||
|
partial = wrapped or partial
|
||||||
|
return partial, unicode.sub(value, unicode.len(partial) + 1), true
|
||||||
|
else
|
||||||
|
return "", value, true -- write in new line.
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local start = unicode.len(line) + unicode.len(nl) + 1
|
||||||
|
return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function text.wrappedLines(value, width, maxWidth)
|
||||||
|
local line
|
||||||
|
return function()
|
||||||
|
if value then
|
||||||
|
line, value = text.wrap(value, width, maxWidth)
|
||||||
|
return line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@ -0,0 +1,93 @@
|
|||||||
|
local vt100 = require("vt100")
|
||||||
|
|
||||||
|
local rules = vt100.rules
|
||||||
|
|
||||||
|
-- [?7[hl] wrap mode
|
||||||
|
rules[{"%[", "%?", "7", "[hl]"}] = function(window, _, _, _, nowrap)
|
||||||
|
window.nowrap = nowrap == "l"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- helper scroll function
|
||||||
|
local function set_cursor(window, x, y)
|
||||||
|
window.x = math.min(math.max(x, 1), window.width)
|
||||||
|
window.y = math.min(math.max(y, 1), window.height)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- -- These DO NOT SCROLL
|
||||||
|
-- [(%d+)A move cursor up n lines
|
||||||
|
-- [(%d+)B move cursor down n lines
|
||||||
|
-- [(%d+)C move cursor right n lines
|
||||||
|
-- [(%d+)D move cursor left n lines
|
||||||
|
rules[{"%[", "%d+", "[ABCD]"}] = function(window, _, n, dir)
|
||||||
|
local dx, dy = 0, 0
|
||||||
|
n = tonumber(n)
|
||||||
|
if dir == "A" then
|
||||||
|
dy = -n
|
||||||
|
elseif dir == "B" then
|
||||||
|
dy = n
|
||||||
|
elseif dir == "C" then
|
||||||
|
dx = n
|
||||||
|
else -- D
|
||||||
|
dx = -n
|
||||||
|
end
|
||||||
|
set_cursor(window, window.x + dx, window.y + dy)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- [Line;ColumnH Move cursor to screen location v,h
|
||||||
|
-- [Line;Columnf ^ same
|
||||||
|
rules[{"%[", "%d+", ";", "%d+", "[Hf]"}] = function(window, _, y, _, x)
|
||||||
|
set_cursor(window, tonumber(x), tonumber(y))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- [K clear line from cursor right
|
||||||
|
-- [0K ^ same
|
||||||
|
-- [1K clear line from cursor left
|
||||||
|
-- [2K clear entire line
|
||||||
|
local function clear_line(window, _, n)
|
||||||
|
n = tonumber(n) or 0
|
||||||
|
local x = n == 0 and window.x or 1
|
||||||
|
local rep = n == 1 and window.x or window.width
|
||||||
|
window.gpu.set(x, window.y, (" "):rep(rep))
|
||||||
|
end
|
||||||
|
rules[{"%[", "[012]?", "K"}] = clear_line
|
||||||
|
|
||||||
|
-- [J clear screen from cursor down
|
||||||
|
-- [0J ^ same
|
||||||
|
-- [1J clear screen from cursor up
|
||||||
|
-- [2J clear entire screen
|
||||||
|
rules[{"%[", "[012]?", "J"}] = function(window, _, n)
|
||||||
|
clear_line(window, _, n)
|
||||||
|
n = tonumber(n) or 0
|
||||||
|
local y = n == 0 and (window.y + 1) or 1
|
||||||
|
local rep = n == 1 and (window.y - 1) or window.height
|
||||||
|
window.gpu.fill(1, y, window.width, rep, " ")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- [H move cursor to upper left corner
|
||||||
|
-- [;H ^ same
|
||||||
|
-- [f ^ same
|
||||||
|
-- [;f ^ same
|
||||||
|
rules[{"%[;?", "[Hf]"}] = function(window)
|
||||||
|
set_cursor(window, 1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ]
|
||||||
|
rules[{"%[", "6", "n"}] = function(window)
|
||||||
|
-- this solution puts the response on stdin, but it isn't echo'd
|
||||||
|
-- I'm personally fine with the lack of echo
|
||||||
|
io.stdin.bufferRead = string.format("%s%s%d;%dR", io.stdin.bufferRead, string.char(0x1b), window.y, window.x)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- D scroll up one line -- moves cursor down
|
||||||
|
-- E move to next line (acts the same ^, but x=1)
|
||||||
|
-- M scroll down one line -- moves cursor up
|
||||||
|
rules[{"[DEM]"}] = function(window, dir)
|
||||||
|
if dir == "D" then
|
||||||
|
window.y = window.y + 1
|
||||||
|
elseif dir == "E" then
|
||||||
|
window.y = window.y + 1
|
||||||
|
window.x = 1
|
||||||
|
else -- M
|
||||||
|
window.y = window.y - 1
|
||||||
|
end
|
||||||
|
end
|
@ -61,13 +61,19 @@ local targets = {}
|
|||||||
|
|
||||||
-- tmpfs is not a candidate unless it is specified
|
-- tmpfs is not a candidate unless it is specified
|
||||||
|
|
||||||
|
local comps = require("component").list("filesystem")
|
||||||
local devices = {}
|
local devices = {}
|
||||||
|
|
||||||
|
-- not all mounts are components, only use components
|
||||||
for dev, path in fs.mounts() do
|
for dev, path in fs.mounts() do
|
||||||
devices[dev] = devices[dev] and #devices[dev] < #path and devices[dev] or path
|
if comps[dev.address] then
|
||||||
|
local known = devices[dev]
|
||||||
|
devices[dev] = known and #known < #path and known or path
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
devices[fs.get("/dev/") or false] = nil
|
local dev_dev = fs.get("/dev")
|
||||||
|
devices[dev_dev == rootfs or dev_dev] = nil
|
||||||
local tmpAddress = computer.tmpAddress()
|
local tmpAddress = computer.tmpAddress()
|
||||||
|
|
||||||
for dev, path in pairs(devices) do
|
for dev, path in pairs(devices) do
|
||||||
|
@ -101,7 +101,10 @@ while tty.isAvailable() do
|
|||||||
if string.sub(command, 1, 1) == "=" then
|
if string.sub(command, 1, 1) == "=" then
|
||||||
code, reason = load("return " .. string.sub(command, 2), "=stdin", "t", env)
|
code, reason = load("return " .. string.sub(command, 2), "=stdin", "t", env)
|
||||||
else
|
else
|
||||||
code, reason = load(command, "=stdin", "t", env)
|
code, reason = load("return " .. command, "=stdin", "t", env)
|
||||||
|
if not code then
|
||||||
|
code, reason = load(command, "=stdin", "t", env)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if code then
|
if code then
|
||||||
local result = table.pack(xpcall(code, debug.traceback))
|
local result = table.pack(xpcall(code, debug.traceback))
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
local lib = {}
|
|
||||||
|
|
||||||
function lib.wrap(proxy)
|
|
||||||
checkArg(1, proxy, "table")
|
|
||||||
if proxy.isReadOnly() then
|
|
||||||
return proxy
|
|
||||||
end
|
|
||||||
|
|
||||||
local function roerr() return nil, "filesystem is readonly" end
|
|
||||||
return setmetatable({
|
|
||||||
rename = roerr,
|
|
||||||
open = function(path, mode)
|
|
||||||
checkArg(1, path, "string")
|
|
||||||
checkArg(2, mode, "string")
|
|
||||||
if mode:match("[wa]") then
|
|
||||||
return roerr()
|
|
||||||
end
|
|
||||||
return proxy.open(path, mode)
|
|
||||||
end,
|
|
||||||
isReadOnly = function()
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
write = roerr,
|
|
||||||
setLabel = roerr,
|
|
||||||
makeDirectory = roerr,
|
|
||||||
remove = roerr,
|
|
||||||
}, {__index=proxy})
|
|
||||||
end
|
|
||||||
|
|
||||||
return lib
|
|
@ -89,48 +89,8 @@ local function createPlainFilter(name, ...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function createMultipleFilter(...)
|
|
||||||
local filter = table.pack(...)
|
|
||||||
if filter.n == 0 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return function(...)
|
|
||||||
local signal = table.pack(...)
|
|
||||||
if type(signal[1]) ~= "string" then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
for i = 1, filter.n do
|
|
||||||
if filter[i] ~= nil and signal[1]:match(filter[i]) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
function event.cancel(timerId)
|
|
||||||
checkArg(1, timerId, "number")
|
|
||||||
if handlers[timerId] then
|
|
||||||
handlers[timerId] = nil
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
function event.ignore(name, callback)
|
|
||||||
checkArg(1, name, "string")
|
|
||||||
checkArg(2, callback, "function")
|
|
||||||
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
|
|
||||||
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")
|
||||||
@ -145,7 +105,7 @@ end
|
|||||||
function event.onError(message)
|
function event.onError(message)
|
||||||
local log = io.open("/tmp/event.log", "a")
|
local log = io.open("/tmp/event.log", "a")
|
||||||
if log then
|
if log then
|
||||||
pcall(log.write, log, message, "\n")
|
pcall(log.write, log, tostring(message), "\n")
|
||||||
log:close()
|
log:close()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -161,24 +121,6 @@ function event.pull(...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function event.pullMultiple(...)
|
|
||||||
local seconds
|
|
||||||
local args
|
|
||||||
if type(...) == "number" then
|
|
||||||
seconds = ...
|
|
||||||
args = table.pack(select(2,...))
|
|
||||||
for i=1,args.n do
|
|
||||||
checkArg(i+1, args[i], "string", "nil")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
args = table.pack(...)
|
|
||||||
for i=1,args.n do
|
|
||||||
checkArg(i, args[i], "string", "nil")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n)))
|
|
||||||
end
|
|
||||||
|
|
||||||
function event.pullFiltered(...)
|
function event.pullFiltered(...)
|
||||||
local args = table.pack(...)
|
local args = table.pack(...)
|
||||||
local seconds, filter
|
local seconds, filter
|
||||||
@ -217,6 +159,8 @@ 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
|
||||||
|
|
||||||
|
require("package").delay(event, "/lib/core/full_event.lua")
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
return event
|
return event
|
||||||
|
@ -184,8 +184,8 @@ function filesystem.mount(fs, path)
|
|||||||
return nil, why
|
return nil, why
|
||||||
end
|
end
|
||||||
|
|
||||||
if filesystem.exists(real) then
|
if filesystem.exists(real) and not filesystem.isDirectory(real) then
|
||||||
return nil, "file already exists"
|
return nil, "mount point is not a directory"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -234,23 +234,13 @@ function filesystem.name(path)
|
|||||||
return parts[#parts]
|
return parts[#parts]
|
||||||
end
|
end
|
||||||
|
|
||||||
function filesystem.proxy(filter)
|
function filesystem.proxy(filter, options)
|
||||||
checkArg(1, filter, "string")
|
checkArg(1, filter, "string")
|
||||||
local address
|
if not component.list("filesystem")[filter] or next(options or {}) then
|
||||||
for c in component.list("filesystem", true) do
|
-- if not, load fs full library, it has a smarter proxy that also supports options
|
||||||
if component.invoke(c, "getLabel") == filter then
|
return filesystem.internal.proxy(filter, options)
|
||||||
address = c
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if c:sub(1, filter:len()) == filter then
|
|
||||||
address = c
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
if not address then
|
return component.proxy(filter) -- it might be a perfect match
|
||||||
return nil, "no such file system"
|
|
||||||
end
|
|
||||||
return component.proxy(address)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function filesystem.exists(path)
|
function filesystem.exists(path)
|
||||||
|
@ -72,9 +72,9 @@ end
|
|||||||
function package.delay(lib, file)
|
function package.delay(lib, file)
|
||||||
local mt = {
|
local mt = {
|
||||||
__index = function(tbl, key)
|
__index = function(tbl, key)
|
||||||
dofile(file)
|
|
||||||
setmetatable(lib, nil)
|
setmetatable(lib, nil)
|
||||||
setmetatable(lib.internal or {}, nil)
|
setmetatable(lib.internal or {}, nil)
|
||||||
|
dofile(file)
|
||||||
return tbl[key]
|
return tbl[key]
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
@ -65,11 +65,7 @@ function process.load(path, env, init, name)
|
|||||||
return code(init(...))
|
return code(init(...))
|
||||||
end,
|
end,
|
||||||
function(msg)
|
function(msg)
|
||||||
-- msg can be a custom error object
|
if type(msg) == "table" and msg.reason == "terminated" then
|
||||||
if type(msg) == "table" then
|
|
||||||
if msg.reason ~= "terminated" then
|
|
||||||
io.stderr:write(tostring(msg.reason), "\n")
|
|
||||||
end
|
|
||||||
return msg.code or 0
|
return msg.code or 0
|
||||||
end
|
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")
|
||||||
|
@ -4,53 +4,13 @@ local tx = require("transforms")
|
|||||||
local text = {}
|
local text = {}
|
||||||
text.internal = {}
|
text.internal = {}
|
||||||
|
|
||||||
text.syntax = {"^%d?>>?&%d+$","^%d?>>?",">>?","<%&%d+","<",";","&&","||?"}
|
text.syntax = {"^%d?>>?&%d+","^%d?>>?",">>?","<%&%d+","<",";","&&","||?"}
|
||||||
|
|
||||||
function text.trim(value) -- from http://lua-users.org/wiki/StringTrim
|
function text.trim(value) -- from http://lua-users.org/wiki/StringTrim
|
||||||
local from = string.match(value, "^%s*()")
|
local from = string.match(value, "^%s*()")
|
||||||
return from > #value and "" or string.match(value, ".*%S", from)
|
return from > #value and "" or string.match(value, ".*%S", from)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- used in motd
|
|
||||||
function text.padRight(value, length)
|
|
||||||
checkArg(1, value, "string", "nil")
|
|
||||||
checkArg(2, length, "number")
|
|
||||||
if not value or unicode.wlen(value) == 0 then
|
|
||||||
return string.rep(" ", length)
|
|
||||||
else
|
|
||||||
return value .. string.rep(" ", length - unicode.wlen(value))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function text.wrap(value, width, maxWidth)
|
|
||||||
checkArg(1, value, "string")
|
|
||||||
checkArg(2, width, "number")
|
|
||||||
checkArg(3, maxWidth, "number")
|
|
||||||
local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline
|
|
||||||
if unicode.wlen(line) > width then -- do we even need to wrap?
|
|
||||||
local partial = unicode.wtrunc(line, width)
|
|
||||||
local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])")
|
|
||||||
if wrapped or unicode.wlen(line) > maxWidth then
|
|
||||||
partial = wrapped or partial
|
|
||||||
return partial, unicode.sub(value, unicode.len(partial) + 1), true
|
|
||||||
else
|
|
||||||
return "", value, true -- write in new line.
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local start = unicode.len(line) + unicode.len(nl) + 1
|
|
||||||
return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0
|
|
||||||
end
|
|
||||||
|
|
||||||
function text.wrappedLines(value, width, maxWidth)
|
|
||||||
local line
|
|
||||||
return function()
|
|
||||||
if value then
|
|
||||||
line, value = text.wrap(value, width, maxWidth)
|
|
||||||
return line
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- used by lib/sh
|
-- used by lib/sh
|
||||||
function text.escapeMagic(txt)
|
function text.escapeMagic(txt)
|
||||||
return txt:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1')
|
return txt:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1')
|
||||||
|
@ -32,11 +32,11 @@ function tty.key_down_handler(handler, cursor, char, code)
|
|||||||
elseif code == keys.enter or code == keys.numpadenter then
|
elseif code == keys.enter or code == keys.numpadenter then
|
||||||
cursor:move(math.huge)
|
cursor:move(math.huge)
|
||||||
cursor:draw("\n")
|
cursor:draw("\n")
|
||||||
if #data > 0 then
|
if data:find("%S") and data ~= handler[1] then
|
||||||
table.insert(handler, 1, data)
|
table.insert(handler, 1, data)
|
||||||
handler[(tonumber(os.getenv("HISTSIZE")) or 10)+1]=nil
|
handler[(tonumber(os.getenv("HISTSIZE")) or 10)+1]=nil
|
||||||
handler[0]=nil
|
|
||||||
end
|
end
|
||||||
|
handler[0]=nil
|
||||||
return nil, data .. "\n"
|
return nil, data .. "\n"
|
||||||
elseif code == keys.up or code == keys.down then
|
elseif code == keys.up or code == keys.down then
|
||||||
local ni = handler.index + (code == keys.up and 1 or -1)
|
local ni = handler.index + (code == keys.up and 1 or -1)
|
||||||
@ -101,49 +101,23 @@ function tty.isAvailable()
|
|||||||
return not not (gpu and gpu.getScreen())
|
return not not (gpu and gpu.getScreen())
|
||||||
end
|
end
|
||||||
|
|
||||||
function tty.stream:blink(done)
|
|
||||||
local width, height, dx, dy, x, y = tty.getViewport()
|
|
||||||
local gpu = tty.gpu()
|
|
||||||
if not gpu or x < 1 or x > width or y < 1 or y > height then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
x, y = x + dx, y + dy
|
|
||||||
local blinked, bgColor, bgIsPalette, fgColor, fgIsPalette, char_at_cursor = table.unpack(self.blink_cache or {})
|
|
||||||
if done == nil then -- reset
|
|
||||||
blinked = false
|
|
||||||
bgColor, bgIsPalette = gpu.getBackground()
|
|
||||||
-- it can happen during a type of race condition when a screen is removed
|
|
||||||
if not bgColor then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
fgColor, fgIsPalette = gpu.getForeground()
|
|
||||||
char_at_cursor = gpu.get(x, y)
|
|
||||||
end
|
|
||||||
|
|
||||||
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 and (done or tty.window.blink) then
|
|
||||||
gpu.set(x, y, char_at_cursor)
|
|
||||||
blinked = false
|
|
||||||
end
|
|
||||||
|
|
||||||
self.blink_cache = table.pack(blinked, bgColor, bgIsPalette, fgColor, fgIsPalette, char_at_cursor)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function tty.stream:pull(timeout, ...)
|
function tty.stream:pull(timeout, ...)
|
||||||
timeout = timeout or math.huge
|
timeout = timeout or math.huge
|
||||||
local blink_timeout = tty.window.blink and .5 or math.huge
|
local blink_timeout = tty.window.blink and .5 or math.huge
|
||||||
|
|
||||||
-- it can happen during a type of race condition when a screen is removed
|
local width, height, dx, dy, x, y = tty.getViewport()
|
||||||
if not self:blink() then
|
local gpu = tty.gpu()
|
||||||
return nil, "interrupted"
|
if x < 1 or x > width or y < 1 or y > height then
|
||||||
|
gpu = nil
|
||||||
|
end
|
||||||
|
local char_at_cursor
|
||||||
|
local blinked
|
||||||
|
if gpu then
|
||||||
|
blinked, char_at_cursor = pcall(gpu.get, x + dx, y + dy)
|
||||||
|
if not blinked then
|
||||||
|
return nil, "interrupted"
|
||||||
|
end
|
||||||
|
io.write("\0277\27[7m", char_at_cursor, "\0278")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- get the next event
|
-- get the next event
|
||||||
@ -152,7 +126,15 @@ function tty.stream:pull(timeout, ...)
|
|||||||
|
|
||||||
timeout = timeout - blink_timeout
|
timeout = timeout - blink_timeout
|
||||||
local done = signal.n > 1 or timeout < blink_timeout
|
local done = signal.n > 1 or timeout < blink_timeout
|
||||||
self:blink(done)
|
if gpu then
|
||||||
|
if not blinked and not done then
|
||||||
|
io.write("\0277\27[7m", char_at_cursor, "\0278")
|
||||||
|
blinked = true
|
||||||
|
elseif blinked and (done or tty.window.blink) then
|
||||||
|
io.write("\0277", char_at_cursor, "\0278")
|
||||||
|
blinked = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if done then
|
if done then
|
||||||
return table.unpack(signal, 1, signal.n)
|
return table.unpack(signal, 1, signal.n)
|
||||||
@ -336,27 +318,7 @@ function tty.stream:write(value)
|
|||||||
-- parse the instruction in segment
|
-- parse the instruction in segment
|
||||||
-- [ (%d+;)+ %d+m
|
-- [ (%d+;)+ %d+m
|
||||||
window.ansi_escape = window.ansi_escape .. value
|
window.ansi_escape = window.ansi_escape .. value
|
||||||
local color_attributes = {tonumber(window.ansi_escape:match("^%[(%d%d)m"))}
|
value, ansi_print = require("vt100").parse(window)
|
||||||
if not color_attributes[1] then
|
|
||||||
color_attributes, ansi_print, value = require("vt100").parse(window)
|
|
||||||
else
|
|
||||||
value = window.ansi_escape:sub(5)
|
|
||||||
end
|
|
||||||
for _,catt in ipairs(color_attributes) do
|
|
||||||
-- B6 is closer to cyan in 4 bit color
|
|
||||||
local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00B6ff,0xffffff}
|
|
||||||
catt = catt - 29
|
|
||||||
local method = "setForeground"
|
|
||||||
if catt > 10 then
|
|
||||||
method = "setBackground"
|
|
||||||
catt = catt - 10
|
|
||||||
end
|
|
||||||
local c = colors[catt]
|
|
||||||
if c then
|
|
||||||
gpu[method](c)
|
|
||||||
end
|
|
||||||
window.ansi_escape = nil -- might happen multiple times, that's fine
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- scroll before parsing next line
|
-- scroll before parsing next line
|
||||||
|
@ -1,190 +1,129 @@
|
|||||||
local text = require("text")
|
local text = require("text")
|
||||||
local vt100 = {}
|
|
||||||
|
|
||||||
-- runs patterns on ansi until failure
|
|
||||||
-- returns valid:boolean, completed_index:nil|number
|
|
||||||
function vt100.validate(ansi, patterns)
|
|
||||||
local last_index = 0
|
|
||||||
local captures = {}
|
|
||||||
for _,pattern in ipairs(patterns) do
|
|
||||||
if last_index >= #ansi then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
local si, ei, capture = ansi:find("^(" .. pattern .. ")", last_index + 1)
|
|
||||||
if not si then -- failed to match
|
|
||||||
return
|
|
||||||
end
|
|
||||||
captures[#captures + 1] = capture
|
|
||||||
last_index = ei
|
|
||||||
end
|
|
||||||
return true, last_index, captures
|
|
||||||
end
|
|
||||||
|
|
||||||
local rules = {}
|
local rules = {}
|
||||||
|
local vt100 = {rules=rules}
|
||||||
|
local full
|
||||||
|
|
||||||
-- colors
|
-- colors, blinking, and reverse
|
||||||
-- [%d+;%d+;..%d+m
|
-- [%d+;%d+;..%d+m
|
||||||
rules[{"%[", "[%d;]*", "m"}] = function(_, _, number_text)
|
-- cost: 2,250
|
||||||
local numbers = {}
|
rules[{"%[", "[%d;]*", "m"}] = function(window, _, number_text)
|
||||||
-- prefix and suffix ; act as reset
|
-- prefix and suffix ; act as reset
|
||||||
-- e.g. \27[41;m is actually 41 followed by a reset
|
-- e.g. \27[41;m is actually 41 followed by a reset
|
||||||
number_text = ";" .. number_text:gsub("^;$","") .. ";"
|
local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00B6ff,0xffffff}
|
||||||
local parts = text.split(number_text, {";"})
|
local fg, bg = window.gpu.setForeground, window.gpu.setBackground
|
||||||
|
if window.flip then
|
||||||
|
fg, bg = bg, fg
|
||||||
|
end
|
||||||
|
number_text = " _ " .. number_text:gsub("^;$", ""):gsub(";", " _ ") .. " _ "
|
||||||
|
local parts = text.internal.tokenize(number_text)
|
||||||
local last_was_break
|
local last_was_break
|
||||||
for _,part in ipairs(parts) do
|
for _,part in ipairs(parts) do
|
||||||
local num = tonumber(part)
|
local num = tonumber(part[1].txt)
|
||||||
if not num then
|
last_was_break, num = not num, num or last_was_break and 0
|
||||||
num = last_was_break and 0
|
|
||||||
last_was_break = true
|
if num == 7 then
|
||||||
else
|
if not window.flip then
|
||||||
last_was_break = false
|
fg(bg(window.gpu.getForeground()))
|
||||||
end
|
fg, bg = bg, fg
|
||||||
if num == 0 then
|
end
|
||||||
numbers[#numbers + 1] = 40
|
window.flip = true
|
||||||
numbers[#numbers + 1] = 37
|
elseif num == 5 then
|
||||||
|
window.blink = true
|
||||||
|
elseif num == 0 then
|
||||||
|
bg(colors[1])
|
||||||
|
fg(colors[8])
|
||||||
elseif num then
|
elseif num then
|
||||||
numbers[#numbers + 1] = num
|
num = num - 29
|
||||||
|
local set = fg
|
||||||
|
if num > 10 then
|
||||||
|
num = num - 10
|
||||||
|
set = bg
|
||||||
|
end
|
||||||
|
local color = colors[num]
|
||||||
|
if color then
|
||||||
|
set(color)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return numbers
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- [?7[hl] wrap mode
|
local function save_attributes(window, seven, s)
|
||||||
rules[{"%[", "%?", "7", "[hl]"}] = function(window, _, _, _, nowrap)
|
if seven == "7" or s == "s" then
|
||||||
window.nowrap = nowrap == "l"
|
window.saved =
|
||||||
end
|
{
|
||||||
|
window.x,
|
||||||
-- helper scroll function
|
window.y,
|
||||||
local function set_cursor(window, x, y)
|
{window.gpu.getBackground()},
|
||||||
window.x = math.min(math.max(x, 1), window.width)
|
{window.gpu.getForeground()},
|
||||||
window.y = math.min(math.max(y, 1), window.height)
|
window.flip,
|
||||||
end
|
window.blink
|
||||||
|
}
|
||||||
-- -- These DO NOT SCROLL
|
else
|
||||||
-- [(%d+)A move cursor up n lines
|
local data = window.saved or {1, 1, {0x0}, {0xffffff}, window.flip, window.blink}
|
||||||
-- [(%d+)B move cursor down n lines
|
|
||||||
-- [(%d+)C move cursor right n lines
|
|
||||||
-- [(%d+)D move cursor left n lines
|
|
||||||
rules[{"%[", "%d+", "[ABCD]"}] = function(window, _, n, dir)
|
|
||||||
local dx, dy = 0, 0
|
|
||||||
n = tonumber(n)
|
|
||||||
if dir == "A" then
|
|
||||||
dy = -n
|
|
||||||
elseif dir == "B" then
|
|
||||||
dy = n
|
|
||||||
elseif dir == "C" then
|
|
||||||
dx = n
|
|
||||||
else -- D
|
|
||||||
dx = -n
|
|
||||||
end
|
|
||||||
set_cursor(window, window.x + dx, window.y + dy)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- [Line;ColumnH Move cursor to screen location v,h
|
|
||||||
-- [Line;Columnf ^ same
|
|
||||||
rules[{"%[", "%d+", ";", "%d+", "[Hf]"}] = function(window, _, y, _, x)
|
|
||||||
set_cursor(window, tonumber(x), tonumber(y))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- [K clear line from cursor right
|
|
||||||
-- [0K ^ same
|
|
||||||
-- [1K clear line from cursor left
|
|
||||||
-- [2K clear entire line
|
|
||||||
local function clear_line(window, _, n)
|
|
||||||
n = tonumber(n) or 0
|
|
||||||
local x = n == 0 and window.x or 1
|
|
||||||
local rep = n == 1 and window.x or window.width
|
|
||||||
window.gpu.set(x, window.y, (" "):rep(rep))
|
|
||||||
end
|
|
||||||
rules[{"%[", "[012]?", "K"}] = clear_line
|
|
||||||
|
|
||||||
-- [J clear screen from cursor down
|
|
||||||
-- [0J ^ same
|
|
||||||
-- [1J clear screen from cursor up
|
|
||||||
-- [2J clear entire screen
|
|
||||||
rules[{"%[", "[012]?", "J"}] = function(window, _, n)
|
|
||||||
clear_line(window, _, n)
|
|
||||||
n = tonumber(n) or 0
|
|
||||||
local y = n == 0 and (window.y + 1) or 1
|
|
||||||
local rep = n == 1 and (window.y - 1) or window.height
|
|
||||||
window.gpu.fill(1, y, window.width, rep, " ")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- [H move cursor to upper left corner
|
|
||||||
-- [;H ^ same
|
|
||||||
-- [f ^ same
|
|
||||||
-- [;f ^ same
|
|
||||||
rules[{"%[;?", "[Hf]"}] = function(window)
|
|
||||||
set_cursor(window, 1, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ]
|
|
||||||
rules[{"%[", "6", "n"}] = function(window)
|
|
||||||
-- this solution puts the response on stdin, but it isn't echo'd
|
|
||||||
-- I'm personally fine with the lack of echo
|
|
||||||
io.stdin.bufferRead = string.format("%s%s%d;%dR", io.stdin.bufferRead, string.char(0x1b), window.y, window.x)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- D scroll up one line -- moves cursor down
|
|
||||||
-- E move to next line (acts the same ^, but x=1)
|
|
||||||
-- M scroll down one line -- moves cursor up
|
|
||||||
rules[{"[DEM]"}] = function(window, dir)
|
|
||||||
if dir == "D" then
|
|
||||||
window.y = window.y + 1
|
|
||||||
elseif dir == "E" then
|
|
||||||
window.y = window.y + 1
|
|
||||||
window.x = 1
|
|
||||||
else -- M
|
|
||||||
window.y = window.y - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function save_attributes(window, save)
|
|
||||||
if save then
|
|
||||||
local data = window.saved or {1, 1, {0x0}, {0xffffff}}
|
|
||||||
window.x = data[1]
|
window.x = data[1]
|
||||||
window.y = data[2]
|
window.y = data[2]
|
||||||
window.gpu.setBackground(table.unpack(data[3]))
|
window.gpu.setBackground(table.unpack(data[3]))
|
||||||
window.gpu.setForeground(table.unpack(data[4]))
|
window.gpu.setForeground(table.unpack(data[4]))
|
||||||
else
|
window.flip = data[5]
|
||||||
window.saved = {window.x, window.y, {window.gpu.getBackground()}, {window.gpu.getForeground()}}
|
window.blink = data[6]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 7 save cursor position and attributes
|
-- 7 save cursor position and attributes
|
||||||
-- 8 restore cursor position and attributes
|
-- 8 restore cursor position and attributes
|
||||||
rules[{"[78]"}] = function(window, restore)
|
rules[{"[78]"}] = save_attributes
|
||||||
save_attributes(window, restore == "8")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- s save cursor position
|
-- s save cursor position
|
||||||
-- u restore cursor position
|
-- u restore cursor position
|
||||||
rules[{"%[", "[su]"}] = function(window, _, restore)
|
rules[{"%[", "[su]"}] = save_attributes
|
||||||
save_attributes(window, restore == "u")
|
|
||||||
end
|
|
||||||
|
|
||||||
|
-- returns 2 values
|
||||||
|
-- value: parsed text
|
||||||
|
-- ansi_print: failed to parse
|
||||||
function vt100.parse(window)
|
function vt100.parse(window)
|
||||||
local ansi = window.ansi_escape
|
local ansi = window.ansi_escape
|
||||||
window.ansi_escape = nil
|
window.ansi_escape = nil
|
||||||
local any_valid
|
local any_valid
|
||||||
|
|
||||||
for rule,action in pairs(rules) do
|
for rule,action in pairs(rules) do
|
||||||
local ok, completed, captures = vt100.validate(ansi, rule)
|
local last_index = 0
|
||||||
if completed then
|
local captures = {}
|
||||||
return action(window, table.unpack(captures)) or {}, "", ansi:sub(completed + 1)
|
for _,pattern in ipairs(rule) do
|
||||||
elseif ok then
|
if last_index >= #ansi then
|
||||||
any_valid = true
|
any_valid = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
local si, ei, capture = ansi:find("^(" .. pattern .. ")", last_index + 1)
|
||||||
|
if not si then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
captures[#captures + 1] = capture
|
||||||
|
last_index = ei
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if #captures == #rule then
|
||||||
|
action(window, table.unpack(captures))
|
||||||
|
return ansi:sub(last_index + 1), ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not full then
|
||||||
|
-- maybe it did satisfy a rule, load more rules
|
||||||
|
full = true
|
||||||
|
dofile("/lib/core/full_vt.lua")
|
||||||
|
window.ansi_escape = ansi
|
||||||
|
return vt100.parse(window)
|
||||||
end
|
end
|
||||||
|
|
||||||
if not any_valid then
|
if not any_valid then
|
||||||
-- malformed
|
-- malformed
|
||||||
return {}, string.char(0x1b), ansi
|
return ansi, "\27"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- else, still consuming
|
-- else, still consuming
|
||||||
window.ansi_escape = ansi
|
window.ansi_escape = ansi
|
||||||
return {}, "", ""
|
return "", ""
|
||||||
end
|
end
|
||||||
|
|
||||||
return vt100
|
return vt100
|
||||||
|
@ -5,9 +5,12 @@ SYNOPSIS
|
|||||||
mount
|
mount
|
||||||
mount LABEL PATH
|
mount LABEL PATH
|
||||||
mount ADDRESS PATH
|
mount ADDRESS PATH
|
||||||
|
mount --bind PATH PATH
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
-r mount filesystem readonly
|
-r, --readonly mount filesystem readonly
|
||||||
|
--bind mount a bind point (folder to folder)
|
||||||
|
-h, --help print help message
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
All files accessible in OpenOS are arranged in one big tree, starting with the root node, '/'. The files are the leaves of the tree, directories are inner nodes of the tree. Files can be distributed across several devices (file system components, such as hard drives and floppies). The `mount` command is used to attach a file system to this tree. The `umount` command can be used to remove a mounted file system from the tree (note that `rm` works for this, too).
|
All files accessible in OpenOS are arranged in one big tree, starting with the root node, '/'. The files are the leaves of the tree, directories are inner nodes of the tree. Files can be distributed across several devices (file system components, such as hard drives and floppies). The `mount` command is used to attach a file system to this tree. The `umount` command can be used to remove a mounted file system from the tree (note that `rm` works for this, too).
|
||||||
@ -22,5 +25,8 @@ EXAMPLES
|
|||||||
mount 56f /var
|
mount 56f /var
|
||||||
Mounts the file system of which the address starts with `56f` at `/var`.
|
Mounts the file system of which the address starts with `56f` at `/var`.
|
||||||
|
|
||||||
mount -r tmpfs /tmp_ro
|
mount --readonly tmpfs /tmp_ro
|
||||||
Mounts a readonly access path of tmpfs to /tmp_ro
|
Mounts a readonly access path of tmpfs to /tmp_ro
|
||||||
|
|
||||||
|
mount --bind /mnt/fa4/home /home
|
||||||
|
Mounts /mnt/fa5/home to /home
|
||||||
|
Loading…
x
Reference in New Issue
Block a user