mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-16 02:39:48 -04:00
Merge branch 'master-MC1.7.10' into master-MC1.10
This commit is contained in:
commit
e2307316ee
@ -53,7 +53,5 @@ waila.version=1.6.0-B3_1.8.8
|
||||
wrcbe.version=2.0.0.19
|
||||
cofhcore.version=4.1.1.156
|
||||
|
||||
maven.url=file:///home/www/maven.cil.li/web
|
||||
|
||||
curse.project.id=223008
|
||||
curse.project.releaseType=release
|
||||
|
@ -23,6 +23,7 @@ options =
|
||||
{
|
||||
cmd = "cp",
|
||||
i = options.i,
|
||||
f = options.f,
|
||||
n = options.n,
|
||||
r = options.r,
|
||||
u = options.u,
|
||||
|
@ -68,8 +68,7 @@ local cols =
|
||||
end},
|
||||
{"HANDLES", function(_, p)
|
||||
local count = 0
|
||||
for stream,closure in pairs(p.data.handles) do
|
||||
cprint(string.format("%s %s", stream, closure))
|
||||
for _,closure in pairs(p.data.handles) do
|
||||
if closure then
|
||||
count = count + 1
|
||||
end
|
||||
|
@ -38,9 +38,11 @@ local function load(name, args)
|
||||
if result then
|
||||
rc.loaded[name] = env
|
||||
return env
|
||||
else
|
||||
return nil, string.format("%s failed to start: %s", fileName, reason)
|
||||
end
|
||||
end
|
||||
return nil, reason
|
||||
return nil, string.format("%s failed to load: %s", fileName, reason)
|
||||
end
|
||||
|
||||
function rc.unload(name)
|
||||
@ -116,24 +118,33 @@ local function allRunCommand(cmd, ...)
|
||||
return results
|
||||
end
|
||||
|
||||
if select("#", ...) == 0 then
|
||||
local results,reason = allRunCommand("start")
|
||||
local stream = io.stderr
|
||||
local write = stream.write
|
||||
|
||||
if select("#", ...) == 0 then
|
||||
-- if called during boot, pipe errors to onError handler
|
||||
if _G.runlevel == "S" then
|
||||
write = function(_, msg)
|
||||
require("event").onError(msg)
|
||||
end
|
||||
end
|
||||
|
||||
local results, reason = allRunCommand("start")
|
||||
if not results then
|
||||
local msg = "rc failed to start:"..tostring(reason)
|
||||
io.stderr:write(msg)
|
||||
require("event").onError(msg)
|
||||
write(stream, msg, "\n")
|
||||
return
|
||||
end
|
||||
for _, result in pairs(results) do
|
||||
local ok, reason = table.unpack(result)
|
||||
if not ok then
|
||||
io.stderr:write(reason, "\n")
|
||||
write(stream, reason, "\n")
|
||||
end
|
||||
end
|
||||
else
|
||||
local result, reason = runCommand(...)
|
||||
if not result then
|
||||
io.stderr:write(reason, "\n")
|
||||
write(stream, reason, "\n")
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
@ -23,7 +23,8 @@ if #args == 0 then
|
||||
end
|
||||
io.write(sh.expand(os.getenv("PS1") or "$ "))
|
||||
end
|
||||
local command = tty.read(input_handler)
|
||||
tty.window.cursor = input_handler
|
||||
local command = io.stdin:readLine(false)
|
||||
if command then
|
||||
command = text.trim(command)
|
||||
if command == "exit" then
|
||||
|
@ -32,14 +32,11 @@ end
|
||||
function print(...)
|
||||
local args = table.pack(...)
|
||||
local stdout = io.stdout
|
||||
local old_mode, old_size = stdout:setvbuf()
|
||||
stdout:setvbuf("line")
|
||||
local pre = ""
|
||||
for i = 1, args.n do
|
||||
stdout:write(pre, tostring(args[i]))
|
||||
pre = "\t"
|
||||
end
|
||||
stdout:write("\n")
|
||||
stdout:setvbuf(old_mode, old_size)
|
||||
stdout:flush()
|
||||
end
|
||||
|
@ -9,7 +9,7 @@ _G.coroutine = setmetatable(
|
||||
resume = function(co, ...)
|
||||
local proc = process.info(co)
|
||||
-- proc is nil if the process closed, natural resume will likely complain the coroutine is dead
|
||||
-- but if proc is dead and an aborted coroutine is alive, it doesn't have any proc data like stack info
|
||||
-- but if proc is dead and an orphan coroutine is alive, it doesn't have any proc data like stack info
|
||||
-- if the user really wants to resume it, let them
|
||||
return (proc and proc.data.coroutine_handler.resume or _coroutine.resume)(co, ...)
|
||||
end
|
||||
|
@ -1,19 +1,7 @@
|
||||
local computer = require("computer")
|
||||
local event = require("event")
|
||||
local fs = require("filesystem")
|
||||
local shell = require("shell")
|
||||
local info = require("process").info
|
||||
|
||||
os.execute = function(command)
|
||||
if not command then
|
||||
return type(shell) == "table"
|
||||
end
|
||||
return shell.execute(command)
|
||||
end
|
||||
|
||||
function os.exit(code)
|
||||
error({reason="terminated", code=code}, 0)
|
||||
end
|
||||
local event = require("event")
|
||||
|
||||
function os.getenv(varname)
|
||||
local env = info().data.vars
|
||||
@ -34,9 +22,6 @@ function os.setenv(varname, value)
|
||||
return value
|
||||
end
|
||||
|
||||
os.remove = fs.remove
|
||||
os.rename = fs.rename
|
||||
|
||||
function os.sleep(timeout)
|
||||
checkArg(1, timeout, "number", "nil")
|
||||
local deadline = computer.uptime() + (timeout or 0)
|
||||
@ -45,18 +30,6 @@ function os.sleep(timeout)
|
||||
until computer.uptime() >= deadline
|
||||
end
|
||||
|
||||
function os.tmpname()
|
||||
local path = os.getenv("TMPDIR") or "/tmp"
|
||||
if fs.exists(path) then
|
||||
for _ = 1, 10 do
|
||||
local name = fs.concat(path, tostring(math.random(1, 0x7FFFFFFF)))
|
||||
if not fs.exists(name) then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
os.setenv("PATH", "/bin:/usr/bin:/home/bin:.")
|
||||
os.setenv("TMP", "/tmp") -- Deprecated
|
||||
os.setenv("TMPDIR", "/tmp")
|
||||
@ -64,3 +37,5 @@ os.setenv("TMPDIR", "/tmp")
|
||||
if computer.tmpAddress() then
|
||||
fs.mount(computer.tmpAddress(), "/tmp")
|
||||
end
|
||||
|
||||
require("package").delay(os, "/lib/core/full_filesystem.lua")
|
||||
|
@ -17,7 +17,7 @@ local function onComponentAdded(_, address, componentType)
|
||||
end
|
||||
name = fs.concat("/mnt", name)
|
||||
fs.mount(proxy, name)
|
||||
if fs.isAutorunEnabled() then
|
||||
if not fs.exists("/etc/filesystem.cfg") or fs.isAutorunEnabled() then
|
||||
local file = shell.resolve(fs.concat(name, "autorun"), "lua") or
|
||||
shell.resolve(fs.concat(name, ".autorun"), "lua")
|
||||
if file then
|
||||
|
@ -1,4 +1,6 @@
|
||||
-- there doesn't seem to be a reason to update $HOSTNAME after the init signal
|
||||
-- as user space /etc/profile comes after this point anyways
|
||||
loadfile("/bin/hostname.lua")("--update")
|
||||
if require("filesystem").exists("/etc/hostname") then
|
||||
loadfile("/bin/hostname.lua")("--update")
|
||||
end
|
||||
os.setenv("SHELL","/bin/sh.lua")
|
||||
|
@ -8,10 +8,11 @@ local computer = computer
|
||||
local unicode = unicode
|
||||
|
||||
-- Runlevel information.
|
||||
local runlevel, shutdown = "S", computer.shutdown
|
||||
computer.runlevel = function() return runlevel end
|
||||
_G.runlevel = "S"
|
||||
local shutdown = computer.shutdown
|
||||
computer.runlevel = function() return _G.runlevel end
|
||||
computer.shutdown = function(reboot)
|
||||
runlevel = reboot and 6 or 0
|
||||
_G.runlevel = reboot and 6 or 0
|
||||
if os.sleep then
|
||||
computer.pushSignal("shutdown")
|
||||
os.sleep(0.1) -- Allow shutdown processing.
|
||||
|
@ -0,0 +1,251 @@
|
||||
local unicode = require("unicode")
|
||||
local kb = require("keyboard")
|
||||
local tty = require("tty")
|
||||
local text = require("text")
|
||||
local computer = require("computer")
|
||||
local keys = kb.keys
|
||||
|
||||
local core_cursor = {}
|
||||
|
||||
core_cursor.vertical = {}
|
||||
|
||||
function core_cursor.vertical:move(n)
|
||||
local s = math.max(math.min(self.index + n, self.len), 0)
|
||||
if s == self.index then return end
|
||||
local echo_cmd = keys.left
|
||||
local from = s + 1
|
||||
local to = self.index
|
||||
if s > self.index then
|
||||
echo_cmd, from, to = keys.right, to + 1, s
|
||||
end
|
||||
self.index = s
|
||||
local step = unicode.wlen(unicode.sub(self.data, from, to))
|
||||
self:echo(echo_cmd, step)
|
||||
end
|
||||
|
||||
-- back is used when arg comes after the cursor
|
||||
function core_cursor.vertical:update(arg, back)
|
||||
if not arg then
|
||||
self.tails = {}
|
||||
self.data = ""
|
||||
self.index = 0
|
||||
self.sy = 0
|
||||
self.hindex = 0
|
||||
end
|
||||
local s1 = unicode.sub(self.data, 1, self.index)
|
||||
local s2 = unicode.sub(self.data, self.index + 1)
|
||||
if type(arg) == "string" then
|
||||
if back == false then
|
||||
arg, s2 = arg .. s2, ""
|
||||
else
|
||||
self.index = self.index + unicode.len(arg)
|
||||
self:echo(arg)
|
||||
end
|
||||
self.data = s1 .. arg
|
||||
elseif arg then -- number
|
||||
local has_tail = arg < 0 or #s2 > 0
|
||||
if arg < 0 then
|
||||
-- backspace? ignore if at start
|
||||
if self.index <= 0 then return end
|
||||
self:move(arg)
|
||||
s1 = unicode.sub(s1, 1, -1 + arg)
|
||||
else
|
||||
-- forward? ignore if at end
|
||||
if self.index >= self.len then return end
|
||||
s2 = unicode.sub(s2, 1 + arg)
|
||||
end
|
||||
self.data = s1
|
||||
if has_tail then
|
||||
self:echo(self.clear)
|
||||
end
|
||||
end
|
||||
self.len = unicode.len(self.data) -- recompute len
|
||||
self:move(back or 0)
|
||||
if #s2 > 0 then
|
||||
self:update(s2, -unicode.len(s2))
|
||||
end
|
||||
end
|
||||
|
||||
function core_cursor.vertical:echo(arg, num)
|
||||
local win = tty.window
|
||||
win.nowrap = self.nowrap
|
||||
if arg == "" then -- special scroll request
|
||||
local gpu, width, x, y = win.gpu, win.width, win.x, win.y
|
||||
if x > width then
|
||||
win.x = ((x - 1) % width) + 1
|
||||
win.y = y + math.floor(x / width)
|
||||
io.write("") -- tty.stream:write knows how to scroll vertically
|
||||
x, y = win.x, win.y
|
||||
end
|
||||
if x <= 0 or y <= 0 or y > win.height or not gpu then return end
|
||||
return table.pack(select(2, pcall(gpu.get, x + win.dx, y + win.dy)))
|
||||
elseif arg == keys.left then
|
||||
local x = win.x - num
|
||||
local y = win.y
|
||||
while x < 1 do
|
||||
x = x + win.width - #(self.tails[win.dy + y - self.sy - 1] or "")
|
||||
y = y - 1
|
||||
end
|
||||
win.x, win.y = x, y
|
||||
arg = ""
|
||||
elseif arg == keys.right then
|
||||
local x = win.x + num
|
||||
local y = win.y
|
||||
while true do
|
||||
local width = win.width - #(self.tails[win.dy + y - self.sy] or "")
|
||||
if x <= width then break end
|
||||
x = x - width
|
||||
y = y + 1
|
||||
end
|
||||
win.x, win.y = x, y
|
||||
arg = ""
|
||||
elseif not arg or arg == true then -- blink
|
||||
local gpu = win.gpu
|
||||
local char = self.char_at_cursor
|
||||
if (arg == nil and not char) or (arg and not self.blinked) then
|
||||
char = char or self:echo("") --scroll and get char
|
||||
if not char[1] then return false end
|
||||
self.blinked = true
|
||||
if not arg then
|
||||
io.write("\0277")
|
||||
char.saved = win.saved
|
||||
gpu.setForeground(char[4] or char[2], not not char[4])
|
||||
gpu.setBackground(char[5] or char[3], not not char[5])
|
||||
end
|
||||
io.write("\0277", "\27[7m", char[1], "\0278")
|
||||
elseif (arg and self.blinked) or arg == false then
|
||||
self.blinked = false
|
||||
gpu.set(win.x + win.dx, win.y + win.dy, char[1])
|
||||
if not arg then
|
||||
win.saved = char.saved
|
||||
io.write("\0278")
|
||||
char = nil
|
||||
end
|
||||
end
|
||||
self.char_at_cursor = char
|
||||
return true
|
||||
end
|
||||
return io.write(arg)
|
||||
end
|
||||
|
||||
function core_cursor.vertical:handle(name, char, code)
|
||||
if name == "clipboard" then
|
||||
self.cache = nil -- this stops tab completion
|
||||
local newline = char:find("\10") or #char
|
||||
local printable_prefix, remainder = char:sub(1, newline), char:sub(newline + 1)
|
||||
self:update(printable_prefix)
|
||||
self:update(remainder, false)
|
||||
elseif name == "touch" or name == "drag" then
|
||||
core_cursor.touch(self, char, code)
|
||||
elseif name == "interrupted" then
|
||||
self:echo("^C\n")
|
||||
return false, name
|
||||
elseif name == "key_down" then
|
||||
local data = self.data
|
||||
local backup_cache = self.cache
|
||||
self.cache = nil
|
||||
local ctrl = kb.isControlDown()
|
||||
if ctrl and code == keys.d then
|
||||
return --nil:close
|
||||
elseif code == keys.tab then
|
||||
self.cache = backup_cache
|
||||
core_cursor.tab(self)
|
||||
elseif code == keys.enter or code == keys.numpadenter then
|
||||
self:move(self.len)
|
||||
self:update("\n")
|
||||
elseif code == keys.up or code == keys.down then
|
||||
local ni = self.hindex + (code == keys.up and 1 or -1)
|
||||
if ni >= 0 and ni <= #self then
|
||||
self[self.hindex] = data
|
||||
self.hindex = ni
|
||||
self:move(self.len)
|
||||
self:update(-self.len)
|
||||
self:update(self[ni])
|
||||
end
|
||||
elseif code == keys.left or code == keys.back or code == keys.w and ctrl then
|
||||
local value = ctrl and ((unicode.sub(data, 1, self.index):find("%s[^%s]+%s*$") or 0) - self.index) or -1
|
||||
if code == keys.left then
|
||||
self:move(value)
|
||||
else
|
||||
self:update(value)
|
||||
end
|
||||
elseif code == keys.right then
|
||||
self:move(ctrl and ((data:find("%s[^%s]", self.index + 1) or self.len) - self.index) or 1)
|
||||
elseif code == keys.home then self:move(-self.len)
|
||||
elseif code == keys["end"] then self:move( self.len)
|
||||
elseif code == keys.delete then self:update(1)
|
||||
elseif char >= 32 then self:update(unicode.char(char))
|
||||
else self.cache = backup_cache -- ignored chars shouldn't clear hint cache
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- echo'd to clear the input text in the tty
|
||||
core_cursor.vertical.clear = "\27[J"
|
||||
|
||||
function core_cursor.new(base, index)
|
||||
-- if base has defined any methods, those are called first
|
||||
-- any new methods here are "super" methods to base
|
||||
base = base or {}
|
||||
base.super = base.super or index or core_cursor.vertical
|
||||
setmetatable(base, getmetatable(base) or { __index = base.super })
|
||||
if not base.data then
|
||||
base:update()
|
||||
end
|
||||
return base
|
||||
end
|
||||
|
||||
function core_cursor.read(cursor)
|
||||
local last = cursor.next or ""
|
||||
cursor.next = nil
|
||||
if #last > 0 then
|
||||
cursor:handle("clipboard", last)
|
||||
end
|
||||
|
||||
-- address checks
|
||||
local address_check =
|
||||
{
|
||||
key_down = tty.keyboard,
|
||||
clipboard = tty.keyboard,
|
||||
touch = tty.screen,
|
||||
drag = tty.screen,
|
||||
drop = tty.screen
|
||||
}
|
||||
|
||||
while true do
|
||||
local next_line = cursor.data:find("\10")
|
||||
if next_line then
|
||||
local result = cursor.data:sub(1, next_line)
|
||||
local overflow = cursor.data:sub(next_line + 1)
|
||||
local history = text.trim(result)
|
||||
if history ~= "" and history ~= cursor[1] then
|
||||
table.insert(cursor, 1, history)
|
||||
cursor[(tonumber(os.getenv("HISTSIZE")) or 10) + 1] = nil
|
||||
end
|
||||
cursor[0] = nil
|
||||
cursor:update()
|
||||
cursor.next = overflow
|
||||
return result
|
||||
end
|
||||
|
||||
cursor:echo()
|
||||
local pack = table.pack(computer.pullSignal(tty.window.blink and .5 or math.huge))
|
||||
local name = pack[1]
|
||||
cursor:echo(not name)
|
||||
|
||||
if name then
|
||||
local filter_address = address_check[name]
|
||||
if not filter_address or filter_address() == pack[2] then
|
||||
local ret, why = cursor:handle(name, table.unpack(pack, 3, pack.n))
|
||||
if not ret then
|
||||
return ret, why
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require("package").delay(core_cursor, "/lib/core/full_cursor.lua")
|
||||
|
||||
return core_cursor
|
@ -18,7 +18,7 @@ return
|
||||
},
|
||||
null =
|
||||
{
|
||||
open = function(mode)
|
||||
open = function()
|
||||
return
|
||||
{
|
||||
read = function() end,
|
||||
@ -34,9 +34,9 @@ return
|
||||
end
|
||||
return
|
||||
{
|
||||
read = function(self, n)
|
||||
read = function(_, n)
|
||||
local chars = {}
|
||||
for i=1,n do
|
||||
for _=1,n do
|
||||
table.insert(chars,string.char(math.random(0,255)))
|
||||
end
|
||||
return table.concat(chars)
|
||||
@ -47,4 +47,17 @@ return
|
||||
return math.huge
|
||||
end
|
||||
},
|
||||
zero =
|
||||
{
|
||||
open = function()
|
||||
return
|
||||
{
|
||||
read = function(_, n) return ("\0"):rep(n) end,
|
||||
write = function() end
|
||||
}
|
||||
end,
|
||||
size = function()
|
||||
return math.huge
|
||||
end
|
||||
},
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ function buffer:readBytesOrChars(readChunk, n)
|
||||
sub = unicode.sub
|
||||
end
|
||||
local data = ""
|
||||
repeat
|
||||
while len(data) ~= n do
|
||||
if len(self.bufferRead) == 0 then
|
||||
local result, reason = readChunk(self)
|
||||
if not result then
|
||||
@ -156,7 +156,7 @@ function buffer:readBytesOrChars(readChunk, n)
|
||||
local left = n - len(data)
|
||||
data = data .. sub(self.bufferRead, 1, left)
|
||||
self.bufferRead = sub(self.bufferRead, left + 1)
|
||||
until len(data) == n
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
|
@ -0,0 +1,123 @@
|
||||
local core_cursor = require("core/cursor")
|
||||
local unicode = require("unicode")
|
||||
local kb = require("keyboard")
|
||||
local tty = require("tty")
|
||||
|
||||
core_cursor.horizontal = {}
|
||||
|
||||
function core_cursor.touch(cursor, gx, gy)
|
||||
if cursor.len > 0 then
|
||||
local win = tty.window
|
||||
gx, gy = gx - win.dx, gy - win.dy
|
||||
while true do
|
||||
local x, y, d = win.x, win.y, win.width
|
||||
local dx = ((gy*d+gx)-(y*d+x))
|
||||
if dx == 1 then
|
||||
dx = unicode.wlen(unicode.sub(cursor.data, cursor.index + 1, cursor.index + 1)) == 2 and 0 or dx
|
||||
end
|
||||
if dx == 0 then
|
||||
break
|
||||
end
|
||||
cursor:move(dx > 0 and 1 or -1)
|
||||
if x == win.x and y == win.y then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function core_cursor.tab(cursor)
|
||||
local hints = cursor.hint
|
||||
if not hints then return end
|
||||
if not cursor.cache then
|
||||
cursor.cache =
|
||||
type(hints) == "table" and hints or
|
||||
hints(cursor.data, cursor.index + 1) or
|
||||
{}
|
||||
cursor.cache.i = -1
|
||||
end
|
||||
|
||||
local cache = cursor.cache
|
||||
|
||||
if #cache == 1 and cache.i == 0 then
|
||||
-- there was only one solution, and the user is asking for the next
|
||||
cursor.cache = hints(cache[1], cursor.index + 1)
|
||||
if not cursor.cache then return end
|
||||
cursor.cache.i = -1
|
||||
cache = cursor.cache
|
||||
end
|
||||
|
||||
local change = kb.isShiftDown() and -1 or 1
|
||||
cache.i = (cache.i + change) % math.max(#cache, 1)
|
||||
local next = cache[cache.i + 1]
|
||||
if next then
|
||||
local tail = unicode.len(cursor.data) - cursor.index
|
||||
cursor:move(cursor.len)
|
||||
cursor:update(-cursor.len)
|
||||
cursor:update(next, -tail)
|
||||
end
|
||||
end
|
||||
|
||||
function core_cursor.horizontal:scroll(num, final_index)
|
||||
self:move(self.vindex - self.index) -- go to left edge
|
||||
-- shift (v)index by num
|
||||
self.vindex = self.vindex + num
|
||||
self.index = self.index + num
|
||||
|
||||
self:echo("\0277".. -- remember the location
|
||||
unicode.sub(self.data, self.index + 1).. -- write part after
|
||||
"\27[K\0278") -- clear tail and restore left edge
|
||||
|
||||
self:move(final_index - self.index) -- move to final_index location
|
||||
end
|
||||
|
||||
function core_cursor.horizontal:echo(arg, num)
|
||||
local w = tty.window
|
||||
w.nowrap = self.nowrap
|
||||
if arg == "" then -- special scroll request
|
||||
local width = w.width
|
||||
if w.x >= width then
|
||||
-- the width that matters depends on the following char width
|
||||
width = width - math.max(unicode.wlen(unicode.sub(self.data, self.index + 1, self.index + 1)) - 1, 0)
|
||||
if w.x > width then
|
||||
local s1 = unicode.sub(self.data, self.vindex + 1, self.index)
|
||||
self:scroll(unicode.len(unicode.wtrunc(s1, w.x - width + 1)), self.index)
|
||||
end
|
||||
end
|
||||
-- scroll is safe now, return as normal below
|
||||
elseif arg == kb.keys.left then
|
||||
if self.index < self.vindex then
|
||||
local s2 = unicode.sub(self.data, self.index + 1)
|
||||
w.x = w.x - num + unicode.wlen(unicode.sub(s2, 1, self.vindex - self.index))
|
||||
local current_x = w.x
|
||||
self:echo(s2)
|
||||
w.x = current_x
|
||||
self.vindex = self.index
|
||||
return true
|
||||
end
|
||||
elseif arg == kb.keys.right then
|
||||
w.x = w.x + num
|
||||
return self:echo("") -- scroll
|
||||
end
|
||||
return core_cursor.vertical.echo(self, arg, num)
|
||||
end
|
||||
|
||||
function core_cursor.horizontal:update(arg, back)
|
||||
if back then
|
||||
-- if we're just going to render arg and move back, and we're not wrapping, just render arg
|
||||
-- back may be more or less from current x
|
||||
self:update(arg, false)
|
||||
local x = tty.window.x
|
||||
self:echo(arg) -- nowrap echo
|
||||
tty.window.x = x
|
||||
self:move(self.len - self.index + back) -- back is negative from end
|
||||
return true
|
||||
elseif not arg then -- reset
|
||||
self.nowrap = true
|
||||
self.clear = "\27[K"
|
||||
self.vindex = 0 -- visual/virtual index
|
||||
end
|
||||
return core_cursor.vertical.update(self, arg, back)
|
||||
end
|
||||
|
||||
setmetatable(core_cursor.horizontal, { __index = core_cursor.vertical })
|
@ -59,3 +59,17 @@ function event.ignore(name, callback)
|
||||
return false
|
||||
end
|
||||
|
||||
function event.onError(message)
|
||||
local log = io.open("/tmp/event.log", "a")
|
||||
if log then
|
||||
pcall(log.write, log, tostring(message), "\n")
|
||||
log:close()
|
||||
end
|
||||
end
|
||||
|
||||
function event.timer(interval, callback, times)
|
||||
checkArg(1, interval, "number")
|
||||
checkArg(2, callback, "function")
|
||||
checkArg(3, times, "number", "nil")
|
||||
return event.register(false, callback, interval, times)
|
||||
end
|
||||
|
@ -1,5 +1,6 @@
|
||||
local filesystem = require("filesystem")
|
||||
local component = require("component")
|
||||
local shell = require("shell")
|
||||
|
||||
function filesystem.makeDirectory(path)
|
||||
if filesystem.exists(path) then
|
||||
@ -179,8 +180,8 @@ local function bind_proxy(path)
|
||||
-- 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), ...)
|
||||
return function(mpath, ...)
|
||||
return fp(filesystem.concat(rest, mpath), ...)
|
||||
end
|
||||
end
|
||||
local bind = {
|
||||
@ -236,3 +237,126 @@ function filesystem.internal.proxy(filter, options)
|
||||
end
|
||||
return proxy
|
||||
end
|
||||
|
||||
function filesystem.remove(path)
|
||||
local function removeVirtual()
|
||||
local _, _, vnode, vrest = filesystem.findNode(filesystem.path(path), false, true)
|
||||
-- vrest represents the remaining path beyond vnode
|
||||
-- vrest is nil if vnode reaches the full path
|
||||
-- thus, if vrest is NOT NIL, then we SHOULD NOT remove children nor links
|
||||
if not vrest then
|
||||
local name = filesystem.name(path)
|
||||
if vnode.children[name] or vnode.links[name] then
|
||||
vnode.children[name] = nil
|
||||
vnode.links[name] = nil
|
||||
while vnode and vnode.parent and not vnode.fs and not next(vnode.children) and not next(vnode.links) do
|
||||
vnode.parent.children[vnode.name] = nil
|
||||
vnode = vnode.parent
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- return false even if vrest is nil because this means it was a expected
|
||||
-- to be a real file
|
||||
return false
|
||||
end
|
||||
local function removePhysical()
|
||||
local node, rest = filesystem.findNode(path)
|
||||
if node.fs and rest then
|
||||
return node.fs.remove(rest)
|
||||
end
|
||||
return false
|
||||
end
|
||||
local success = removeVirtual()
|
||||
success = removePhysical() or success -- Always run.
|
||||
if success then return true
|
||||
else return nil, "no such file or directory"
|
||||
end
|
||||
end
|
||||
|
||||
function filesystem.rename(oldPath, newPath)
|
||||
if filesystem.isLink(oldPath) then
|
||||
local _, _, vnode, _ = filesystem.findNode(filesystem.path(oldPath))
|
||||
local target = vnode.links[filesystem.name(oldPath)]
|
||||
local result, reason = filesystem.link(target, newPath)
|
||||
if result then
|
||||
filesystem.remove(oldPath)
|
||||
end
|
||||
return result, reason
|
||||
else
|
||||
local oldNode, oldRest = filesystem.findNode(oldPath)
|
||||
local newNode, newRest = filesystem.findNode(newPath)
|
||||
if oldNode.fs and oldRest and newNode.fs and newRest then
|
||||
if oldNode.fs.address == newNode.fs.address then
|
||||
return oldNode.fs.rename(oldRest, newRest)
|
||||
else
|
||||
local result, reason = filesystem.copy(oldPath, newPath)
|
||||
if result then
|
||||
return filesystem.remove(oldPath)
|
||||
else
|
||||
return nil, reason
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, "trying to read from or write to virtual directory"
|
||||
end
|
||||
end
|
||||
|
||||
local isAutorunEnabled = nil
|
||||
local function saveConfig()
|
||||
local root = filesystem.get("/")
|
||||
if root and not root.isReadOnly() then
|
||||
local f = filesystem.open("/etc/filesystem.cfg", "w")
|
||||
if f then
|
||||
f:write("autorun="..tostring(isAutorunEnabled))
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function filesystem.isAutorunEnabled()
|
||||
if isAutorunEnabled == nil then
|
||||
local env = {}
|
||||
local config = loadfile("/etc/filesystem.cfg", nil, env)
|
||||
if config then
|
||||
pcall(config)
|
||||
isAutorunEnabled = not not env.autorun
|
||||
else
|
||||
isAutorunEnabled = true
|
||||
end
|
||||
saveConfig()
|
||||
end
|
||||
return isAutorunEnabled
|
||||
end
|
||||
|
||||
function filesystem.setAutorunEnabled(value)
|
||||
checkArg(1, value, "boolean")
|
||||
isAutorunEnabled = value
|
||||
saveConfig()
|
||||
end
|
||||
|
||||
os.remove = filesystem.remove
|
||||
os.rename = filesystem.rename
|
||||
|
||||
os.execute = function(command)
|
||||
if not command then
|
||||
return type(shell) == "table"
|
||||
end
|
||||
return shell.execute(command)
|
||||
end
|
||||
|
||||
function os.exit(code)
|
||||
error({reason="terminated", code=code}, 0)
|
||||
end
|
||||
|
||||
function os.tmpname()
|
||||
local path = os.getenv("TMPDIR") or "/tmp"
|
||||
if filesystem.exists(path) then
|
||||
for _ = 1, 10 do
|
||||
local name = filesystem.concat(path, tostring(math.random(1, 0x7FFFFFFF)))
|
||||
if not filesystem.exists(name) then
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -36,27 +36,6 @@ end
|
||||
local ec = 0
|
||||
local fOut = tty.isAvailable() and io.output().tty
|
||||
local function perr(msg) io.stderr:write(msg,"\n") ec = 2 end
|
||||
local function stat(names, index)
|
||||
local name = names[index]
|
||||
if type(name) == "table" then
|
||||
return name
|
||||
end
|
||||
local info = {}
|
||||
info.key = name
|
||||
info.path = name:sub(1, 1) == "/" and "" or names.path
|
||||
info.full_path = fs.concat(info.path, name)
|
||||
info.isDir = fs.isDirectory(info.full_path)
|
||||
info.name = name:gsub("/+$", "") .. (ops.p and info.isDir and "/" or "")
|
||||
info.sort_name = info.name:gsub("^%.","")
|
||||
info.isLink, info.link = fs.isLink(info.full_path)
|
||||
info.size = info.isLink and 0 or fs.size(info.full_path)
|
||||
info.time = fs.lastModified(info.full_path)
|
||||
info.fs = fs.get(info.full_path)
|
||||
info.ext = info.name:match("(%.[^.]+)$") or ""
|
||||
names[index] = info
|
||||
return info
|
||||
end
|
||||
local function toArray(i) local r={} for n in i do r[#r+1]=n end return r end
|
||||
local set_color = function() end
|
||||
local function colorize() return end
|
||||
if fOut and not ops["no-color"] then
|
||||
@ -89,7 +68,7 @@ function msft.tail(names)
|
||||
end
|
||||
local totalSize, totalFiles, totalDirs = 0, 0, 0
|
||||
for i=1,#names do
|
||||
local info = stat(names, i)
|
||||
local info = names[i]
|
||||
if info.isDir then
|
||||
totalDirs = totalDirs + 1
|
||||
else
|
||||
@ -168,15 +147,10 @@ local function filter(names)
|
||||
if ops.a then
|
||||
return names
|
||||
end
|
||||
local set = {}
|
||||
for key, value in pairs(names) do
|
||||
if type(key) == "number" then
|
||||
local info = stat(names, key)
|
||||
if fs.name(info.name):sub(1, 1) ~= "." then
|
||||
table.insert(set, names[key])
|
||||
end
|
||||
else
|
||||
set[key] = value
|
||||
local set = { path = names.path }
|
||||
for _, info in ipairs(names) do
|
||||
if fs.name(info.name):sub(1, 1) ~= "." then
|
||||
set[#set + 1] = info
|
||||
end
|
||||
end
|
||||
return set
|
||||
@ -185,10 +159,9 @@ end
|
||||
local function sort(names)
|
||||
local once = false
|
||||
local function ni(v)
|
||||
local vname = type(v) == "string" and v or v.key
|
||||
for i=1,#names do
|
||||
local info = stat(names, i)
|
||||
if info.key == vname then
|
||||
local info = names[i]
|
||||
if info.key == v.key then
|
||||
return i
|
||||
end
|
||||
end
|
||||
@ -196,8 +169,8 @@ local function sort(names)
|
||||
local function sorter(key)
|
||||
once = true
|
||||
table.sort(names, function(a, b)
|
||||
local ast = stat(names, ni(a))
|
||||
local bst = stat(names, ni(b))
|
||||
local ast = names[ni(a)]
|
||||
local bst = names[ni(b)]
|
||||
return ast[key] > bst[key]
|
||||
end)
|
||||
end
|
||||
@ -218,7 +191,7 @@ local function dig(names, dirs, dir)
|
||||
if ops.R then
|
||||
local di = 1
|
||||
for i=1,#names do
|
||||
local info = stat(names, i)
|
||||
local info = names[i]
|
||||
if info.isDir then
|
||||
local path = dir..(dir:sub(-1) == "/" and "" or "/")
|
||||
table.insert(dirs, di, path..info.name)
|
||||
@ -238,12 +211,12 @@ local function display(names)
|
||||
local max_size_width = 1
|
||||
local max_date_width = 0
|
||||
for i=1,lines.n do
|
||||
local info = stat(names, i)
|
||||
local info = names[i]
|
||||
max_size_width = math.max(max_size_width, formatSize(info.size):len())
|
||||
max_date_width = math.max(max_date_width, formatDate(info.time):len())
|
||||
end
|
||||
mt.__index = function(_, index)
|
||||
local info = stat(names, index)
|
||||
local info = names[index]
|
||||
local file_type = info.isLink and 'l' or info.isDir and 'd' or 'f'
|
||||
local link_target = info.isLink and string.format(" -> %s", info.link:gsub("/+$", "") .. (info.isDir and "/" or "")) or ""
|
||||
local write_mode = info.fs.isReadOnly() and '-' or 'w'
|
||||
@ -257,7 +230,7 @@ local function display(names)
|
||||
elseif ops["1"] or not fOut then
|
||||
lines.n = #names
|
||||
mt.__index = function(_, index)
|
||||
local info = stat(names, index)
|
||||
local info = names[index]
|
||||
return {{color = colorize(info), name = info.name}}
|
||||
end
|
||||
else -- columns
|
||||
@ -271,7 +244,7 @@ local function display(names)
|
||||
for r=1,items_per_column do
|
||||
local ri = real(column_index, r)
|
||||
if not ri then break end
|
||||
local info = stat(names, ri)
|
||||
local info = names[ri]
|
||||
max = math.max(max, unicode.wlen(info.name))
|
||||
end
|
||||
return max
|
||||
@ -297,7 +270,7 @@ local function display(names)
|
||||
__index=function(_, column_index)
|
||||
local ri = real(column_index, line_index)
|
||||
if not ri then return end
|
||||
local info = stat(names, ri)
|
||||
local info = names[ri]
|
||||
local name = info.name
|
||||
return {color = colorize(info), name = name .. string.rep(' ', max_name(column_index) - unicode.wlen(name) + (column_index < num_columns and 2 or 0))}
|
||||
end,
|
||||
@ -313,6 +286,7 @@ local function display(names)
|
||||
set_color(e.color)
|
||||
io.write(e.name)
|
||||
end
|
||||
set_color()
|
||||
print()
|
||||
end
|
||||
msft.tail(names)
|
||||
@ -325,6 +299,23 @@ if #dirsArg > 1 or ops.R then
|
||||
io.write(path,":\n")
|
||||
end
|
||||
end
|
||||
|
||||
local function stat(path, name)
|
||||
local info = {}
|
||||
info.key = name
|
||||
info.path = name:sub(1, 1) == "/" and "" or path
|
||||
info.full_path = fs.concat(info.path, name)
|
||||
info.isDir = fs.isDirectory(info.full_path)
|
||||
info.name = name:gsub("/+$", "") .. (ops.p and info.isDir and "/" or "")
|
||||
info.sort_name = info.name:gsub("^%.","")
|
||||
info.isLink, info.link = fs.isLink(info.full_path)
|
||||
info.size = info.isLink and 0 or fs.size(info.full_path)
|
||||
info.time = fs.lastModified(info.full_path)
|
||||
info.fs = fs.get(info.full_path)
|
||||
info.ext = info.name:match("(%.[^.]+)$") or ""
|
||||
return info
|
||||
end
|
||||
|
||||
local function displayDirList(dirs)
|
||||
while #dirs > 0 do
|
||||
local dir = table.remove(dirs, 1)
|
||||
@ -334,8 +325,10 @@ local function displayDirList(dirs)
|
||||
if not list then
|
||||
perr(reason)
|
||||
else
|
||||
local names = toArray(list)
|
||||
names.path = path
|
||||
local names = { path = path }
|
||||
for name in list do
|
||||
names[#names + 1] = stat(path, name)
|
||||
end
|
||||
display(dig(sort(filter(names)), dirs, dir))
|
||||
end
|
||||
end
|
||||
@ -352,7 +345,7 @@ for _,dir in ipairs(dirsArg) do
|
||||
elseif fs.isDirectory(path) then
|
||||
table.insert(dir_set, dir)
|
||||
else -- file or link
|
||||
table.insert(file_set, dir)
|
||||
table.insert(file_set, stat(dir, dir))
|
||||
end
|
||||
end
|
||||
|
||||
@ -366,7 +359,6 @@ end)
|
||||
|
||||
io.output():flush()
|
||||
io.output():setvbuf("no")
|
||||
set_color()
|
||||
|
||||
assert(ok, msg)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
local text = require("text")
|
||||
local tx = require("transforms")
|
||||
local unicode = require("unicode")
|
||||
local process = require("process")
|
||||
|
||||
-- separate string value into an array of words delimited by whitespace
|
||||
-- groups by quotes
|
||||
@ -91,7 +92,7 @@ function text.internal.splitWords(words, delimiters)
|
||||
add_part(part)
|
||||
else
|
||||
local part_text_splits = text.split(part.txt, delimiters)
|
||||
tx.foreach(part_text_splits, function(sub_txt, spi)
|
||||
tx.foreach(part_text_splits, function(sub_txt)
|
||||
local delim = #text.split(sub_txt, delimiters, true) == 0
|
||||
next_word = next_word or delim
|
||||
add_part({txt=sub_txt,qr=qr})
|
||||
@ -177,7 +178,8 @@ function text.internal.reader(txt, mode)
|
||||
_.txt = nil
|
||||
return true
|
||||
end,
|
||||
}, {__index=text.internal.stream_base(mode:match("b"))})
|
||||
}, {__index=text.internal.stream_base((mode or ""):match("b"))})
|
||||
process.closeOnExit(reader)
|
||||
|
||||
return require("buffer").new("r", reader)
|
||||
end
|
||||
@ -201,7 +203,7 @@ function text.internal.writer(ostream, mode, append_txt)
|
||||
local pre = _.psub(_.txt, 1, _.index)
|
||||
local vs = {}
|
||||
local pos = _.psub(_.txt, _.index + 1)
|
||||
for i,v in ipairs({...}) do
|
||||
for _,v in ipairs({...}) do
|
||||
table.insert(vs, v)
|
||||
end
|
||||
vs = table.concat(vs)
|
||||
@ -218,7 +220,8 @@ function text.internal.writer(ostream, mode, append_txt)
|
||||
_.txt = nil
|
||||
return true
|
||||
end,
|
||||
}, {__index=text.internal.stream_base(mode:match("b"))})
|
||||
}, {__index=text.internal.stream_base((mode or ""):match("b"))})
|
||||
process.closeOnExit(writer)
|
||||
|
||||
return require("buffer").new("w", writer)
|
||||
end
|
||||
|
@ -1,87 +0,0 @@
|
||||
local tty = require("tty")
|
||||
local unicode = require("unicode")
|
||||
local kb = require("keyboard")
|
||||
|
||||
function tty.touch_handler(_, cursor, gx, gy)
|
||||
if cursor.data == "" then
|
||||
return false
|
||||
end
|
||||
cursor:move(-math.huge)
|
||||
local win = tty.window
|
||||
gx, gy = gx - win.dx, gy - win.dy
|
||||
local x2, y2, d = win.x, win.y, win.width
|
||||
local char_width_to_move = ((gy*d+gx)-(y2*d+x2))
|
||||
if char_width_to_move <= 0 then
|
||||
return false
|
||||
end
|
||||
local total_wlen = unicode.wlen(cursor.data)
|
||||
if char_width_to_move >= total_wlen then
|
||||
cursor:move(math.huge)
|
||||
else
|
||||
local chars_to_move = unicode.wtrunc(cursor.data, char_width_to_move + 1)
|
||||
cursor:move(unicode.len(chars_to_move))
|
||||
end
|
||||
-- fake white space can make the index off, redo adjustment for alignment
|
||||
x2, y2, d = win.x, win.y, win.width
|
||||
char_width_to_move = ((gy*d+gx)-(y2*d+x2))
|
||||
if (char_width_to_move < 0) then
|
||||
-- using char_width_to_move as a type of index is wrong, but large enough and helps to speed this up
|
||||
local up_to_cursor = unicode.sub(cursor.data, cursor.index+char_width_to_move, cursor.index)
|
||||
local full_wlen = unicode.wlen(up_to_cursor)
|
||||
local without_tail = unicode.wtrunc(up_to_cursor, full_wlen + char_width_to_move + 1)
|
||||
local chars_cut = unicode.len(up_to_cursor) - unicode.len(without_tail)
|
||||
cursor:move(-chars_cut)
|
||||
end
|
||||
return false -- no further cursor update
|
||||
end
|
||||
tty.drag_handler = tty.touch_handler
|
||||
|
||||
function tty.clipboard_handler(handler, _, char, _)
|
||||
handler.cache = nil
|
||||
local first_line, end_index = char:find("\13?\10")
|
||||
if first_line then
|
||||
local after = char:sub(end_index + 1)
|
||||
if after ~= "" then
|
||||
-- todo look at postponing the text on cursor
|
||||
require("computer").pushSignal("key_down", tty.keyboard(), 13, 28)
|
||||
require("computer").pushSignal("clipboard", tty.keyboard(), after)
|
||||
end
|
||||
char = char:sub(1, first_line - 1)
|
||||
end
|
||||
return char
|
||||
end
|
||||
|
||||
function tty.on_tab(handler, cursor)
|
||||
local hints = handler.hint
|
||||
if not hints then return end
|
||||
local main_kb = tty.keyboard()
|
||||
-- tty may not have a keyboard
|
||||
-- in which case, we shouldn't be handling tab events
|
||||
if not main_kb then
|
||||
return
|
||||
end
|
||||
if not handler.cache then
|
||||
handler.cache = type(hints) == "table" and hints or hints(cursor.data, cursor.index + 1) or {}
|
||||
handler.cache.i = -1
|
||||
end
|
||||
|
||||
local cache = handler.cache
|
||||
|
||||
if #cache == 1 and cache.i == 0 then
|
||||
-- there was only one solution, and the user is asking for the next
|
||||
handler.cache = hints(cache[1], cursor.index + 1)
|
||||
if not handler.cache then return end
|
||||
handler.cache.i = -1
|
||||
cache = handler.cache
|
||||
end
|
||||
|
||||
local change = kb.isShiftDown(main_kb) and -1 or 1
|
||||
cache.i = (cache.i + change) % math.max(#cache, 1)
|
||||
local next = cache[cache.i + 1]
|
||||
if next then
|
||||
local tail = unicode.len(cursor.data) - cursor.index
|
||||
cursor:clear()
|
||||
cursor:update(next)
|
||||
cursor:move(-tail)
|
||||
end
|
||||
end
|
@ -45,9 +45,9 @@ end
|
||||
-- [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.fill(x, window.y, rep, 1, " ")
|
||||
local x = (n == 0 and window.x or 1)
|
||||
local rep = n == 1 and window.x or (window.width - x + 1)
|
||||
window.gpu.fill(x + window.dx, window.y + window.dy, rep, 1, " ")
|
||||
end
|
||||
rules[{"%[", "[012]?", "K"}] = clear_line
|
||||
|
||||
@ -59,8 +59,8 @@ 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, " ")
|
||||
local rep = n == 1 and (window.y - 1) or (window.height)
|
||||
window.gpu.fill(1 + window.dx, y + window.dy, window.width, rep, " ")
|
||||
end
|
||||
|
||||
-- [H move cursor to upper left corner
|
||||
@ -81,7 +81,7 @@ 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)
|
||||
rules[{"[DEM]"}] = function(window, _, dir)
|
||||
if dir == "D" then
|
||||
window.y = window.y + 1
|
||||
elseif dir == "E" then
|
||||
|
@ -1,5 +1,5 @@
|
||||
local package = require("package")
|
||||
local tty = require("tty")
|
||||
local term = require("term")
|
||||
|
||||
local function optrequire(...)
|
||||
local success, module = pcall(require, ...)
|
||||
@ -91,9 +91,9 @@ io.write("\27[33mEnter a statement and hit enter to evaluate it.\n")
|
||||
io.write("Prefix an expression with '=' to show its value.\n")
|
||||
io.write("Press Ctrl+D to exit the interpreter.\n\27[37m")
|
||||
|
||||
while tty.isAvailable() do
|
||||
while term.isAvailable() do
|
||||
io.write(env._PROMPT)
|
||||
local command = tty.read(read_handler)
|
||||
local command = term.read(read_handler)
|
||||
if not command then -- eof
|
||||
return
|
||||
end
|
||||
@ -122,7 +122,7 @@ while tty.isAvailable() do
|
||||
if not ok then
|
||||
io.stderr:write("crashed serializing result: ", tostring(why))
|
||||
end
|
||||
if tty.getCursor() > 1 then
|
||||
if term.getCursor() > 1 then
|
||||
io.write("\n")
|
||||
end
|
||||
end
|
||||
|
@ -102,14 +102,6 @@ function event.listen(name, callback)
|
||||
return event.register(name, callback, math.huge, math.huge)
|
||||
end
|
||||
|
||||
function event.onError(message)
|
||||
local log = io.open("/tmp/event.log", "a")
|
||||
if log then
|
||||
pcall(log.write, log, tostring(message), "\n")
|
||||
log:close()
|
||||
end
|
||||
end
|
||||
|
||||
function event.pull(...)
|
||||
local args = table.pack(...)
|
||||
if type(args[1]) == "string" then
|
||||
@ -149,13 +141,6 @@ function event.pullFiltered(...)
|
||||
until computer.uptime() >= deadline
|
||||
end
|
||||
|
||||
function event.timer(interval, callback, times)
|
||||
checkArg(1, interval, "number")
|
||||
checkArg(2, callback, "function")
|
||||
checkArg(3, times, "number", "nil")
|
||||
return event.register(false, callback, interval, times)
|
||||
end
|
||||
|
||||
-- users may expect to find event.push to exist
|
||||
event.push = computer.pushSignal
|
||||
|
||||
|
@ -2,7 +2,6 @@ local component = require("component")
|
||||
local unicode = require("unicode")
|
||||
|
||||
local filesystem = {}
|
||||
local isAutorunEnabled = nil
|
||||
local mtab = {name="", children={}, links={}}
|
||||
local fstab = {}
|
||||
|
||||
@ -21,17 +20,6 @@ local function segments(path)
|
||||
return parts
|
||||
end
|
||||
|
||||
local function saveConfig()
|
||||
local root = filesystem.get("/")
|
||||
if root and not root.isReadOnly() then
|
||||
local f = filesystem.open("/etc/filesystem.cfg", "w")
|
||||
if f then
|
||||
f:write("autorun="..tostring(isAutorunEnabled))
|
||||
f:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function findNode(path, create, resolve_links)
|
||||
checkArg(1, path, "string")
|
||||
local visited = {}
|
||||
@ -94,27 +82,6 @@ end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function filesystem.isAutorunEnabled()
|
||||
if isAutorunEnabled == nil then
|
||||
local env = {}
|
||||
local config = loadfile("/etc/filesystem.cfg", nil, env)
|
||||
if config then
|
||||
pcall(config)
|
||||
isAutorunEnabled = not not env.autorun
|
||||
else
|
||||
isAutorunEnabled = true
|
||||
end
|
||||
saveConfig()
|
||||
end
|
||||
return isAutorunEnabled
|
||||
end
|
||||
|
||||
function filesystem.setAutorunEnabled(value)
|
||||
checkArg(1, value, "boolean")
|
||||
isAutorunEnabled = value
|
||||
saveConfig()
|
||||
end
|
||||
|
||||
function filesystem.canonical(path)
|
||||
local result = table.concat(segments(path), "/")
|
||||
if unicode.sub(path, 1, 1) == "/" then
|
||||
@ -298,14 +265,6 @@ function filesystem.list(path)
|
||||
end
|
||||
end
|
||||
|
||||
function filesystem.remove(path)
|
||||
return require("tools/fsmod").remove(path, findNode)
|
||||
end
|
||||
|
||||
function filesystem.rename(oldPath, newPath)
|
||||
return require("tools/fsmod").rename(oldPath, newPath, findNode)
|
||||
end
|
||||
|
||||
function filesystem.open(path, mode)
|
||||
checkArg(1, path, "string")
|
||||
mode = tostring(mode or "r")
|
||||
@ -327,30 +286,22 @@ function filesystem.open(path, mode)
|
||||
return nil, reason
|
||||
end
|
||||
|
||||
local function create_handle_method(key)
|
||||
return function(self, ...)
|
||||
if not self.handle then
|
||||
return nil, "file is closed"
|
||||
end
|
||||
return self.fs[key](self.handle, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local stream =
|
||||
{
|
||||
return setmetatable({
|
||||
fs = node.fs,
|
||||
handle = handle,
|
||||
close = function(self)
|
||||
if self.handle then
|
||||
self.fs.close(self.handle)
|
||||
}, {__index = function(tbl, key)
|
||||
if not tbl.fs[key] then return end
|
||||
if not tbl.handle then
|
||||
return nil, "file is closed"
|
||||
end
|
||||
return function(self, ...)
|
||||
local h = self.handle
|
||||
if key == "close" then
|
||||
self.handle = nil
|
||||
end
|
||||
return self.fs[key](h, ...)
|
||||
end
|
||||
}
|
||||
stream.read = create_handle_method("read")
|
||||
stream.seek = create_handle_method("seek")
|
||||
stream.write = create_handle_method("write")
|
||||
return stream
|
||||
end})
|
||||
end
|
||||
|
||||
filesystem.findNode = findNode
|
||||
|
@ -73,11 +73,15 @@ function process.load(path, env, init, name)
|
||||
return 128 -- syserr
|
||||
end, ...)
|
||||
}
|
||||
process.internal.close(thread, result)
|
||||
|
||||
--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])))
|
||||
pcall(require("event").onError, string.format("process library exception handler crashed: %s", tostring(result[2])))
|
||||
end
|
||||
|
||||
-- onError opens a file, you can't open a file without a process, we close the process last
|
||||
process.internal.close(thread, result)
|
||||
|
||||
return select(2, table.unpack(result))
|
||||
end, true)
|
||||
local new_proc =
|
||||
|
@ -73,11 +73,11 @@ function sh.internal.resolveActions(input, resolver, resolved)
|
||||
resolved[key] = resolver(key)
|
||||
local value = resolved[key]
|
||||
if value and key ~= value then
|
||||
local replacement_tokens, reason = sh.internal.resolveActions(value, resolver, resolved)
|
||||
local replacement_tokens, resolve_reason = sh.internal.resolveActions(value, resolver, resolved)
|
||||
if not replacement_tokens then
|
||||
return replacement_tokens, reason
|
||||
return replacement_tokens, resolve_reason
|
||||
end
|
||||
simple = simple and reason
|
||||
simple = simple and resolve_reason
|
||||
words = tx.concat(replacement_tokens, words)
|
||||
next = table.remove(words,1)
|
||||
end
|
||||
|
@ -1,23 +1,17 @@
|
||||
local tty = require("tty")
|
||||
local unicode = require("unicode")
|
||||
local computer = require("computer")
|
||||
local process = require("process")
|
||||
local event = require("event")
|
||||
local core_cursor = require("core/cursor")
|
||||
|
||||
local kb = require("keyboard")
|
||||
local keys = kb.keys
|
||||
|
||||
-- tty is bisected into a delay loaded library
|
||||
-- term indexing will fail to use full_tty unless tty is fully loaded
|
||||
-- accessing tty.full_tty [a nonexistent field] will cause that full load
|
||||
local term = setmetatable({internal={},tty.full_tty}, {__index=tty})
|
||||
|
||||
function term.internal.window()
|
||||
return process.info().data.window
|
||||
end
|
||||
local term = setmetatable({internal={}}, {__index=tty})
|
||||
|
||||
local function as_window(window, func, ...)
|
||||
local data = process.info().data
|
||||
if not data.window then
|
||||
if not data.window or not window then
|
||||
return func(...)
|
||||
end
|
||||
local prev = rawget(data, "window")
|
||||
@ -29,7 +23,7 @@ end
|
||||
|
||||
function term.internal.open(...)
|
||||
local dx, dy, w, h = ...
|
||||
local window = {fullscreen=select("#",...) == 0, blink = true}
|
||||
local window = {fullscreen=select("#",...) == 0, blink = true, output_buffer = ""}
|
||||
|
||||
-- support legacy code using direct manipulation of w and h
|
||||
-- (e.g. wocchat) instead of using setViewport
|
||||
@ -46,93 +40,34 @@ function term.internal.open(...)
|
||||
})
|
||||
|
||||
-- first time we open a pty the current tty.window must become the process window
|
||||
if not term.internal.window() then
|
||||
local init_index = 2
|
||||
while process.info(init_index) do
|
||||
init_index = init_index + 1
|
||||
if rawget(tty, "window") then
|
||||
for _,p in pairs(process.list) do
|
||||
if not p.parent then
|
||||
p.data.window = tty.window
|
||||
break
|
||||
end
|
||||
end
|
||||
process.info(init_index - 1).data.window = tty.window
|
||||
tty.window = nil
|
||||
setmetatable(tty,
|
||||
{
|
||||
__index = function(_, key)
|
||||
if key == "window" then
|
||||
return term.internal.window()
|
||||
return process.info().data.window
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
as_window(window, tty.setViewport, w, h, dx, dy, 1, 1)
|
||||
as_window(window, tty.bind, tty.gpu())
|
||||
return window
|
||||
end
|
||||
|
||||
local function build_horizontal_reader(cursor)
|
||||
cursor.clear_tail = function(self)
|
||||
local w,_,dx,dy,x,y = tty.getViewport()
|
||||
local _,s2=tty.split(self)
|
||||
local wlen = math.min(unicode.wlen(s2),w-x+1)
|
||||
tty.gpu().fill(x+dx,y+dy,wlen,1," ")
|
||||
end
|
||||
cursor.move = function(self, n)
|
||||
local win = tty.window
|
||||
local a = self.index
|
||||
local b = math.max(0,math.min(unicode.len(self.data), self.index+n))
|
||||
self.index = b
|
||||
a, b = a < b and a or b, a < b and b or a
|
||||
local wlen_moved = unicode.wlen(unicode.sub(self.data, a + 1, b))
|
||||
win.x = win.x + wlen_moved * (n<0 and -1 or 1)
|
||||
self:scroll()
|
||||
end
|
||||
cursor.draw = function(_, text)
|
||||
local nowrap = tty.window.nowrap
|
||||
tty.window.nowrap = true
|
||||
tty.stream:write(text)
|
||||
tty.window.nowrap = nowrap
|
||||
end
|
||||
cursor.scroll = function(self, goback, prev_x)
|
||||
local win = tty.window
|
||||
win.x = goback and prev_x or win.x
|
||||
local x = win.x
|
||||
local w = win.width
|
||||
local data,px,i = self.data, self.promptx, self.index
|
||||
local available = w-px+1
|
||||
if x > w then
|
||||
local blank
|
||||
if i == unicode.len(data) then
|
||||
available,blank=available-1," "
|
||||
else
|
||||
i,blank=i+1,""
|
||||
end
|
||||
data = unicode.sub(data,1,i)
|
||||
local rev = unicode.reverse(data)
|
||||
local ending = unicode.wtrunc(rev, available+1)
|
||||
data = unicode.reverse(ending)
|
||||
win.x = self.promptx
|
||||
self:draw(data..blank)
|
||||
-- wide chars may place the cursor not exactly at the end
|
||||
win.x = math.min(w, self.promptx + unicode.wlen(data))
|
||||
-- x could be negative, we scroll it back into view
|
||||
elseif x < self.promptx then
|
||||
data = unicode.sub(data, self.index+1)
|
||||
if unicode.wlen(data) > available then
|
||||
data = unicode.wtrunc(data,available+1)
|
||||
end
|
||||
win.x = self.promptx
|
||||
self:draw(data)
|
||||
win.x = math.max(px, math.min(w, x))
|
||||
end
|
||||
end
|
||||
cursor.clear = function(self)
|
||||
local win = tty.window
|
||||
local gpu, px = win.gpu, self.promptx
|
||||
local w,_,dx,dy,_,y = tty.getViewport()
|
||||
self.index, self.data, win.x = 0, "", px
|
||||
gpu.fill(px+dx,y+dy,w-px+1-dx,1," ")
|
||||
end
|
||||
end
|
||||
local function create_cursor(history, ops)
|
||||
local cursor = history or {}
|
||||
cursor.hint = ops.hint or cursor.hint
|
||||
|
||||
local function inject_filter(handler, filter)
|
||||
local filter = ops.filter or cursor.filter
|
||||
if filter then
|
||||
if type(filter) == "string" then
|
||||
local filter_text = filter
|
||||
@ -141,47 +76,42 @@ local function inject_filter(handler, filter)
|
||||
end
|
||||
end
|
||||
|
||||
handler.key_down = function(self, cursor, char, code)
|
||||
if code == keys.enter or code == keys.numpadenter then
|
||||
if not filter(cursor.data) then
|
||||
function cursor:handle(name, char, code)
|
||||
if name == "key_down" and (code == keys.enter or code == keys.numpadenter) then
|
||||
if not filter(self.data) then
|
||||
computer.beep(2000, 0.1)
|
||||
return false -- ignore
|
||||
return true -- handled
|
||||
end
|
||||
end
|
||||
return tty.key_down_handler(self, cursor, char, code)
|
||||
return self.super.handle(self, name, char, code)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function inject_mask(cursor, dobreak, pwchar)
|
||||
if not pwchar and dobreak ~= false then
|
||||
return
|
||||
end
|
||||
|
||||
if pwchar then
|
||||
local pwchar = ops.pwchar or cursor.pwchar
|
||||
local nobreak = ops.dobreak == false or cursor.dobreak == false
|
||||
if pwchar or nobreak then
|
||||
if type(pwchar) == "string" then
|
||||
local pwchar_text = pwchar
|
||||
pwchar = function(text)
|
||||
return text:gsub(".", pwchar_text)
|
||||
end
|
||||
end
|
||||
function cursor:echo(arg, ...)
|
||||
if pwchar and type(arg) == "string" and #arg > 0 and not arg:match("^\27") then -- "" is used for scrolling
|
||||
arg = pwchar(arg)
|
||||
elseif nobreak and arg == "\n" then
|
||||
arg = ""
|
||||
end
|
||||
return self.super.echo(self, arg, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local cursor_draw = cursor.draw
|
||||
cursor.draw = function(self, text)
|
||||
local pre, newline = text:match("(.-)(\n?)$")
|
||||
if dobreak == false then
|
||||
newline = ""
|
||||
end
|
||||
if pwchar then
|
||||
pre = pwchar(pre)
|
||||
end
|
||||
return cursor_draw(self, pre .. newline)
|
||||
end
|
||||
return core_cursor.new(cursor, cursor.nowrap and core_cursor.horizontal)
|
||||
end
|
||||
|
||||
-- cannot use term.write = io.write because io.write invokes metatable
|
||||
function term.write(value, wrap)
|
||||
io.stdout:flush()
|
||||
local previous_nowrap = tty.window.nowrap
|
||||
tty.window.nowrap = wrap == false
|
||||
io.write(value)
|
||||
@ -190,32 +120,22 @@ function term.write(value, wrap)
|
||||
end
|
||||
|
||||
function term.read(history, dobreak, hint, pwchar, filter)
|
||||
history = history or {}
|
||||
local handler = history
|
||||
handler.hint = handler.hint or hint
|
||||
|
||||
local cursor = tty.build_vertical_reader()
|
||||
if handler.nowrap then
|
||||
build_horizontal_reader(cursor)
|
||||
end
|
||||
|
||||
inject_filter(handler, filter)
|
||||
inject_mask(cursor, dobreak, pwchar or history.pwchar)
|
||||
handler.cursor = cursor
|
||||
|
||||
return tty.read(handler)
|
||||
tty.window.cursor = create_cursor(history, {
|
||||
dobreak = dobreak,
|
||||
pwchar = pwchar,
|
||||
filter = filter,
|
||||
hint = hint
|
||||
})
|
||||
return io.stdin:readLine(false)
|
||||
end
|
||||
|
||||
function term.getGlobalArea(window)
|
||||
local w,h,dx,dy = as_window(window, tty.getViewport)
|
||||
function term.getGlobalArea()
|
||||
local w,h,dx,dy = tty.getViewport()
|
||||
return dx+1,dy+1,w,h
|
||||
end
|
||||
|
||||
function term.clearLine(window)
|
||||
window = window or tty.window
|
||||
local w, h, dx, dy, _, y = as_window(window, tty.getViewport)
|
||||
window.gpu.fill(dx + 1, dy + math.max(1, math.min(y, h)), w, 1, " ")
|
||||
window.x = 1
|
||||
function term.clearLine()
|
||||
term.write("\27[2K\27[999D")
|
||||
end
|
||||
|
||||
function term.setCursorBlink(enabled)
|
||||
@ -228,19 +148,16 @@ end
|
||||
|
||||
function term.pull(...)
|
||||
local args = table.pack(...)
|
||||
local timeout = nil
|
||||
local timeout = math.huge
|
||||
if type(args[1]) == "number" then
|
||||
timeout = table.remove(args, 1)
|
||||
timeout = computer.uptime() + table.remove(args, 1)
|
||||
args.n = args.n - 1
|
||||
end
|
||||
local stdin_stream = io.stdin.stream
|
||||
if stdin_stream.pull then
|
||||
return stdin_stream:pull(timeout, table.unpack(args, 1, args.n))
|
||||
end
|
||||
-- if stdin does not have pull() we can build the result
|
||||
local result = io.read(1)
|
||||
if result then
|
||||
return "clipboard", nil, result
|
||||
local cursor = core_cursor.new()
|
||||
while timeout >= computer.uptime() and cursor:echo() do
|
||||
local s = table.pack(event.pull(.5, table.unpack(args, 1, args.n)))
|
||||
cursor:echo(not s[1])
|
||||
if s.n > 1 then return table.unpack(s, 1, s.n) end
|
||||
end
|
||||
end
|
||||
|
||||
@ -248,12 +165,7 @@ function term.bind(gpu, window)
|
||||
return as_window(window, tty.bind, gpu)
|
||||
end
|
||||
|
||||
function term.scroll(...)
|
||||
if io.stdout.tty then
|
||||
return io.stdout.stream.scroll(...)
|
||||
end
|
||||
end
|
||||
|
||||
term.scroll = tty.stream.scroll
|
||||
term.internal.run_in_window = as_window
|
||||
|
||||
return term
|
||||
|
@ -1,68 +0,0 @@
|
||||
local filesystem = require("filesystem")
|
||||
|
||||
local lib = {}
|
||||
function lib.remove(path, findNode)
|
||||
local function removeVirtual()
|
||||
local _, _, vnode, vrest = findNode(filesystem.path(path), false, true)
|
||||
-- vrest represents the remaining path beyond vnode
|
||||
-- vrest is nil if vnode reaches the full path
|
||||
-- thus, if vrest is NOT NIL, then we SHOULD NOT remove children nor links
|
||||
if not vrest then
|
||||
local name = filesystem.name(path)
|
||||
if vnode.children[name] or vnode.links[name] then
|
||||
vnode.children[name] = nil
|
||||
vnode.links[name] = nil
|
||||
while vnode and vnode.parent and not vnode.fs and not next(vnode.children) and not next(vnode.links) do
|
||||
vnode.parent.children[vnode.name] = nil
|
||||
vnode = vnode.parent
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- return false even if vrest is nil because this means it was a expected
|
||||
-- to be a real file
|
||||
return false
|
||||
end
|
||||
local function removePhysical()
|
||||
local node, rest = findNode(path)
|
||||
if node.fs and rest then
|
||||
return node.fs.remove(rest)
|
||||
end
|
||||
return false
|
||||
end
|
||||
local success = removeVirtual()
|
||||
success = removePhysical() or success -- Always run.
|
||||
if success then return true
|
||||
else return nil, "no such file or directory"
|
||||
end
|
||||
end
|
||||
|
||||
function lib.rename(oldPath, newPath, findNode)
|
||||
if filesystem.isLink(oldPath) then
|
||||
local _, _, vnode, _ = findNode(filesystem.path(oldPath))
|
||||
local target = vnode.links[filesystem.name(oldPath)]
|
||||
local result, reason = filesystem.link(target, newPath)
|
||||
if result then
|
||||
filesystem.remove(oldPath)
|
||||
end
|
||||
return result, reason
|
||||
else
|
||||
local oldNode, oldRest = findNode(oldPath)
|
||||
local newNode, newRest = findNode(newPath)
|
||||
if oldNode.fs and oldRest and newNode.fs and newRest then
|
||||
if oldNode.fs.address == newNode.fs.address then
|
||||
return oldNode.fs.rename(oldRest, newRest)
|
||||
else
|
||||
local result, reason = filesystem.copy(oldPath, newPath)
|
||||
if result then
|
||||
return filesystem.remove(oldPath)
|
||||
else
|
||||
return nil, reason
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, "trying to read from or write to virtual directory"
|
||||
end
|
||||
end
|
||||
|
||||
return lib
|
@ -1,9 +1,7 @@
|
||||
local unicode = require("unicode")
|
||||
local event = require("event")
|
||||
local kb = require("keyboard")
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local keys = kb.keys
|
||||
|
||||
local tty = {}
|
||||
tty.window =
|
||||
@ -14,55 +12,11 @@ tty.window =
|
||||
dy = 0,
|
||||
x = 1,
|
||||
y = 1,
|
||||
output_buffer = "",
|
||||
}
|
||||
|
||||
tty.stream = {}
|
||||
|
||||
function tty.key_down_handler(handler, cursor, char, code)
|
||||
local data = cursor.data
|
||||
local c = false
|
||||
local backup_cache = handler.cache
|
||||
handler.cache = nil
|
||||
local ctrl = kb.isControlDown(tty.keyboard())
|
||||
if ctrl and code == keys.d then
|
||||
return --close
|
||||
elseif code == keys.tab then
|
||||
handler.cache = backup_cache
|
||||
tty.on_tab(handler, cursor)
|
||||
elseif code == keys.enter or code == keys.numpadenter then
|
||||
cursor:move(math.huge)
|
||||
cursor:draw("\n")
|
||||
if data:find("%S") and data ~= handler[1] then
|
||||
table.insert(handler, 1, data)
|
||||
handler[(tonumber(os.getenv("HISTSIZE")) or 10)+1]=nil
|
||||
end
|
||||
handler[0]=nil
|
||||
return nil, data .. "\n"
|
||||
elseif code == keys.up or code == keys.down then
|
||||
local ni = handler.index + (code == keys.up and 1 or -1)
|
||||
if ni >= 0 and ni <= #handler then
|
||||
handler[handler.index] = data
|
||||
handler.index = ni
|
||||
cursor:clear()
|
||||
cursor:update(handler[ni])
|
||||
end
|
||||
elseif code == keys.left or code == keys.back or code == keys.w and ctrl then
|
||||
local value = ctrl and ((unicode.sub(data, 1, cursor.index):find("%s[^%s]+%s*$") or 0) - cursor.index) or -1
|
||||
if code == keys.left then
|
||||
cursor:move(value)
|
||||
else
|
||||
c = value
|
||||
end
|
||||
elseif code == keys.right then cursor:move(ctrl and ((data:find("%s[^%s]", cursor.index + 1) or math.huge) - cursor.index) or 1)
|
||||
elseif code == keys.home then cursor:move(-math.huge)
|
||||
elseif code == keys["end"] then cursor:move( math.huge)
|
||||
elseif code == keys.delete then c = 1
|
||||
elseif char >= 32 then c = unicode.char(char)
|
||||
else handler.cache = backup_cache -- ignored chars shouldn't clear hint cache
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
local screen_cache = {}
|
||||
local function screen_reset(gpu, addr)
|
||||
screen_cache[addr or gpu.getScreen() or false] = nil
|
||||
@ -101,199 +55,21 @@ function tty.isAvailable()
|
||||
return not not (gpu and gpu.getScreen())
|
||||
end
|
||||
|
||||
function tty.stream:pull(timeout, ...)
|
||||
timeout = timeout or math.huge
|
||||
local blink_timeout = tty.window.blink and .5 or math.huge
|
||||
|
||||
local width, height, dx, dy, x, y = tty.getViewport()
|
||||
local gpu = tty.gpu()
|
||||
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
|
||||
|
||||
-- get the next event
|
||||
while true do
|
||||
local signal = table.pack(event.pull(math.min(blink_timeout, timeout), ...))
|
||||
|
||||
timeout = timeout - blink_timeout
|
||||
local done = signal.n > 1 or timeout < blink_timeout
|
||||
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
|
||||
return table.unpack(signal, 1, signal.n)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function tty.split(cursor)
|
||||
local data, index = cursor.data, cursor.index
|
||||
local dlen = unicode.len(data)
|
||||
index = math.max(0, math.min(index, dlen))
|
||||
local tail = dlen - index
|
||||
return unicode.sub(data, 1, index), tail == 0 and "" or unicode.sub(data, -tail)
|
||||
end
|
||||
|
||||
function tty.build_vertical_reader()
|
||||
return
|
||||
{
|
||||
promptx = tty.window.x,
|
||||
prompty = tty.window.y,
|
||||
index = 0,
|
||||
data = "",
|
||||
sy = 0,
|
||||
scroll = function(self, goback, prev_x, prev_y)
|
||||
local width, x = tty.window.width, tty.getCursor() - 1
|
||||
tty.setCursor(x % width + 1, tty.window.y + math.floor(x / width))
|
||||
self:draw("")
|
||||
if goback then
|
||||
tty.setCursor(prev_x, prev_y - self.sy)
|
||||
end
|
||||
end,
|
||||
move = function(self, n)
|
||||
local win = tty.window
|
||||
self.index = math.min(math.max(0, self.index + n), unicode.len(self.data))
|
||||
local s1, s2 = tty.split(self)
|
||||
s2 = unicode.sub(s2.." ", 1, 1)
|
||||
local data_remaining = ("_"):rep(self.promptx - 1)..s1..s2
|
||||
win.y = self.prompty - self.sy
|
||||
while true do
|
||||
local wlen_remaining = unicode.wlen(data_remaining)
|
||||
if wlen_remaining > win.width then
|
||||
local line_cut = unicode.wtrunc(data_remaining, win.width + 1)
|
||||
data_remaining = unicode.sub(data_remaining, unicode.len(line_cut) + 1)
|
||||
win.y = win.y + 1
|
||||
else
|
||||
win.x = wlen_remaining - unicode.wlen(s2) + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end,
|
||||
clear_tail = function(self)
|
||||
local oi, width, _, dx, dy, ox, oy = self.index, tty.getViewport()
|
||||
self:move(math.huge)
|
||||
self:move(-1)
|
||||
local _, ey = tty.getCursor()
|
||||
tty.setCursor(ox, oy)
|
||||
self.index = oi
|
||||
local cx = oy == ey and ox or 1
|
||||
tty.gpu().fill(cx + dx, ey + dy, width - cx + 1, 1, " ")
|
||||
end,
|
||||
update = function(self, arg)
|
||||
local s1, s2 = tty.split(self)
|
||||
if type(arg) == "string" then
|
||||
self.data = s1 .. arg .. s2
|
||||
self.index = self.index + unicode.len(arg)
|
||||
self:draw(arg)
|
||||
else -- number
|
||||
if arg < 0 then
|
||||
-- backspace? ignore if at start
|
||||
if self.index <= 0 then return end
|
||||
self:move(arg)
|
||||
s1 = unicode.sub(s1, 1, -1 + arg)
|
||||
else
|
||||
-- forward? ignore if at end
|
||||
if self.index >= unicode.len(self.data) then return end
|
||||
s2 = unicode.sub(s2, 1 + arg)
|
||||
end
|
||||
self:clear_tail()
|
||||
self.data = s1 .. s2
|
||||
end
|
||||
|
||||
-- redraw suffix
|
||||
local prev_x, prev_y = tty.getCursor()
|
||||
prev_y = prev_y + self.sy -- scroll will remove it
|
||||
self:draw(s2)
|
||||
self:scroll(s2 ~= "", prev_x, prev_y)
|
||||
end,
|
||||
clear = function(self)
|
||||
self:move(-math.huge)
|
||||
self:draw((" "):rep(unicode.wlen(self.data)))
|
||||
self:move(-math.huge)
|
||||
self.index = 0
|
||||
self.data = ""
|
||||
end,
|
||||
draw = function(self, text)
|
||||
self.sy = self.sy + tty.stream:write(text)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
function tty.read(handler)
|
||||
tty.window.handler = handler
|
||||
|
||||
local stdin = io.stdin
|
||||
local result = table.pack(pcall(stdin.readLine, stdin, false))
|
||||
tty.window.handler = nil
|
||||
return select(2, assert(table.unpack(result)))
|
||||
end
|
||||
|
||||
-- PLEASE do not use this method directly, use io.read or term.read
|
||||
function tty.stream:read()
|
||||
local handler = tty.window.handler or {}
|
||||
local cursor = handler.cursor or tty.build_vertical_reader()
|
||||
function tty.stream.read()
|
||||
local core = require("core/cursor")
|
||||
local cursor = core.new(tty.window.cursor)
|
||||
-- the window is given the cursor to allow sy updates [needed for wide char wrapping]
|
||||
-- even if the user didn't set a cursor, we need one to read
|
||||
tty.window.cursor = cursor
|
||||
|
||||
tty.window.handler = nil
|
||||
handler.index = 0
|
||||
local ok, result, reason = xpcall(core.read, debug.traceback, cursor)
|
||||
|
||||
while true do
|
||||
local name, address, char, code = self:pull()
|
||||
-- we may have lost tty during the pull
|
||||
if not tty.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 = tty.keyboard()
|
||||
local main_sc = tty.screen()
|
||||
if name == "interrupted" then
|
||||
self:write("^C\n")
|
||||
return false, name
|
||||
elseif address == main_kb or address == main_sc then
|
||||
local handler_method = handler[name] or
|
||||
-- this handler listing hack is to delay load tty
|
||||
({key_down=1, touch=1, drag=1, clipboard=1})[name] and tty[name .. "_handler"]
|
||||
if handler_method then
|
||||
-- nil to end (close)
|
||||
-- false to ignore
|
||||
-- true-thy updates cursor
|
||||
local c, ret = handler_method(handler, cursor, char, code)
|
||||
if c == nil then
|
||||
return ret
|
||||
elseif c then
|
||||
-- if we obtained something (c) to handle
|
||||
cursor:update(c)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not ok or not result then
|
||||
pcall(cursor.update, cursor)
|
||||
end
|
||||
end
|
||||
|
||||
function tty.getCursor()
|
||||
local window = tty.window
|
||||
return window.x, window.y
|
||||
end
|
||||
|
||||
function tty.setCursor(x, y)
|
||||
local window = tty.window
|
||||
window.x, window.y = x, y
|
||||
return select(2, assert(ok, result, reason))
|
||||
end
|
||||
|
||||
-- PLEASE do not use this method directly, use io.write or term.write
|
||||
@ -303,36 +79,32 @@ function tty.stream:write(value)
|
||||
return
|
||||
end
|
||||
local window = tty.window
|
||||
local sy = 0
|
||||
local cursor = window.cursor or {sy = 0, tails = {}}
|
||||
local beeped
|
||||
local uptime = computer.uptime
|
||||
local last_sleep = uptime()
|
||||
window.output_buffer = window.output_buffer .. value
|
||||
while true do
|
||||
if uptime() - last_sleep > 1 then
|
||||
if uptime() - last_sleep > 3 then
|
||||
os.sleep(0)
|
||||
last_sleep = uptime()
|
||||
end
|
||||
|
||||
local ansi_print = ""
|
||||
if window.ansi_escape then
|
||||
-- parse the instruction in segment
|
||||
-- [ (%d+;)+ %d+m
|
||||
window.ansi_escape = window.ansi_escape .. value
|
||||
value, ansi_print = require("vt100").parse(window)
|
||||
end
|
||||
local ansi_print = require("vt100").parse(window)
|
||||
|
||||
-- scroll before parsing next line
|
||||
-- the value may only have been a newline
|
||||
sy = sy + self.scroll()
|
||||
cursor.sy = cursor.sy + self.scroll()
|
||||
-- we may have needed to scroll one last time [nowrap adjustments]
|
||||
if #value == 0 then
|
||||
-- or the vt100 parse is incomplete, print nothing else
|
||||
if #window.output_buffer == 0 or not ansi_print then
|
||||
break
|
||||
end
|
||||
|
||||
local x, y = tty.getCursor()
|
||||
|
||||
local _, ei, delim = unicode.sub(value, 1, window.width):find("([\27\t\r\n\a])", #ansi_print + 1)
|
||||
local segment = ansi_print .. (ei and value:sub(1, ei - 1) or value)
|
||||
local _, ei, delim = unicode.sub(window.output_buffer, 1, window.width):find("([\27\t\r\n\a])")
|
||||
local segment = ansi_print .. (ei and window.output_buffer:sub(1, ei - 1) or window.output_buffer)
|
||||
|
||||
if segment ~= "" then
|
||||
local gpu_x, gpu_y = x + window.dx, y + window.dy
|
||||
@ -341,22 +113,22 @@ function tty.stream:write(value)
|
||||
local wlen_remaining = window.width - x + 1
|
||||
if wlen_remaining < wlen_needed then
|
||||
segment = unicode.wtrunc(segment, wlen_remaining + 1)
|
||||
local wlen_used = unicode.wlen(segment)
|
||||
-- we can clear the line because we already know remaining < needed
|
||||
tail = (" "):rep(wlen_remaining - wlen_used)
|
||||
wlen_needed = unicode.wlen(segment)
|
||||
tail = wlen_needed < wlen_remaining and " " or ""
|
||||
cursor.tails[gpu_y - cursor.sy] = tail
|
||||
if not window.nowrap then
|
||||
-- we have to reparse the delimeter
|
||||
ei = #segment
|
||||
-- fake a newline
|
||||
delim = "\n"
|
||||
wlen_needed = wlen_used
|
||||
end
|
||||
end
|
||||
gpu.set(gpu_x, gpu_y, segment..tail)
|
||||
x = x + wlen_needed
|
||||
end
|
||||
|
||||
value = ei and value:sub(ei + 1) or ""
|
||||
window.output_buffer = ei and window.output_buffer:sub(ei + 1) or
|
||||
unicode.sub(window.output_buffer, window.width + 1)
|
||||
|
||||
if delim == "\t" then
|
||||
x = ((x-1) - ((x-1) % 8)) + 9
|
||||
@ -366,14 +138,26 @@ function tty.stream:write(value)
|
||||
elseif delim == "\a" and not beeped then
|
||||
computer.beep()
|
||||
beeped = true
|
||||
elseif delim == "\27" then -- ansi escape
|
||||
window.ansi_escape = ""
|
||||
elseif delim == "\27" then
|
||||
window.output_buffer = delim .. window.output_buffer
|
||||
end
|
||||
|
||||
tty.setCursor(x, y)
|
||||
window.cr_last = delim == "\r"
|
||||
end
|
||||
return sy
|
||||
return cursor.sy
|
||||
end
|
||||
|
||||
function tty.getCursor()
|
||||
local window = tty.window
|
||||
return window.x, window.y
|
||||
end
|
||||
|
||||
function tty.setCursor(x, y)
|
||||
checkArg(1, x, "number")
|
||||
checkArg(2, y, "number")
|
||||
local window = tty.window
|
||||
window.x, window.y = x, y
|
||||
end
|
||||
|
||||
local gpu_intercept = {}
|
||||
@ -394,7 +178,7 @@ function tty.bind(gpu)
|
||||
end
|
||||
end
|
||||
local window = tty.window
|
||||
if not window.gpu or window.gpu == gpu then
|
||||
if window.gpu ~= gpu then
|
||||
window.gpu = gpu
|
||||
window.keyboard = nil -- without a keyboard bound, always use the screen's main keyboard (1st)
|
||||
tty.getViewport()
|
||||
@ -420,7 +204,7 @@ function tty.keyboard()
|
||||
return system_keyboard
|
||||
end
|
||||
|
||||
-- if we are using a gpu bound to the primary scren, then use the primary keyboard
|
||||
-- if we are using a gpu bound to the primary screen, then use the primary keyboard
|
||||
if component.isAvailable("screen") and component.screen.address == screen then
|
||||
window.keyboard = system_keyboard
|
||||
else
|
||||
@ -479,7 +263,6 @@ function tty.stream.scroll(lines)
|
||||
gpu.fill(dx + 1, fill_top, width, abs_lines, ' ')
|
||||
|
||||
tty.setCursor(x, math.max(1, math.min(y, height)))
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
@ -489,6 +272,4 @@ tty.stream.close = bfd
|
||||
tty.stream.seek = bfd
|
||||
tty.stream.handle = "tty"
|
||||
|
||||
require("package").delay(tty, "/lib/core/full_tty.lua")
|
||||
|
||||
return tty
|
||||
|
@ -1,3 +1,4 @@
|
||||
local bit32 = require("bit32")
|
||||
local uuid = {}
|
||||
|
||||
function uuid.next()
|
||||
@ -11,7 +12,7 @@ function uuid.next()
|
||||
if result:len() > 0 then
|
||||
result = result .. "-"
|
||||
end
|
||||
for i = 1,set do
|
||||
for _ = 1,set do
|
||||
local byte = math.random(0, 255)
|
||||
if pos == 6 then
|
||||
byte = bit32.bor(bit32.band(byte, 0x0F), 0x40)
|
||||
|
@ -21,14 +21,14 @@ rules[{"%[", "[%d;]*", "m"}] = function(window, _, number_text)
|
||||
for _,part in ipairs(parts) do
|
||||
local num = tonumber(part[1].txt)
|
||||
last_was_break, num = not num, num or last_was_break and 0
|
||||
|
||||
if num == 7 then
|
||||
|
||||
local flip = num == 7
|
||||
if flip then
|
||||
if not window.flip then
|
||||
local rgb, pal = bg(window.gpu.getForeground())
|
||||
fg(pal or rgb, not not pal)
|
||||
fg, bg = bg, fg
|
||||
end
|
||||
window.flip = true
|
||||
elseif num == 5 then
|
||||
window.blink = true
|
||||
elseif num == 0 then
|
||||
@ -46,6 +46,7 @@ rules[{"%[", "[%d;]*", "m"}] = function(window, _, number_text)
|
||||
set(color)
|
||||
end
|
||||
end
|
||||
window.flip = flip
|
||||
end
|
||||
end
|
||||
|
||||
@ -79,23 +80,22 @@ rules[{"[78]"}] = save_attributes
|
||||
-- u restore cursor position
|
||||
rules[{"%[", "[su]"}] = save_attributes
|
||||
|
||||
-- returns 2 values
|
||||
-- value: parsed text
|
||||
-- ansi_print: failed to parse
|
||||
-- returns: anything that failed to parse
|
||||
function vt100.parse(window)
|
||||
local ansi = window.ansi_escape
|
||||
window.ansi_escape = nil
|
||||
if window.output_buffer:sub(1, 1) ~= "\27" then
|
||||
return ""
|
||||
end
|
||||
local any_valid
|
||||
|
||||
for rule,action in pairs(rules) do
|
||||
local last_index = 0
|
||||
local last_index = 1 -- start at 1 to skip the \27
|
||||
local captures = {}
|
||||
for _,pattern in ipairs(rule) do
|
||||
if last_index >= #ansi then
|
||||
if last_index >= #window.output_buffer then
|
||||
any_valid = true
|
||||
break
|
||||
end
|
||||
local si, ei, capture = ansi:find("^(" .. pattern .. ")", last_index + 1)
|
||||
local si, ei, capture = window.output_buffer:find("^(" .. pattern .. ")", last_index + 1)
|
||||
if not si then
|
||||
break
|
||||
end
|
||||
@ -105,7 +105,8 @@ function vt100.parse(window)
|
||||
|
||||
if #captures == #rule then
|
||||
action(window, table.unpack(captures))
|
||||
return ansi:sub(last_index + 1), ""
|
||||
window.output_buffer = window.output_buffer:sub(last_index + 1)
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
@ -113,18 +114,16 @@ function vt100.parse(window)
|
||||
-- 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
|
||||
|
||||
if not any_valid then
|
||||
-- malformed
|
||||
return ansi, "\27"
|
||||
window.output_buffer = window.output_buffer:sub(2)
|
||||
return "\27"
|
||||
end
|
||||
|
||||
-- else, still consuming
|
||||
window.ansi_escape = ansi
|
||||
return "", ""
|
||||
end
|
||||
|
||||
return vt100
|
||||
|
@ -1,4 +1,44 @@
|
||||
local hookInterval = 100
|
||||
local hookInterval = 10000
|
||||
local function calcHookInterval()
|
||||
local bogomipsDivider = 0.05
|
||||
local bogomipsDeadline = computer.realTime() + bogomipsDivider
|
||||
local ipsCount = 0
|
||||
local bogomipsBusy = true
|
||||
local function calcBogoMips()
|
||||
ipsCount = ipsCount + hookInterval
|
||||
if computer.realTime() > bogomipsDeadline then
|
||||
bogomipsBusy = false
|
||||
end
|
||||
end
|
||||
-- The following is a bit of nonsensical-seeming code attempting
|
||||
-- to cover Lua's VM sufficiently for the IPS calculation.
|
||||
local bogomipsTmpA = {{["b"]=3, ["d"]=9}}
|
||||
local function c(k)
|
||||
if k <= 2 then
|
||||
bogomipsTmpA[1].d = k / 2.0
|
||||
end
|
||||
end
|
||||
debug.sethook(calcBogoMips, "", hookInterval)
|
||||
while bogomipsBusy do
|
||||
local st = ""
|
||||
for k=2,4 do
|
||||
st = st .. "a" .. k
|
||||
c(k)
|
||||
if k >= 3 then
|
||||
bogomipsTmpA[1].b = bogomipsTmpA[1].b * (k ^ k)
|
||||
end
|
||||
end
|
||||
end
|
||||
debug.sethook()
|
||||
return ipsCount / bogomipsDivider
|
||||
end
|
||||
|
||||
local ipsCount = calcHookInterval()
|
||||
-- Since our IPS might still be too generous (hookInterval needs to run at most
|
||||
-- every 0.05 seconds), we divide it further by 10 relative to that.
|
||||
hookInterval = (ipsCount * 0.005)
|
||||
if hookInterval < 1000 then hookInterval = 1000 end
|
||||
|
||||
local deadline = math.huge
|
||||
local hitDeadline = false
|
||||
local function checkDeadline()
|
||||
|
@ -0,0 +1,43 @@
|
||||
package li.cil.oc.integration.computercraft
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI
|
||||
import dan200.computercraft.api.redstone.IBundledRedstoneProvider
|
||||
import li.cil.oc.common.tileentity.traits.BundledRedstoneAware
|
||||
import li.cil.oc.integration.util.BundledRedstone
|
||||
import li.cil.oc.integration.util.BundledRedstone.RedstoneProvider
|
||||
import li.cil.oc.util.BlockPosition
|
||||
import net.minecraft.world.World
|
||||
import net.minecraftforge.common.util.ForgeDirection
|
||||
|
||||
object BundledRedstoneProvider extends IBundledRedstoneProvider with RedstoneProvider {
|
||||
def init() {
|
||||
ComputerCraftAPI.registerBundledRedstoneProvider(this)
|
||||
BundledRedstone.addProvider(this)
|
||||
}
|
||||
|
||||
override def getBundledRedstoneOutput(world: World, x: Int, y: Int, z: Int, side: Int): Int =
|
||||
world.getTileEntity(x, y, z) match {
|
||||
case tile: BundledRedstoneAware =>
|
||||
var result = 0
|
||||
val colours = tile.bundledOutput(ForgeDirection.VALID_DIRECTIONS(side))
|
||||
for (colour <- 0 to 15) {
|
||||
if (colours(colour) > 0) result |= 1 << colour
|
||||
}
|
||||
result
|
||||
case _ => -1
|
||||
}
|
||||
|
||||
override def computeInput(pos: BlockPosition, side: ForgeDirection): Int = 0
|
||||
|
||||
override def computeBundledInput(pos: BlockPosition, side: ForgeDirection): Array[Int] = {
|
||||
val offset = pos.offset(side)
|
||||
val strength = ComputerCraftAPI.getBundledRedstoneOutput(pos.world.get, offset.x, offset.y, offset.z, side.getOpposite.ordinal())
|
||||
if (strength >= 0) {
|
||||
val strengths = new Array[Int](16)
|
||||
for (colour <- 0 to 15) {
|
||||
strengths(colour) = if ((strength & (1 << colour)) == 0) 0 else 15
|
||||
}
|
||||
strengths
|
||||
} else null
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ object ModComputerCraft extends ModProxy {
|
||||
|
||||
override def initialize() {
|
||||
PeripheralProvider.init()
|
||||
BundledRedstoneProvider.init()
|
||||
|
||||
Driver.add(DriverComputerCraftMedia)
|
||||
Driver.add(new DriverPeripheral())
|
||||
|
Loading…
x
Reference in New Issue
Block a user