From 9b12c1e3506e6e06de454e440c5b4e7e375f6ad8 Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 23 Aug 2018 23:09:25 -0700 Subject: [PATCH] openos pre-1.7.3 patching 1. as always, code cleanup and memory savings 2. allow force (-f) option with cp 3. cursor library, separating logic from tty [ the cursor library is robust and heavily tested, but the api is not finalized and is considered beta ] 4. fix autorun on ro fs 5. /bin/ls: run all filesystem calls in lua, safe from c-boundaries 6. vt100: small fix with clear line (1-off error) --- .../opencomputers/loot/openos/bin/cp.lua | 1 + .../opencomputers/loot/openos/bin/sh.lua | 3 +- .../loot/openos/boot/00_base.lua | 3 - .../loot/openos/boot/01_process.lua | 2 +- .../opencomputers/loot/openos/boot/02_os.lua | 31 +- .../loot/openos/boot/90_filesystem.lua | 2 +- .../loot/openos/boot/94_shell.lua | 4 +- .../loot/openos/lib/core/cursor.lua | 284 +++++++++++------- .../loot/openos/lib/core/devfs/02_utils.lua | 19 +- .../loot/openos/lib/core/full_buffer.lua | 4 +- .../loot/openos/lib/core/full_cursor.lua | 119 +++++--- .../loot/openos/lib/core/full_event.lua | 14 + .../loot/openos/lib/core/full_filesystem.lua | 128 +++++++- .../loot/openos/lib/core/full_ls.lua | 84 +++--- .../loot/openos/lib/core/full_text.lua | 11 +- .../loot/openos/lib/core/full_vt.lua | 3 +- .../loot/openos/lib/core/lua_shell.lua | 8 +- .../opencomputers/loot/openos/lib/event.lua | 15 - .../loot/openos/lib/filesystem.lua | 71 +---- .../opencomputers/loot/openos/lib/process.lua | 6 +- .../opencomputers/loot/openos/lib/sh.lua | 6 +- .../opencomputers/loot/openos/lib/term.lua | 123 ++------ .../loot/openos/lib/tools/fsmod.lua | 68 ----- .../opencomputers/loot/openos/lib/tty.lua | 63 ++-- .../opencomputers/loot/openos/lib/vt100.lua | 7 +- 25 files changed, 543 insertions(+), 536 deletions(-) delete mode 100644 src/main/resources/assets/opencomputers/loot/openos/lib/tools/fsmod.lua diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua index 9b68c7190..d173e219a 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/cp.lua @@ -23,6 +23,7 @@ options = { cmd = "cp", i = options.i, + f = options.f, n = options.n, r = options.r, u = options.u, diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua index e702e1629..0f4a03df3 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/sh.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua index c8a56ac4b..7bd7b3aba 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/boot/00_base.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/01_process.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/01_process.lua index 9c2e9808b..f6e793956 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/boot/01_process.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/boot/01_process.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua index 5a40d04bf..314635696 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/boot/02_os.lua @@ -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") diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/90_filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/90_filesystem.lua index 03506eaa1..578037e9f 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/boot/90_filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/boot/90_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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/94_shell.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/94_shell.lua index 445b75d9d..13c9560fc 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/boot/94_shell.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/boot/94_shell.lua @@ -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") diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/cursor.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/cursor.lua index 4ffd5f613..6a3b783c4 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/cursor.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/cursor.lua @@ -1,32 +1,48 @@ 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 = {} -local super = {} -function super:move(n) - local s = - n < 0 and self.index > 0 and -1 or - n > 0 and self.index < self.len and 1 or - 0 - if s == 0 then return end - local echo_cmd = (s > 0 and keys.right or keys.left) - self.index = self.index + s - self:echo(echo_cmd) - return self:move(n - s) +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 super:update(arg, back) +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 - self.index = self.index + unicode.len(arg) - self:echo(arg) - self:move(back or 0) - else -- number + elseif arg then -- number local has_tail = arg < 0 or #s2 > 0 if arg < 0 then -- backspace? ignore if at start @@ -44,136 +60,190 @@ function super:update(arg, back) 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 super:echo(arg) +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 = - self.window.gpu, - self.window.width, - self.window.x, - self.window.y + local gpu, width, x, y = win.gpu, win.width, win.x, win.y if x > width then - self.window.x = ((x - 1) % width) + 1 - self.window.y = y + math.floor(x / width) - self.output:write("") - x, y = self.window.x, self.window.y + 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 - return x > 0 and y > 0 and y <= self.window.height and gpu and - select(2, pcall(gpu.get, x + self.window.dx, y + self.window.dy)) - elseif arg == keys.enter then - arg = "\n" + 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 = self.window.x - unicode.wlen(unicode.sub(self.data, self.index + 1, self.index + 1)) - local y = self.window.y - if x < 1 then - x = x + self.window.width - #(self.tails[self.window.dy + y - self.sy - 1] or "") + 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 - self.window.x, self.window.y = x, y + win.x, win.y = x, y arg = "" elseif arg == keys.right then - local x = self.window.x + unicode.wlen(unicode.sub(self.data, self.index, self.index)) - local y = self.window.y - local width = self.window.width - #(self.tails[self.window.dy + y - self.sy] or "") - if x > width 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 - self.window.x, self.window.y = x, y + win.x, win.y = x, y arg = "" - elseif type(arg) == "boolean" or arg == nil then -- blink + 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 then return false end + if not char[1] then return false end self.blinked = true - arg = "\0277\27[7m" .. char .. "\0278" + 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 - arg, char = "\0277" .. char .. "\0278", arg and char + 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 not arg or self.output:write(arg) + return io.write(arg) end -function super:handle(name, char, code) +function core_cursor.vertical:handle(name, char, code) if name == "clipboard" then - return core_cursor.clipboard(self, char, code) + 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 - return core_cursor.touch(self, char, code) + core_cursor.touch(self, char, code) elseif name == "interrupted" then self:echo("^C\n") return false, name - elseif name ~= "key_down" then - return true -- handled - end - - local data = self.data - local value = false - 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:echo(keys.enter) - if data:find("%S") and data ~= self[1] then - table.insert(self, 1, data) - self[(tonumber(os.getenv("HISTSIZE")) or 10)+1] = nil - end - self[0] = nil - return data .. "\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 + 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(-self.len) - self:update(self[ni]) + 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 - elseif code == keys.left or code == keys.back or code == keys.w and ctrl then - 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) - value = false -- don't also update (cut) - 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 value = 1 - elseif char >= 32 then value = unicode.char(char) - else self.cache = backup_cache -- ignored chars shouldn't clear hint cache - end - if value then - self:update(value) end return true end -function core_cursor.new(base, window, output) - local result = base or {} - result.tails = {} - result.data = "" - result.index = 0 - result.len = 0 - result.sy = 0 - result.hindex = 0 - result.window = window - result.output = output - result.clear = "\27[J" -- echo'd to clear the input text in the tty - result.super = super - return setmetatable(result, { __index = super }) +-- 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") diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/02_utils.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/02_utils.lua index a34c49abc..90ea24711 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/02_utils.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/devfs/02_utils.lua @@ -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 + }, } diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_buffer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_buffer.lua index b3e2382ca..98d5fae55 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_buffer.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_buffer.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_cursor.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_cursor.lua index 55048e5b6..671ac1838 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_cursor.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_cursor.lua @@ -1,46 +1,29 @@ 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.data == "" then - return true - end - local win = cursor.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 + 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 - return true -end - -function core_cursor.clipboard(cursor, char) - cursor.cache = nil - local first_line, end_index = char:find("\13?\10") - local after = "" - if first_line then - after = char:sub(end_index + 1) - char = char:sub(1, first_line - 1) - end - cursor:update(char) - if after ~= "" then - -- todo look at postponing the text on cursor - local keyboard = require("tty").keyboard() - require("computer").pushSignal("key_down", keyboard, 13, 28) - require("computer").pushSignal("clipboard", keyboard, after) - end - return first_line and char or true end function core_cursor.tab(cursor) @@ -74,3 +57,67 @@ function core_cursor.tab(cursor) 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 }) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua index d1e5710d7..ef90a8b06 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua index 108373619..1a67f1763 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_filesystem.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua index 57e178153..491ac46b8 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_ls.lua @@ -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) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua index dc7f2eb4d..2f9c33e64 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_text.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua index df4e1c467..ee4b4f494 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua @@ -1,5 +1,4 @@ local vt100 = require("vt100") -local unicode = require("unicode") local rules = vt100.rules @@ -60,7 +59,7 @@ 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 - 1) + local rep = n == 1 and (window.y - 1) or (window.height) window.gpu.fill(1 + window.dx, y + window.dy, window.width, rep, " ") end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua index 7f01aee54..12cf51f94 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/lua_shell.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua index 52ad8761b..37cd42dde 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua index bdb315026..ae36a963e 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua index f0b157e7a..d535a5639 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua @@ -73,11 +73,15 @@ function process.load(path, env, init, name) return 128 -- syserr end, ...) } + --result[1] is false if the exception handler also crashed if not result[1] and type(result[2]) ~= "number" then - require("event").onError(string.format("process library exception handler crashed: %s", tostring(result[2]))) + 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 = diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua index 1f923161e..f3890ac53 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua index 999127967..3868fc79b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua @@ -3,17 +3,12 @@ local computer = require("computer") local process = require("process") local event = require("event") local core_cursor = require("core/cursor") -local unicode = require("unicode") local kb = require("keyboard") local keys = kb.keys local term = setmetatable({internal={}}, {__index=tty}) -function term.internal.window() - return process.info().data.window -end - local function as_window(window, func, ...) local data = process.info().data if not data.window or not window then @@ -45,99 +40,33 @@ 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 horizontal_echo(self, arg) - if arg == "" then -- special scroll request - local w = self.window - local width = w.width - if w.x >= width then - -- render all text from 1 to width from end - -- the width that matters depends on the following char width - local next_overlap = math.max(unicode.wlen(unicode.sub(self.data, self.index + 1, self.index + 1)) - 1, 0) - width = width - next_overlap - if w.x > width then - local s1, s2 = - unicode.sub(self.data, self.vindex + 1, self.index), - unicode.sub(self.data, self.index + 1) - -- vindex is really the only way we know where input started - local s1wlen = unicode.wlen(s1) - -- adjust is how far to push the string back - local adjust = w.x - width - local wlen_available = s1wlen - adjust + 1 -- +1 because wtrunc removes the final position - -- now we have to resize s2 to fit in wlen_available, from the end, not the front - local trunc = unicode.wtrunc(unicode.reverse(s1), wlen_available) - -- is it faster to reverse again, or take wlen and sub, probably just reverse - trunc = unicode.reverse(trunc) - -- a double wide may have just been cut - local cutwlen = s1wlen - unicode.wlen(trunc) - -- we have to move to position 1, which should be at vindex, or s2wlen back from x - w.x = w.x - s1wlen - self.output:write(trunc .. s2 .. (" "):rep(cutwlen)) - self.vindex = self.index - unicode.len(trunc) - w.x = width - cutwlen + 1 - end - elseif w.x < 1 then - -- render all text from 1 to width from end - local s2 = unicode.sub(self.data, self.index + 1) - w.x = 1 - self.output:write(s2) - w.x = 1 - self.vindex = self.index - end - -- scroll is safe now, return as normal below - elseif arg == keys.left then - if self.index < self.vindex then - self.window.x = 0 - return self:echo("") - end - elseif arg == keys.right then - self.window.x = self.window.x + unicode.wlen(unicode.sub(self.data, self.index, self.index)) - return self:echo("") - end - return self.super.echo(self, arg) -end +local function create_cursor(history, ops) + local cursor = history or {} + cursor.hint = ops.hint or cursor.hint -local function horizontal_update(self, 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 - local arg_len = unicode.len(arg) - local x = self.window.x - self.output:write(arg) - self.window.x = x - self.data = self.data .. arg - self.len = self.len + arg_len -- recompute len - self:move(self.len - self.index + back) -- back is negative from end - return true - end - return self.super.update(self, arg, back) -end - -local function add_cursor_handlers(cursor, ops) - -- cursor inheritance gives super: to access base methods - -- function cursor:method(...) - -- return self.super.method(self, ...) - -- end local filter = ops.filter or cursor.filter if filter then if type(filter) == "string" then @@ -160,32 +89,29 @@ local function add_cursor_handlers(cursor, ops) local pwchar = ops.pwchar or cursor.pwchar local nobreak = ops.dobreak == false or cursor.dobreak == false - if pwchar or nobreak or cursor.nowrap then + 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 then -- "" is used for scrolling + 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 == keys.enter then + elseif nobreak and arg == "\n" then arg = "" end - local echo = cursor.nowrap and horizontal_echo or self.super.echo - return echo(self, arg) + return self.super.echo(self, arg, ...) end end - if cursor.nowrap then - cursor.update = horizontal_update - tty.window.nowrap = true - cursor.vindex = 0 -- visual/virtual index - 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) @@ -194,16 +120,13 @@ function term.write(value, wrap) end function term.read(history, dobreak, hint, pwchar, filter) - local cursor = history or {} - cursor.hint = cursor.hint or hint - - add_cursor_handlers(cursor, { + tty.window.cursor = create_cursor(history, { dobreak = dobreak, pwchar = pwchar, filter = filter, + hint = hint }) - - return tty.read(cursor) + return io.stdin:readLine(false) end function term.getGlobalArea() @@ -230,7 +153,7 @@ function term.pull(...) timeout = computer.uptime() + table.remove(args, 1) args.n = args.n - 1 end - local cursor = core_cursor.new(nil, tty.window, tty.stream) -- cursors can blink (base arg is optional) + 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]) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/fsmod.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tools/fsmod.lua deleted file mode 100644 index f2905b898..000000000 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tools/fsmod.lua +++ /dev/null @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua index 0cfc65f00..91b6c6d66 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua @@ -27,7 +27,7 @@ event.listen("screen_resized", screen_reset) function tty.getViewport() local window = tty.window local screen = tty.screen() - if window.fullscreen and screen and not screen_cache[screen]then + if window.fullscreen and screen and not screen_cache[screen] then screen_cache[screen] = true window.width, window.height = window.gpu.getViewport() end @@ -55,50 +55,21 @@ function tty.isAvailable() return not not (gpu and gpu.getScreen()) end -function tty.read(cursor) - tty.window.cursor = cursor - - local stdin = io.stdin - local result = table.pack(pcall(stdin.readLine, stdin, false)) - tty.window.cursor = 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 cursor = require("core/cursor").new(tty.window.cursor, tty.window, tty.stream) - tty.window.cursor = cursor -- used by output to update the sy offset - local ret, why + 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 - -- address checks - local address_check = - { - key_down = tty.keyboard, - clipboard = tty.keyboard, - touch = tty.screen, - drag = tty.screen - } + local ok, result, reason = xpcall(core.read, debug.traceback, cursor) - while cursor:echo() do - local name, address, char, code = event.pull(tty.window.blink and .5 or math.huge) - - -- we may have lost the cursor during pull - if not cursor:echo(not name) then - ret, why = nil, nil - break - elseif name then - local filter_address = address_check[name] - if not filter_address or filter_address() == address then - ret, why = cursor:handle(name, char, code) - if ret ~= true then - break - end - end - end + if not ok or not result then + pcall(cursor.update, cursor) end - tty.window.cursor = nil - return ret, why + return select(2, assert(ok, result, reason)) end -- PLEASE do not use this method directly, use io.write or term.write @@ -108,13 +79,13 @@ function tty.stream:write(value) return end local window = tty.window - local cursor = window.cursor or {sy=0, tails={}} + 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 @@ -142,8 +113,8 @@ 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) - tail = wlen_used < wlen_remaining and " " or "" + 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 @@ -151,13 +122,13 @@ function tty.stream:write(value) -- fake a newline delim = "\n" end - wlen_needed = wlen_used end gpu.set(gpu_x, gpu_y, segment..tail) x = x + wlen_needed end - window.output_buffer = ei and window.output_buffer: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 @@ -183,6 +154,8 @@ function tty.getCursor() 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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua index 5abc8bd79..d42e64d9c 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua @@ -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