From e07954a02fd1595a436afe08e7a88ce13cefdd46 Mon Sep 17 00:00:00 2001 From: payonel Date: Fri, 10 Nov 2017 11:29:26 -0800 Subject: [PATCH 01/12] link to IDEA instructions for 1.7.10 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 593ce7f96..e4edf0285 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,8 @@ to create an IntelliJ IDEA project. Open the project and you will be asked to *import the Gradle project* (check your Event Log if you missed the pop-up). **Do so**. This will configure additionally referenced libraries. +For more specific instructions, read [Steps to run master MC1.7.10 from IDEA][idea_1.7.10] + In the case you wish to use Eclipse rather than IntelliJ IDEA, the process is mostly the same, except you must run `gradlew eclipse` rather than `gradlew idea`. @@ -108,3 +110,4 @@ In the case you wish to use Eclipse rather than IntelliJ IDEA, the process is mo [wiki]: http://ocdoc.cil.li/ [integration]: https://github.com/MightyPirates/OpenComputers/tree/master-MC1.7.10/src/main/scala/li/cil/oc/integration [ingame manual]: https://github.com/MightyPirates/OpenComputers/tree/master-MC1.7.10/src/main/resources/assets/opencomputers/doc +[idea_1.7.10]: http://ocdoc.cil.li/tutorial:debug_1.7.10 \ No newline at end of file From 355a5209771d19f874c3a8f31573288e18e85fc2 Mon Sep 17 00:00:00 2001 From: payonel Date: Sat, 11 Nov 2017 10:50:01 -0800 Subject: [PATCH 02/12] allow custom error objects to print to stderr --- .../assets/opencomputers/loot/openos/lib/process.lua | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 38c8a2e3d..795af49a1 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/process.lua @@ -65,11 +65,7 @@ function process.load(path, env, init, name) return code(init(...)) end, function(msg) - -- msg can be a custom error object - if type(msg) == "table" then - if msg.reason ~= "terminated" then - io.stderr:write(tostring(msg.reason), "\n") - end + if type(msg) == "table" and msg.reason == "terminated" then return msg.code or 0 end local stack = debug.traceback():gsub("^([^\n]*\n)[^\n]*\n[^\n]*\n","%1") From a9c4529372c3ea9654b985521a59a39cdc656ba7 Mon Sep 17 00:00:00 2001 From: payonel Date: Mon, 13 Nov 2017 13:49:29 -0800 Subject: [PATCH 03/12] bump openos version string to 1.7.0 probably should have done this back when OC updated to 1.7 --- .../assets/opencomputers/loot/openos/lib/core/boot.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4244ef680..8f8bcd536 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 @@ -1,7 +1,7 @@ -- called from /init.lua local raw_loadfile = ... -_G._OSVERSION = "OpenOS 1.6.8" +_G._OSVERSION = "OpenOS 1.7.0" local component = component local computer = computer From ebb7dcc0d2878faf73de5c1df97f832778c45d95 Mon Sep 17 00:00:00 2001 From: payonel Date: Tue, 14 Nov 2017 23:05:42 -0800 Subject: [PATCH 04/12] fix missing dev bug for install and let mount use existing dirs --- .../opencomputers/loot/openos/lib/core/install_basics.lua | 3 ++- .../assets/opencomputers/loot/openos/lib/filesystem.lua | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua index 67d218fdd..fd6bfa30e 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua @@ -67,7 +67,8 @@ for dev, path in fs.mounts() do devices[dev] = devices[dev] and #devices[dev] < #path and devices[dev] or path end -devices[fs.get("/dev/") or false] = nil +local dev_dev = fs.get("/dev") +devices[dev_dev == rootfs or dev_dev] = nil local tmpAddress = computer.tmpAddress() for dev, path in pairs(devices) do 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 2d6d7740d..a8e91d315 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua @@ -184,8 +184,8 @@ function filesystem.mount(fs, path) return nil, why end - if filesystem.exists(real) then - return nil, "file already exists" + if filesystem.exists(real) and not filesystem.isDirectory(real) then + return nil, "mount point is not a directory" end end From 89abb6f30bf6a66d43ef33155e2e7723ddad8c03 Mon Sep 17 00:00:00 2001 From: payonel Date: Wed, 15 Nov 2017 21:24:12 -0800 Subject: [PATCH 05/12] mount bind point support --- .../opencomputers/loot/openos/bin/mount.lua | 70 +++++++++---- .../loot/openos/lib/core/full_filesystem.lua | 99 +++++++++++++++++++ .../loot/openos/lib/core/ro_wrapper.lua | 30 ------ .../loot/openos/lib/filesystem.lua | 20 +--- .../opencomputers/loot/openos/lib/package.lua | 2 +- .../opencomputers/loot/openos/usr/man/mount | 12 ++- 6 files changed, 164 insertions(+), 69 deletions(-) delete mode 100644 src/main/resources/assets/opencomputers/loot/openos/lib/core/ro_wrapper.lua diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua index de5eb75b7..12b64d2df 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/mount.lua @@ -1,18 +1,37 @@ local fs = require("filesystem") local shell = require("shell") -local args, ops = shell.parse(...) -local argc = #args +local function usage() + io.stderr:write([==[ +Usage: mount [OPTIONS] [device path]") + If no args are given, all current mount points are printed. + Note that multiple options can be used together + -r, --ro Mount the filesystem read only + --bind Create a mount bind point, folder to folder + + device Specify filesystem device by one of: + a. label + b. address (can be abbreviated) + c. folder path (requires --bind) + path Target folder path to mount to -if ops and (ops.h or ops.help) then - print("see `man mount` for help"); +See `man mount` for more details + ]==]) os.exit(1) end - -if argc == 0 then + +-- smart parse, follow arg after -o +local args, opts = shell.parse(...) +opts.readonly = opts.r or opts.readonly + +if opts.h or opts.help then + usage() +end + +local function print_mounts() -- for each mount local mounts = {} - + for proxy,path in fs.mounts() do local device = {} @@ -25,7 +44,7 @@ if argc == 0 then local dev_mounts = mounts[device.dev_path] table.insert(dev_mounts, device) end - + local smounts = {} for key,value in pairs(mounts) do smounts[#smounts+1] = {key, value} @@ -38,29 +57,40 @@ if argc == 0 then local rw_ro = "(" .. device.rw_ro .. ")" local fs_label = "\"" .. device.fs_label .. "\"" - io.write(string.format("%s on %-10s %s %s\n", + io.write(string.format("%-8s on %-10s %s %s\n", dev_path:sub(1,8), device.mount_path, rw_ro, fs_label)) end end -elseif argc ~= 2 then - print("Usage: mount [ ]") - print("Note that the address may be abbreviated.") - return 1 -- error code -else - local proxy, reason = fs.proxy(args[1]) +end + +local function do_mount() + -- bind converts a path to a proxy + local proxy, reason = fs.proxy(args[1], opts) if not proxy then - io.stderr:write(reason,"\n") - return 1 - elseif ops.r then - proxy = dofile("/lib/core/ro_wrapper.lua").wrap(proxy) + io.stderr:write("Failed to mount: ", tostring(reason), "\n") + os.exit(1) end local result, mount_failure = fs.mount(proxy, shell.resolve(args[2])) if not result then io.stderr:write(mount_failure, "\n") - return 2 -- error code + os.exit(2) -- error code end end + +if #args == 0 then + if next(opts) then + io.stderr:write("Missing argument\n") + usage() + else + print_mounts() + end +elseif #args == 2 then + do_mount() +else + io.stderr:write("wrong number of arguments: ", #args, "\n") + usage() +end 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 99328e9eb..bfa44f849 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,4 +1,5 @@ local filesystem = require("filesystem") +local component = require("component") function filesystem.makeDirectory(path) if filesystem.exists(path) then @@ -136,3 +137,101 @@ function filesystem.copy(fromPath, toPath) return data == nil, reason end +local function readonly_wrap(proxy) + checkArg(1, proxy, "table") + if proxy.isReadOnly() then + return proxy + end + + local function roerr() return nil, "filesystem is readonly" end + return setmetatable({ + rename = roerr, + open = function(path, mode) + checkArg(1, path, "string") + checkArg(2, mode, "string") + if mode:match("[wa]") then + return roerr() + end + return proxy.open(path, mode) + end, + isReadOnly = function() + return true + end, + write = roerr, + setLabel = roerr, + makeDirectory = roerr, + remove = roerr, + }, {__index=proxy}) +end + +local function bind_proxy(path) + local real, reason = filesystem.realPath(path) + if not real then + return nil, reason + end + local real_fs, real_fs_path = filesystem.get(real) + if real == real_fs_path then + return real_fs + end + -- turn /tmp/foo into foo + local rest = real:sub(#real_fs_path + 1) + local function wrap_relative(fp) + return function(path, ...) + return fp(filesystem.concat(rest, path), ...) + end + end + local bind = { + address = real, + isReadOnly = real_fs.isReadOnly, + list = wrap_relative(real_fs.list), + isDirectory = wrap_relative(real_fs.isDirectory), + size = wrap_relative(real_fs.size), + lastModified = wrap_relative(real_fs.lastModified), + exists = wrap_relative(real_fs.exists), + open = wrap_relative(real_fs.open), + read = real_fs.read, + write = real_fs.write, + close = real_fs.close, + getLabel = function() return "" end, + setLabel = function() return nil, "cannot set the label of a bind point" end, + } + setmetatable(bind, { __index = function(...) + print("missing bind method", ...) + end + }) + return bind +end + +filesystem.internal = {} +function filesystem.internal.proxy(filter, options) + checkArg(1, filter, "string") + checkArg(2, options, "table", "nil") + options = options or {} + local address, proxy, reason + if options.bind then + proxy, reason = bind_proxy(filter) + else + -- no options: filter should be a label or partial address + for c in component.list("filesystem", true) do + if component.invoke(c, "getLabel") == filter then + address = c + break + end + if c:sub(1, filter:len()) == filter then + address = c + break + end + end + if not address then + return nil, "no such file system" + end + proxy, reason = component.proxy(address) + end + if not proxy then + return proxy, reason + end + if options.readonly then + proxy = readonly_wrap(proxy) + end + return proxy +end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/ro_wrapper.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/ro_wrapper.lua deleted file mode 100644 index c9f2f3d12..000000000 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/ro_wrapper.lua +++ /dev/null @@ -1,30 +0,0 @@ -local lib = {} - -function lib.wrap(proxy) - checkArg(1, proxy, "table") - if proxy.isReadOnly() then - return proxy - end - - local function roerr() return nil, "filesystem is readonly" end - return setmetatable({ - rename = roerr, - open = function(path, mode) - checkArg(1, path, "string") - checkArg(2, mode, "string") - if mode:match("[wa]") then - return roerr() - end - return proxy.open(path, mode) - end, - isReadOnly = function() - return true - end, - write = roerr, - setLabel = roerr, - makeDirectory = roerr, - remove = roerr, - }, {__index=proxy}) -end - -return lib 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 a8e91d315..b35cc598a 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua @@ -234,23 +234,13 @@ function filesystem.name(path) return parts[#parts] end -function filesystem.proxy(filter) +function filesystem.proxy(filter, options) checkArg(1, filter, "string") - local address - for c in component.list("filesystem", true) do - if component.invoke(c, "getLabel") == filter then - address = c - break - end - if c:sub(1, filter:len()) == filter then - address = c - break - end + if not component.list("filesystem")[filter] then + -- if not, load fs full library, it has a smarter proxy that also supports options + return filesystem.internal.proxy(filter, options) end - if not address then - return nil, "no such file system" - end - return component.proxy(address) + return component.proxy(filter) -- it might be a perfect match end function filesystem.exists(path) diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua index 394247935..3f356ebff 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua @@ -72,9 +72,9 @@ end function package.delay(lib, file) local mt = { __index = function(tbl, key) - dofile(file) setmetatable(lib, nil) setmetatable(lib.internal or {}, nil) + dofile(file) return tbl[key] end } diff --git a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount index 491f15777..1236216d1 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount +++ b/src/main/resources/assets/opencomputers/loot/openos/usr/man/mount @@ -5,9 +5,12 @@ SYNOPSIS mount mount LABEL PATH mount ADDRESS PATH + mount --bind PATH PATH OPTIONS - -r mount filesystem readonly + -r, --readonly mount filesystem readonly + --bind mount a bind point (folder to folder) + -h, --help print help message DESCRIPTION All files accessible in OpenOS are arranged in one big tree, starting with the root node, '/'. The files are the leaves of the tree, directories are inner nodes of the tree. Files can be distributed across several devices (file system components, such as hard drives and floppies). The `mount` command is used to attach a file system to this tree. The `umount` command can be used to remove a mounted file system from the tree (note that `rm` works for this, too). @@ -22,5 +25,8 @@ EXAMPLES mount 56f /var Mounts the file system of which the address starts with `56f` at `/var`. - mount -r tmpfs /tmp_ro - Mounts a readonly access path of tmpfs to /tmp_ro \ No newline at end of file + mount --readonly tmpfs /tmp_ro + Mounts a readonly access path of tmpfs to /tmp_ro + + mount --bind /mnt/fa4/home /home + Mounts /mnt/fa5/home to /home From 5f3088c4898a3737a544ebceef53f3644697a44e Mon Sep 17 00:00:00 2001 From: payonel Date: Wed, 15 Nov 2017 22:57:53 -0800 Subject: [PATCH 06/12] more efficient autorun --- .../loot/openos/boot/90_filesystem.lua | 40 +++++++------------ .../opencomputers/loot/openos/lib/event.lua | 2 +- 2 files changed, 15 insertions(+), 27 deletions(-) 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 5e21ed7ed..044c4b070 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 @@ -1,29 +1,12 @@ -local component = require("component") local event = require("event") local fs = require("filesystem") local shell = require("shell") -local isInitialized, pendingAutoruns = false, {} - -local function onInit() - isInitialized = true - for _, run in ipairs(pendingAutoruns) do - local result, reason = pcall(run) - if not result then - local path = fs.concat(os.getenv("TMPDIR") or "/tmp", "event.log") - local log = io.open(path, "a") - if log then - log:write(tostring(result) .. ":" .. tostring(reason) .. "\n") - log:close() - end - end - end - pendingAutoruns = nil -end +local pendingAutoruns = {} local function onComponentAdded(_, address, componentType) if componentType == "filesystem" and require("computer").tmpAddress() ~= address then - local proxy = component.proxy(address) + local proxy = fs.proxy(address) if proxy then local name = address:sub(1, 3) while fs.exists(fs.concat("/mnt", name)) and @@ -37,13 +20,11 @@ local function onComponentAdded(_, address, componentType) local file = shell.resolve(fs.concat(name, "autorun"), "lua") or shell.resolve(fs.concat(name, ".autorun"), "lua") if file then - local run = function() - assert(shell.execute(file, _ENV, proxy)) - end - if isInitialized then - run() - else + local run = {file, _ENV, proxy} + if pendingAutoruns then table.insert(pendingAutoruns, run) + else + xpcall(shell.execute, event.onError, table.unpack(run)) end end end @@ -60,7 +41,14 @@ local function onComponentRemoved(_, address, componentType) end end -event.listen("init", onInit) +event.listen("init", function() + for _, run in ipairs(pendingAutoruns) do + xpcall(shell.execute, event.onError, table.unpack(run)) + end + pendingAutoruns = nil + return false +end) + event.listen("component_added", onComponentAdded) event.listen("component_removed", onComponentRemoved) 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 b34ce0287..2a41f0a80 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua @@ -145,7 +145,7 @@ end function event.onError(message) local log = io.open("/tmp/event.log", "a") if log then - pcall(log.write, log, message, "\n") + pcall(log.write, log, tostring(message), "\n") log:close() end end From 72009900483f0228eb145d957edd4fb297fba5a7 Mon Sep 17 00:00:00 2001 From: payonel Date: Wed, 15 Nov 2017 23:08:08 -0800 Subject: [PATCH 07/12] mount bind cleanup --- .../opencomputers/loot/openos/lib/core/full_filesystem.lua | 7 +++---- .../assets/opencomputers/loot/openos/lib/filesystem.lua | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) 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 bfa44f849..1be34ca5b 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 @@ -169,6 +169,9 @@ local function bind_proxy(path) if not real then return nil, reason end + if not filesystem.isDirectory(real) then + return nil, "must bind to a directory" + end local real_fs, real_fs_path = filesystem.get(real) if real == real_fs_path then return real_fs @@ -195,10 +198,6 @@ local function bind_proxy(path) getLabel = function() return "" end, setLabel = function() return nil, "cannot set the label of a bind point" end, } - setmetatable(bind, { __index = function(...) - print("missing bind method", ...) - end - }) return bind end 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 b35cc598a..bdb315026 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/filesystem.lua @@ -236,7 +236,7 @@ end function filesystem.proxy(filter, options) checkArg(1, filter, "string") - if not component.list("filesystem")[filter] then + if not component.list("filesystem")[filter] or next(options or {}) then -- if not, load fs full library, it has a smarter proxy that also supports options return filesystem.internal.proxy(filter, options) end From 5107afb176355635fec05dbbabdb108b61874b2d Mon Sep 17 00:00:00 2001 From: payonel Date: Wed, 15 Nov 2017 23:53:22 -0800 Subject: [PATCH 08/12] fixes for bind remove and cleanup --- .../assets/opencomputers/loot/openos/boot/02_os.lua | 2 +- .../assets/opencomputers/loot/openos/boot/03_io.lua | 2 +- .../opencomputers/loot/openos/boot/90_filesystem.lua | 3 ++- .../opencomputers/loot/openos/lib/core/full_filesystem.lua | 2 ++ .../opencomputers/loot/openos/lib/core/install_basics.lua | 7 ++++++- 5 files changed, 12 insertions(+), 4 deletions(-) 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 6bf181c4c..5a40d04bf 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 @@ -62,5 +62,5 @@ os.setenv("TMP", "/tmp") -- Deprecated os.setenv("TMPDIR", "/tmp") if computer.tmpAddress() then - fs.mount(computer.tmpAddress(), os.getenv("TMPDIR") or "/tmp") + fs.mount(computer.tmpAddress(), "/tmp") end diff --git a/src/main/resources/assets/opencomputers/loot/openos/boot/03_io.lua b/src/main/resources/assets/opencomputers/loot/openos/boot/03_io.lua index f223b0d00..45333ccb0 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/boot/03_io.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/boot/03_io.lua @@ -8,7 +8,7 @@ local core_stderr = buffer.new("w", setmetatable( write = function(_, str) return tty_stream:write("\27[31m"..str.."\27[37m") end - }, {__index=tty_stream})) +}, {__index=tty_stream})) core_stdout:setvbuf("no") core_stderr:setvbuf("no") 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 044c4b070..03506eaa1 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 @@ -1,11 +1,12 @@ local event = require("event") local fs = require("filesystem") local shell = require("shell") +local tmp = require("computer").tmpAddress() local pendingAutoruns = {} local function onComponentAdded(_, address, componentType) - if componentType == "filesystem" and require("computer").tmpAddress() ~= address then + if componentType == "filesystem" and tmp ~= address then local proxy = fs.proxy(address) if proxy then local name = address:sub(1, 3) 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 1be34ca5b..108373619 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 @@ -184,6 +184,7 @@ local function bind_proxy(path) end end local bind = { + type = "filesystem_bind", address = real, isReadOnly = real_fs.isReadOnly, list = wrap_relative(real_fs.list), @@ -192,6 +193,7 @@ local function bind_proxy(path) lastModified = wrap_relative(real_fs.lastModified), exists = wrap_relative(real_fs.exists), open = wrap_relative(real_fs.open), + remove = wrap_relative(real_fs.remove), read = real_fs.read, write = real_fs.write, close = real_fs.close, diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua index fd6bfa30e..ef6f57346 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/install_basics.lua @@ -61,10 +61,15 @@ local targets = {} -- tmpfs is not a candidate unless it is specified +local comps = require("component").list("filesystem") local devices = {} +-- not all mounts are components, only use components for dev, path in fs.mounts() do - devices[dev] = devices[dev] and #devices[dev] < #path and devices[dev] or path + if comps[dev.address] then + local known = devices[dev] + devices[dev] = known and #known < #path and known or path + end end local dev_dev = fs.get("/dev") From f26cb54c28a29c1a4d6a788eefae06e11798e676 Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 16 Nov 2017 18:40:58 -0800 Subject: [PATCH 09/12] allow .shrc to use tty stdin --- .../opencomputers/loot/openos/bin/source.lua | 23 ++++++++++++------- .../opencomputers/loot/openos/etc/profile.lua | 2 +- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua index 8286ad1ed..58d8f2075 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/source.lua @@ -17,13 +17,20 @@ if not file then return 1 end -local current_data = process.info().data +local lines = file:lines() -local source_proc = process.load((assert(os.getenv("SHELL"), "no $SHELL set"))) -local source_data = process.list[source_proc].data -source_data.aliases = current_data.aliases -- hacks to propogate sub shell env changes -source_data.vars = current_data.vars -source_data.io[0] = file -- set stdin to the file -process.internal.continue(source_proc, "-c") +while true do + local line = lines() + if not line then + break + end + local current_data = process.info().data + + local source_proc = process.load((assert(os.getenv("SHELL"), "no $SHELL set"))) + local source_data = process.list[source_proc].data + source_data.aliases = current_data.aliases -- hacks to propogate sub shell env changes + source_data.vars = current_data.vars + process.internal.continue(source_proc, _ENV, line) +end -file:close() -- should have closed when the process closed, but just to be sure +file:close() diff --git a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua index 0fe8487e2..bc66180bd 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua @@ -38,6 +38,6 @@ os.setenv("LS_COLORS", "di=0;36:fi=0:ln=0;33:*.lua=0;32") shell.setWorkingDirectory(os.getenv("HOME")) local home_shrc = shell.resolve(".shrc") -if fs.exists(home_shrc) then +if fs.size(home_shrc) > 0 then loadfile(shell.resolve("source", "lua"))(home_shrc) end From e742af6112fb3e5313aa2ae52c590f56e50a3afd Mon Sep 17 00:00:00 2001 From: payonel Date: Fri, 17 Nov 2017 14:09:21 -0800 Subject: [PATCH 10/12] fs.size loads more fs lib, should keep using exists --- .../resources/assets/opencomputers/loot/openos/etc/profile.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua index bc66180bd..0fe8487e2 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/etc/profile.lua @@ -38,6 +38,6 @@ os.setenv("LS_COLORS", "di=0;36:fi=0:ln=0;33:*.lua=0;32") shell.setWorkingDirectory(os.getenv("HOME")) local home_shrc = shell.resolve(".shrc") -if fs.size(home_shrc) > 0 then +if fs.exists(home_shrc) then loadfile(shell.resolve("source", "lua"))(home_shrc) end From 5b0c085c2d12a82ea1773e7e54985f5451c340dc Mon Sep 17 00:00:00 2001 From: SquidDev Date: Wed, 22 Nov 2017 19:19:33 +0000 Subject: [PATCH 11/12] Miscellaneous tweaks to OpenOS (#2636) * Parse Lua REPL inputs with an implicit "return " If an input does not start with a leading "=", this will parse the input with "return " appended and, if that fails, will parse as a normal statement. This allows for normal expressions to be entered into the repl (such as `2 + 2`) but does mean the parse errors for malformed inputs are confusing. For instance, `3 + ` will error at '3' rather than ''. * Do not insert into history if a duplicate This mimics the behaviour of shells such as bash or zsh, where whitespace-only lines are not entered into history, nor are ones equal to the previous input. This makes history navigation slightly easier. --- .../assets/opencomputers/loot/openos/lib/core/lua_shell.lua | 5 ++++- .../resources/assets/opencomputers/loot/openos/lib/tty.lua | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) 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 48bdeeacd..bcff783ca 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 @@ -101,7 +101,10 @@ while tty.isAvailable() do if string.sub(command, 1, 1) == "=" then code, reason = load("return " .. string.sub(command, 2), "=stdin", "t", env) else - code, reason = load(command, "=stdin", "t", env) + code, reason = load("return " .. command, "=stdin", "t", env) + if not code then + code, reason = load(command, "=stdin", "t", env) + end end if code then local result = table.pack(xpcall(code, debug.traceback)) 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 bdfca7144..820585a1f 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua @@ -32,11 +32,11 @@ function tty.key_down_handler(handler, cursor, char, code) elseif code == keys.enter or code == keys.numpadenter then cursor:move(math.huge) cursor:draw("\n") - if #data > 0 then + if data:find("%S") and data ~= handler[1] then table.insert(handler, 1, data) handler[(tonumber(os.getenv("HISTSIZE")) or 10)+1]=nil - handler[0]=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) From 7ec85999da6d8565718de9bdabaac45fcf845ef5 Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 23 Nov 2017 13:02:55 -0800 Subject: [PATCH 12/12] saving more memory and improving the vt100 library move all vt100 code to vt100 library delay load event rare code fix shell parse for %d>&%d not followed by whitespace remove weird tty blink code and use vt100 codes bump openos patch version --- .../assets/opencomputers/loot/openos/etc/motd | 33 +-- .../opencomputers/loot/openos/lib/buffer.lua | 10 +- .../loot/openos/lib/core/boot.lua | 2 +- .../loot/openos/lib/core/full_event.lua | 61 +++++ .../loot/openos/lib/core/full_text.lua | 40 +++ .../loot/openos/lib/core/full_vt.lua | 93 +++++++ .../opencomputers/loot/openos/lib/event.lua | 60 +---- .../opencomputers/loot/openos/lib/text.lua | 42 +--- .../opencomputers/loot/openos/lib/tty.lua | 84 ++----- .../opencomputers/loot/openos/lib/vt100.lua | 229 +++++++----------- 10 files changed, 325 insertions(+), 329 deletions(-) create mode 100644 src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua create mode 100644 src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua diff --git a/src/main/resources/assets/opencomputers/loot/openos/etc/motd b/src/main/resources/assets/opencomputers/loot/openos/etc/motd index 85ec4759f..da961297b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/etc/motd +++ b/src/main/resources/assets/opencomputers/loot/openos/etc/motd @@ -1,35 +1,38 @@ local component = require("component") local computer = require("computer") -local text = require("text") local unicode = require("unicode") local tty = require("tty") if not component.isAvailable("gpu") then return end -local lines = {_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)"} -local maxWidth = unicode.len(lines[1]) + local f = io.open("/usr/misc/greetings.txt") +local lines = {_OSVERSION .. " (" .. math.floor(computer.totalMemory() / 1024) .. "k RAM)"} +local greeting = "" if f then local greetings = {} pcall(function() for line in f:lines() do table.insert(greetings, line) end end) f:close() - local greeting = greetings[math.random(1, #greetings)] - if greeting then - local width = math.max(10, tty.getViewport()) - for line in text.wrappedLines(greeting, width - 4, width - 4) do - table.insert(lines, line) - maxWidth = math.max(maxWidth, unicode.len(line)) - end - end + greeting = greetings[math.random(1, math.max(#greetings, 1))] or "" end +local width = math.min(#greeting, tty.getViewport() - 5) +local maxLine = #lines[1] +while #greeting > 0 do + local si, ei = greeting:sub(1, width):find("%s%S*$") + local line = #greeting <= width and greeting or greeting:sub(1, si or width) + lines[#lines + 1] = line + maxLine = math.max(maxLine, #line) + greeting = greeting:sub(#line + 1) +end + local borders = {{unicode.char(0x2552), unicode.char(0x2550), unicode.char(0x2555)}, {unicode.char(0x2502), nil, unicode.char(0x2502)}, {unicode.char(0x2514), unicode.char(0x2500), unicode.char(0x2518)}} -io.write(borders[1][1] .. string.rep(borders[1][2], maxWidth + 2) .. borders[1][3] .. "\n") -for _, line in ipairs(lines) do - io.write(borders[2][1] .. " " .. text.padRight(line, maxWidth) .. " " .. borders[2][3] .. "\n") +io.write(borders[1][1], string.rep(borders[1][2], maxLine + 2), borders[1][3], "\n") +for _,line in ipairs(lines) do + io.write(borders[2][1], " ", line, (" "):rep(maxLine - #line + 1), borders[2][3], " \n") end -io.write(borders[3][1] .. string.rep(borders[3][2], maxWidth + 2) .. borders[3][3] .. "\n") +io.write(borders[3][1] .. string.rep(borders[3][2], maxLine + 2) .. borders[3][3] .. "\n") diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua index 384901d5c..2d128df54 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua @@ -39,14 +39,8 @@ function buffer:flush() local tmp = self.bufferWrite self.bufferWrite = "" local result, reason = self.stream:write(tmp) - if result then - self.bufferWrite = "" - else - if reason then - return nil, reason - else - return nil, "bad file descriptor" - end + if not result then + return nil, reason or "bad file descriptor" end end 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 8f8bcd536..351582e29 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 @@ -1,7 +1,7 @@ -- called from /init.lua local raw_loadfile = ... -_G._OSVERSION = "OpenOS 1.7.0" +_G._OSVERSION = "OpenOS 1.7.1" local component = component local computer = computer 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 new file mode 100644 index 000000000..396ebbc9a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua @@ -0,0 +1,61 @@ +local event = require("event") + +local function createMultipleFilter(...) + local filter = table.pack(...) + if filter.n == 0 then + return nil + end + + return function(...) + local signal = table.pack(...) + if type(signal[1]) ~= "string" then + return false + end + for i = 1, filter.n do + if filter[i] ~= nil and signal[1]:match(filter[i]) then + return true + end + end + return false + end +end + +function event.pullMultiple(...) + local seconds + local args + if type(...) == "number" then + seconds = ... + args = table.pack(select(2,...)) + for i=1,args.n do + checkArg(i+1, args[i], "string", "nil") + end + else + args = table.pack(...) + for i=1,args.n do + checkArg(i, args[i], "string", "nil") + end + end + return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n))) +end + +function event.cancel(timerId) + checkArg(1, timerId, "number") + if handlers[timerId] then + handlers[timerId] = nil + return true + end + return false +end + +function event.ignore(name, callback) + checkArg(1, name, "string") + checkArg(2, callback, "function") + for id, handler in pairs(handlers) do + if handler.key == name and handler.callback == callback then + handlers[id] = nil + return true + end + end + return false +end + 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 6fdca240a..dc7f2eb4d 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 @@ -244,3 +244,43 @@ function text.padLeft(value, length) return string.rep(" ", length - unicode.wlen(value)) .. value end end + +function text.padRight(value, length) + checkArg(1, value, "string", "nil") + checkArg(2, length, "number") + if not value or unicode.wlen(value) == 0 then + return string.rep(" ", length) + else + return value .. string.rep(" ", length - unicode.wlen(value)) + end +end + +function text.wrap(value, width, maxWidth) + checkArg(1, value, "string") + checkArg(2, width, "number") + checkArg(3, maxWidth, "number") + local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline + if unicode.wlen(line) > width then -- do we even need to wrap? + local partial = unicode.wtrunc(line, width) + local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])") + if wrapped or unicode.wlen(line) > maxWidth then + partial = wrapped or partial + return partial, unicode.sub(value, unicode.len(partial) + 1), true + else + return "", value, true -- write in new line. + end + end + local start = unicode.len(line) + unicode.len(nl) + 1 + return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0 +end + +function text.wrappedLines(value, width, maxWidth) + local line + return function() + if value then + line, value = text.wrap(value, width, maxWidth) + return line + end + end +end + 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 new file mode 100644 index 000000000..a69b4b6a9 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_vt.lua @@ -0,0 +1,93 @@ +local vt100 = require("vt100") + +local rules = vt100.rules + +-- [?7[hl] wrap mode +rules[{"%[", "%?", "7", "[hl]"}] = function(window, _, _, _, nowrap) + window.nowrap = nowrap == "l" +end + +-- helper scroll function +local function set_cursor(window, x, y) + window.x = math.min(math.max(x, 1), window.width) + window.y = math.min(math.max(y, 1), window.height) +end + +-- -- These DO NOT SCROLL +-- [(%d+)A move cursor up n lines +-- [(%d+)B move cursor down n lines +-- [(%d+)C move cursor right n lines +-- [(%d+)D move cursor left n lines +rules[{"%[", "%d+", "[ABCD]"}] = function(window, _, n, dir) + local dx, dy = 0, 0 + n = tonumber(n) + if dir == "A" then + dy = -n + elseif dir == "B" then + dy = n + elseif dir == "C" then + dx = n + else -- D + dx = -n + end + set_cursor(window, window.x + dx, window.y + dy) +end + +-- [Line;ColumnH Move cursor to screen location v,h +-- [Line;Columnf ^ same +rules[{"%[", "%d+", ";", "%d+", "[Hf]"}] = function(window, _, y, _, x) + set_cursor(window, tonumber(x), tonumber(y)) +end + +-- [K clear line from cursor right +-- [0K ^ same +-- [1K clear line from cursor left +-- [2K clear entire line +local function clear_line(window, _, n) + n = tonumber(n) or 0 + local x = n == 0 and window.x or 1 + local rep = n == 1 and window.x or window.width + window.gpu.set(x, window.y, (" "):rep(rep)) +end +rules[{"%[", "[012]?", "K"}] = clear_line + +-- [J clear screen from cursor down +-- [0J ^ same +-- [1J clear screen from cursor up +-- [2J clear entire screen +rules[{"%[", "[012]?", "J"}] = function(window, _, n) + clear_line(window, _, n) + n = tonumber(n) or 0 + local y = n == 0 and (window.y + 1) or 1 + local rep = n == 1 and (window.y - 1) or window.height + window.gpu.fill(1, y, window.width, rep, " ") +end + +-- [H move cursor to upper left corner +-- [;H ^ same +-- [f ^ same +-- [;f ^ same +rules[{"%[;?", "[Hf]"}] = function(window) + set_cursor(window, 1, 1) +end + +-- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ] +rules[{"%[", "6", "n"}] = function(window) + -- this solution puts the response on stdin, but it isn't echo'd + -- I'm personally fine with the lack of echo + io.stdin.bufferRead = string.format("%s%s%d;%dR", io.stdin.bufferRead, string.char(0x1b), window.y, window.x) +end + +-- D scroll up one line -- moves cursor down +-- E move to next line (acts the same ^, but x=1) +-- M scroll down one line -- moves cursor up +rules[{"[DEM]"}] = function(window, dir) + if dir == "D" then + window.y = window.y + 1 + elseif dir == "E" then + window.y = window.y + 1 + window.x = 1 + else -- M + window.y = window.y - 1 + end +end 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 2a41f0a80..52ad8761b 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/event.lua @@ -89,48 +89,8 @@ local function createPlainFilter(name, ...) end end -local function createMultipleFilter(...) - local filter = table.pack(...) - if filter.n == 0 then - return nil - end - - return function(...) - local signal = table.pack(...) - if type(signal[1]) ~= "string" then - return false - end - for i = 1, filter.n do - if filter[i] ~= nil and signal[1]:match(filter[i]) then - return true - end - end - return false - end -end ------------------------------------------------------------------------------- -function event.cancel(timerId) - checkArg(1, timerId, "number") - if handlers[timerId] then - handlers[timerId] = nil - return true - end - return false -end - -function event.ignore(name, callback) - checkArg(1, name, "string") - checkArg(2, callback, "function") - for id, handler in pairs(handlers) do - if handler.key == name and handler.callback == callback then - handlers[id] = nil - return true - end - end - return false -end - function event.listen(name, callback) checkArg(1, name, "string") checkArg(2, callback, "function") @@ -161,24 +121,6 @@ function event.pull(...) end end -function event.pullMultiple(...) - local seconds - local args - if type(...) == "number" then - seconds = ... - args = table.pack(select(2,...)) - for i=1,args.n do - checkArg(i+1, args[i], "string", "nil") - end - else - args = table.pack(...) - for i=1,args.n do - checkArg(i, args[i], "string", "nil") - end - end - return event.pullFiltered(seconds, createMultipleFilter(table.unpack(args, 1, args.n))) -end - function event.pullFiltered(...) local args = table.pack(...) local seconds, filter @@ -217,6 +159,8 @@ end -- users may expect to find event.push to exist event.push = computer.pushSignal +require("package").delay(event, "/lib/core/full_event.lua") + ------------------------------------------------------------------------------- return event diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua index 7c77f4041..5af1187c9 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/text.lua @@ -4,53 +4,13 @@ local tx = require("transforms") local text = {} text.internal = {} -text.syntax = {"^%d?>>?&%d+$","^%d?>>?",">>?","<%&%d+","<",";","&&","||?"} +text.syntax = {"^%d?>>?&%d+","^%d?>>?",">>?","<%&%d+","<",";","&&","||?"} function text.trim(value) -- from http://lua-users.org/wiki/StringTrim local from = string.match(value, "^%s*()") return from > #value and "" or string.match(value, ".*%S", from) end --- used in motd -function text.padRight(value, length) - checkArg(1, value, "string", "nil") - checkArg(2, length, "number") - if not value or unicode.wlen(value) == 0 then - return string.rep(" ", length) - else - return value .. string.rep(" ", length - unicode.wlen(value)) - end -end - -function text.wrap(value, width, maxWidth) - checkArg(1, value, "string") - checkArg(2, width, "number") - checkArg(3, maxWidth, "number") - local line, nl = value:match("([^\r\n]*)(\r?\n?)") -- read until newline - if unicode.wlen(line) > width then -- do we even need to wrap? - local partial = unicode.wtrunc(line, width) - local wrapped = partial:match("(.*[^a-zA-Z0-9._()'`=])") - if wrapped or unicode.wlen(line) > maxWidth then - partial = wrapped or partial - return partial, unicode.sub(value, unicode.len(partial) + 1), true - else - return "", value, true -- write in new line. - end - end - local start = unicode.len(line) + unicode.len(nl) + 1 - return line, start <= unicode.len(value) and unicode.sub(value, start) or nil, unicode.len(nl) > 0 -end - -function text.wrappedLines(value, width, maxWidth) - local line - return function() - if value then - line, value = text.wrap(value, width, maxWidth) - return line - end - end -end - -- used by lib/sh function text.escapeMagic(txt) return txt:gsub('[%(%)%.%%%+%-%*%?%[%^%$]', '%%%1') 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 820585a1f..c7ad2093c 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua @@ -101,49 +101,23 @@ function tty.isAvailable() return not not (gpu and gpu.getScreen()) end -function tty.stream:blink(done) - local width, height, dx, dy, x, y = tty.getViewport() - local gpu = tty.gpu() - if not gpu or x < 1 or x > width or y < 1 or y > height then - return true - end - x, y = x + dx, y + dy - local blinked, bgColor, bgIsPalette, fgColor, fgIsPalette, char_at_cursor = table.unpack(self.blink_cache or {}) - if done == nil then -- reset - blinked = false - bgColor, bgIsPalette = gpu.getBackground() - -- it can happen during a type of race condition when a screen is removed - if not bgColor then - return - end - - fgColor, fgIsPalette = gpu.getForeground() - char_at_cursor = gpu.get(x, y) - end - - if not blinked and not done then - gpu.setForeground(bgColor, bgIsPalette) - gpu.setBackground(fgColor, fgIsPalette) - gpu.set(x, y, char_at_cursor) - gpu.setForeground(fgColor, fgIsPalette) - gpu.setBackground(bgColor, bgIsPalette) - blinked = true - elseif blinked and (done or tty.window.blink) then - gpu.set(x, y, char_at_cursor) - blinked = false - end - - self.blink_cache = table.pack(blinked, bgColor, bgIsPalette, fgColor, fgIsPalette, char_at_cursor) - return true -end - function tty.stream:pull(timeout, ...) timeout = timeout or math.huge local blink_timeout = tty.window.blink and .5 or math.huge - -- it can happen during a type of race condition when a screen is removed - if not self:blink() then - return nil, "interrupted" + 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 @@ -152,7 +126,15 @@ function tty.stream:pull(timeout, ...) timeout = timeout - blink_timeout local done = signal.n > 1 or timeout < blink_timeout - self:blink(done) + if gpu then + if not blinked and not done then + io.write("\0277\27[7m", char_at_cursor, "\0278") + blinked = true + elseif blinked and (done or tty.window.blink) then + io.write("\0277", char_at_cursor, "\0278") + blinked = false + end + end if done then return table.unpack(signal, 1, signal.n) @@ -336,27 +318,7 @@ function tty.stream:write(value) -- parse the instruction in segment -- [ (%d+;)+ %d+m window.ansi_escape = window.ansi_escape .. value - local color_attributes = {tonumber(window.ansi_escape:match("^%[(%d%d)m"))} - if not color_attributes[1] then - color_attributes, ansi_print, value = require("vt100").parse(window) - else - value = window.ansi_escape:sub(5) - end - for _,catt in ipairs(color_attributes) do - -- B6 is closer to cyan in 4 bit color - local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00B6ff,0xffffff} - catt = catt - 29 - local method = "setForeground" - if catt > 10 then - method = "setBackground" - catt = catt - 10 - end - local c = colors[catt] - if c then - gpu[method](c) - end - window.ansi_escape = nil -- might happen multiple times, that's fine - end + value, ansi_print = require("vt100").parse(window) end -- scroll before parsing next line 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 fc06ad8f6..fce23034d 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua @@ -1,190 +1,129 @@ local text = require("text") -local vt100 = {} - --- runs patterns on ansi until failure --- returns valid:boolean, completed_index:nil|number -function vt100.validate(ansi, patterns) - local last_index = 0 - local captures = {} - for _,pattern in ipairs(patterns) do - if last_index >= #ansi then - return true - end - local si, ei, capture = ansi:find("^(" .. pattern .. ")", last_index + 1) - if not si then -- failed to match - return - end - captures[#captures + 1] = capture - last_index = ei - end - return true, last_index, captures -end local rules = {} +local vt100 = {rules=rules} +local full --- colors +-- colors, blinking, and reverse -- [%d+;%d+;..%d+m -rules[{"%[", "[%d;]*", "m"}] = function(_, _, number_text) - local numbers = {} +-- cost: 2,250 +rules[{"%[", "[%d;]*", "m"}] = function(window, _, number_text) -- prefix and suffix ; act as reset -- e.g. \27[41;m is actually 41 followed by a reset - number_text = ";" .. number_text:gsub("^;$","") .. ";" - local parts = text.split(number_text, {";"}) + local colors = {0x0,0xff0000,0x00ff00,0xffff00,0x0000ff,0xff00ff,0x00B6ff,0xffffff} + local fg, bg = window.gpu.setForeground, window.gpu.setBackground + if window.flip then + fg, bg = bg, fg + end + number_text = " _ " .. number_text:gsub("^;$", ""):gsub(";", " _ ") .. " _ " + local parts = text.internal.tokenize(number_text) local last_was_break for _,part in ipairs(parts) do - local num = tonumber(part) - if not num then - num = last_was_break and 0 - last_was_break = true - else - last_was_break = false - end - if num == 0 then - numbers[#numbers + 1] = 40 - numbers[#numbers + 1] = 37 + local num = tonumber(part[1].txt) + last_was_break, num = not num, num or last_was_break and 0 + + if num == 7 then + if not window.flip then + fg(bg(window.gpu.getForeground())) + fg, bg = bg, fg + end + window.flip = true + elseif num == 5 then + window.blink = true + elseif num == 0 then + bg(colors[1]) + fg(colors[8]) elseif num then - numbers[#numbers + 1] = num + num = num - 29 + local set = fg + if num > 10 then + num = num - 10 + set = bg + end + local color = colors[num] + if color then + set(color) + end end end - return numbers end --- [?7[hl] wrap mode -rules[{"%[", "%?", "7", "[hl]"}] = function(window, _, _, _, nowrap) - window.nowrap = nowrap == "l" -end - --- helper scroll function -local function set_cursor(window, x, y) - window.x = math.min(math.max(x, 1), window.width) - window.y = math.min(math.max(y, 1), window.height) -end - --- -- These DO NOT SCROLL --- [(%d+)A move cursor up n lines --- [(%d+)B move cursor down n lines --- [(%d+)C move cursor right n lines --- [(%d+)D move cursor left n lines -rules[{"%[", "%d+", "[ABCD]"}] = function(window, _, n, dir) - local dx, dy = 0, 0 - n = tonumber(n) - if dir == "A" then - dy = -n - elseif dir == "B" then - dy = n - elseif dir == "C" then - dx = n - else -- D - dx = -n - end - set_cursor(window, window.x + dx, window.y + dy) -end - --- [Line;ColumnH Move cursor to screen location v,h --- [Line;Columnf ^ same -rules[{"%[", "%d+", ";", "%d+", "[Hf]"}] = function(window, _, y, _, x) - set_cursor(window, tonumber(x), tonumber(y)) -end - --- [K clear line from cursor right --- [0K ^ same --- [1K clear line from cursor left --- [2K clear entire line -local function clear_line(window, _, n) - n = tonumber(n) or 0 - local x = n == 0 and window.x or 1 - local rep = n == 1 and window.x or window.width - window.gpu.set(x, window.y, (" "):rep(rep)) -end -rules[{"%[", "[012]?", "K"}] = clear_line - --- [J clear screen from cursor down --- [0J ^ same --- [1J clear screen from cursor up --- [2J clear entire screen -rules[{"%[", "[012]?", "J"}] = function(window, _, n) - clear_line(window, _, n) - n = tonumber(n) or 0 - local y = n == 0 and (window.y + 1) or 1 - local rep = n == 1 and (window.y - 1) or window.height - window.gpu.fill(1, y, window.width, rep, " ") -end - --- [H move cursor to upper left corner --- [;H ^ same --- [f ^ same --- [;f ^ same -rules[{"%[;?", "[Hf]"}] = function(window) - set_cursor(window, 1, 1) -end - --- [6n get the cursor position [ EscLine;ColumnR Response: cursor is at v,h ] -rules[{"%[", "6", "n"}] = function(window) - -- this solution puts the response on stdin, but it isn't echo'd - -- I'm personally fine with the lack of echo - io.stdin.bufferRead = string.format("%s%s%d;%dR", io.stdin.bufferRead, string.char(0x1b), window.y, window.x) -end - --- D scroll up one line -- moves cursor down --- E move to next line (acts the same ^, but x=1) --- M scroll down one line -- moves cursor up -rules[{"[DEM]"}] = function(window, dir) - if dir == "D" then - window.y = window.y + 1 - elseif dir == "E" then - window.y = window.y + 1 - window.x = 1 - else -- M - window.y = window.y - 1 - end -end - -local function save_attributes(window, save) - if save then - local data = window.saved or {1, 1, {0x0}, {0xffffff}} +local function save_attributes(window, seven, s) + if seven == "7" or s == "s" then + window.saved = + { + window.x, + window.y, + {window.gpu.getBackground()}, + {window.gpu.getForeground()}, + window.flip, + window.blink + } + else + local data = window.saved or {1, 1, {0x0}, {0xffffff}, window.flip, window.blink} window.x = data[1] window.y = data[2] window.gpu.setBackground(table.unpack(data[3])) window.gpu.setForeground(table.unpack(data[4])) - else - window.saved = {window.x, window.y, {window.gpu.getBackground()}, {window.gpu.getForeground()}} + window.flip = data[5] + window.blink = data[6] end end -- 7 save cursor position and attributes -- 8 restore cursor position and attributes -rules[{"[78]"}] = function(window, restore) - save_attributes(window, restore == "8") -end +rules[{"[78]"}] = save_attributes -- s save cursor position -- u restore cursor position -rules[{"%[", "[su]"}] = function(window, _, restore) - save_attributes(window, restore == "u") -end +rules[{"%[", "[su]"}] = save_attributes +-- returns 2 values +-- value: parsed text +-- ansi_print: failed to parse function vt100.parse(window) local ansi = window.ansi_escape window.ansi_escape = nil local any_valid for rule,action in pairs(rules) do - local ok, completed, captures = vt100.validate(ansi, rule) - if completed then - return action(window, table.unpack(captures)) or {}, "", ansi:sub(completed + 1) - elseif ok then - any_valid = true + local last_index = 0 + local captures = {} + for _,pattern in ipairs(rule) do + if last_index >= #ansi then + any_valid = true + break + end + local si, ei, capture = ansi:find("^(" .. pattern .. ")", last_index + 1) + if not si then + break + end + captures[#captures + 1] = capture + last_index = ei end + + if #captures == #rule then + action(window, table.unpack(captures)) + return ansi:sub(last_index + 1), "" + end + end + + if not full then + -- maybe it did satisfy a rule, load more rules + full = true + dofile("/lib/core/full_vt.lua") + window.ansi_escape = ansi + return vt100.parse(window) end if not any_valid then -- malformed - return {}, string.char(0x1b), ansi + return ansi, "\27" end -- else, still consuming window.ansi_escape = ansi - return {}, "", "" + return "", "" end return vt100