diff --git a/build.properties b/build.properties index c1ee0e1e3..21bc010ca 100644 --- a/build.properties +++ b/build.properties @@ -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 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/ps.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/ps.lua index 9a31a8abd..2c960273b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/ps.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/ps.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua index ebb35364f..0487b3618 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua @@ -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 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/boot.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua index 2b0f18405..8013be6fe 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/boot.lua @@ -8,10 +8,11 @@ local computer = computer local unicode = unicode -- Runlevel information. -local runlevel, shutdown = "S", computer.shutdown -computer.runlevel = function() return runlevel end +_G.runlevel = "S" +local shutdown = computer.shutdown +computer.runlevel = function() return _G.runlevel end computer.shutdown = function(reboot) - runlevel = reboot and 6 or 0 + _G.runlevel = reboot and 6 or 0 if os.sleep then computer.pushSignal("shutdown") os.sleep(0.1) -- Allow shutdown processing. 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 new file mode 100644 index 000000000..6a3b783c4 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/cursor.lua @@ -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 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 new file mode 100644 index 000000000..671ac1838 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_cursor.lua @@ -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 }) 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_tty.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_tty.lua deleted file mode 100644 index c52eae35b..000000000 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_tty.lua +++ /dev/null @@ -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 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 de6a64eb1..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 @@ -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 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 42a669111..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, ...) } - 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 = 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 77e63253b..3868fc79b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/term.lua @@ -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 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 c7ad2093c..91b6c6d66 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua @@ -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 diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/uuid.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/uuid.lua index d07f7f610..0007e60e4 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/uuid.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/uuid.lua @@ -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) 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 a161ad5be..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 @@ -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 diff --git a/src/main/resources/assets/opencomputers/lua/machine.lua b/src/main/resources/assets/opencomputers/lua/machine.lua index d8c3eb540..d2751bfb0 100644 --- a/src/main/resources/assets/opencomputers/lua/machine.lua +++ b/src/main/resources/assets/opencomputers/lua/machine.lua @@ -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() diff --git a/src/main/scala/li/cil/oc/integration/computercraft/BundledRedstoneProvider.scala b/src/main/scala/li/cil/oc/integration/computercraft/BundledRedstoneProvider.scala new file mode 100644 index 000000000..feddf6b0c --- /dev/null +++ b/src/main/scala/li/cil/oc/integration/computercraft/BundledRedstoneProvider.scala @@ -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 + } +} diff --git a/src/main/scala/li/cil/oc/integration/computercraft/ModComputerCraft.scala b/src/main/scala/li/cil/oc/integration/computercraft/ModComputerCraft.scala index 8001d88fe..11ef7ba7e 100644 --- a/src/main/scala/li/cil/oc/integration/computercraft/ModComputerCraft.scala +++ b/src/main/scala/li/cil/oc/integration/computercraft/ModComputerCraft.scala @@ -9,6 +9,7 @@ object ModComputerCraft extends ModProxy { override def initialize() { PeripheralProvider.init() + BundledRedstoneProvider.init() Driver.add(DriverComputerCraftMedia) Driver.add(new DriverPeripheral())