Merge branch 'master-MC1.7.10' into master-MC1.10

This commit is contained in:
payonel 2018-08-29 02:06:52 -07:00
commit e2307316ee
34 changed files with 839 additions and 772 deletions

View File

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

View File

@ -23,6 +23,7 @@ options =
{
cmd = "cp",
i = options.i,
f = options.f,
n = options.n,
r = options.r,
u = options.u,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,6 +9,7 @@ object ModComputerCraft extends ModProxy {
override def initialize() {
PeripheralProvider.init()
BundledRedstoneProvider.init()
Driver.add(DriverComputerCraftMedia)
Driver.add(new DriverPeripheral())