From 8ff73598db33a07ec76f808dafac089958d1ba3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 18 Jun 2015 22:03:35 +0200 Subject: [PATCH 1/6] Added Plan9k loot disk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Magiera --- .../opencomputers/loot/Plan9k/bin/arp.lua | 25 + .../opencomputers/loot/Plan9k/bin/cat.lua | 25 + .../opencomputers/loot/Plan9k/bin/clear.lua | 3 + .../loot/Plan9k/bin/components.lua | 52 ++ .../opencomputers/loot/Plan9k/bin/cp.lua | 140 ++++ .../opencomputers/loot/Plan9k/bin/dd.lua | 76 +++ .../opencomputers/loot/Plan9k/bin/df.lua | 71 ++ .../opencomputers/loot/Plan9k/bin/dmesg.lua | 32 + .../opencomputers/loot/Plan9k/bin/echo.lua | 8 + .../opencomputers/loot/Plan9k/bin/getty.lua | 311 +++++++++ .../loot/Plan9k/bin/hostname.lua | 21 + .../loot/Plan9k/bin/ifconfig.lua | 26 + .../opencomputers/loot/Plan9k/bin/init.lua | 111 ++++ .../opencomputers/loot/Plan9k/bin/install.lua | 77 +++ .../opencomputers/loot/Plan9k/bin/kill.lua | 8 + .../opencomputers/loot/Plan9k/bin/ln.lua | 23 + .../opencomputers/loot/Plan9k/bin/ls.lua | 110 ++++ .../opencomputers/loot/Plan9k/bin/lua.lua | 39 ++ .../opencomputers/loot/Plan9k/bin/mv.lua | 27 + .../loot/Plan9k/bin/pastebin.lua | 153 +++++ .../opencomputers/loot/Plan9k/bin/ping.lua | 107 +++ .../opencomputers/loot/Plan9k/bin/ps.lua | 6 + .../opencomputers/loot/Plan9k/bin/rc.lua | 13 + .../opencomputers/loot/Plan9k/bin/readkey.lua | 191 ++++++ .../opencomputers/loot/Plan9k/bin/reboot.lua | 4 + .../opencomputers/loot/Plan9k/bin/rm.lua | 18 + .../opencomputers/loot/Plan9k/bin/route.lua | 25 + .../opencomputers/loot/Plan9k/bin/sh.lua | 199 ++++++ .../opencomputers/loot/Plan9k/bin/sleep.lua | 13 + .../opencomputers/loot/Plan9k/bin/tee.lua | 17 + .../opencomputers/loot/Plan9k/bin/wc.lua | 41 ++ .../opencomputers/loot/Plan9k/bin/wget.lua | 102 +++ .../loot/Plan9k/boot/kernel/pipes | 172 +++++ .../opencomputers/loot/Plan9k/etc/rc.cfg | 1 + .../assets/opencomputers/loot/Plan9k/init.lua | 134 ++++ .../opencomputers/loot/Plan9k/lib/event.lua | 248 +++++++ .../loot/Plan9k/lib/internet.lua | 124 ++++ .../loot/Plan9k/lib/modules/base/01_util.lua | 19 + .../loot/Plan9k/lib/modules/base/05_vfs.lua | 552 ++++++++++++++++ .../loot/Plan9k/lib/modules/base/10_devfs.lua | 102 +++ .../Plan9k/lib/modules/base/10_procfs.lua | 87 +++ .../Plan9k/lib/modules/base/15_keventd.lua | 23 + .../Plan9k/lib/modules/base/15_userspace.lua | 139 ++++ .../Plan9k/lib/modules/base/16_buffer.lua | 498 ++++++++++++++ .../Plan9k/lib/modules/base/16_component.lua | 141 ++++ .../Plan9k/lib/modules/base/16_require.lua | 69 ++ .../loot/Plan9k/lib/modules/base/17_io.lua | 177 +++++ .../Plan9k/lib/modules/base/17_keyboard.lua | 200 ++++++ .../Plan9k/lib/modules/base/17_network.lua | 381 +++++++++++ .../loot/Plan9k/lib/modules/base/17_tape.lua | 65 ++ .../loot/Plan9k/lib/modules/base/18_pty.lua | 47 ++ .../Plan9k/lib/modules/base/18_syscall.lua | 31 + .../Plan9k/lib/modules/base/19_libnetwork.lua | 216 +++++++ .../Plan9k/lib/modules/base/19_manageg.lua | 58 ++ .../Plan9k/lib/modules/base/20_threading.lua | 204 ++++++ .../Plan9k/lib/modules/base/21_threadUtil.lua | 54 ++ .../loot/Plan9k/lib/modules/base/21_timer.lua | 69 ++ .../loot/Plan9k/lib/modules/base/25_init.lua | 10 + .../Plan9k/lib/modules/network/loopback.lua | 26 + .../loot/Plan9k/lib/modules/network/modem.lua | 80 +++ .../Plan9k/lib/modules/network/tunnel.lua | 74 +++ .../opencomputers/loot/Plan9k/lib/rc.lua | 124 ++++ .../loot/Plan9k/lib/serialization.lua | 135 ++++ .../opencomputers/loot/Plan9k/lib/shell.lua | 204 ++++++ .../opencomputers/loot/Plan9k/lib/term.lua | 85 +++ .../opencomputers/loot/Plan9k/lib/text.lua | 113 ++++ .../opencomputers/loot/Plan9k/usr/bin/mpt.lua | 607 ++++++++++++++++++ .../opencomputers/loot/Plan9k/usr/bin/nc.lua | 64 ++ .../opencomputers/loot/Plan9k/usr/man/pipes | 6 + .../loot/Plan9k/var/lib/mpt/config.db | 1 + .../loot/Plan9k/var/lib/mpt/mpt.db | 1 + .../assets/opencomputers/loot/loot.properties | 1 + 72 files changed, 7416 insertions(+) create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/arp.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/clear.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/components.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/cp.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/dmesg.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/echo.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/hostname.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/install.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/kill.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/ln.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/ls.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/mv.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/pastebin.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/ping.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/ps.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/rc.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/reboot.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/rm.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/route.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/sleep.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/wc.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/wget.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/etc/rc.cfg create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/init.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/event.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/internet.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/01_util.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_buffer.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_component.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_io.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_keyboard.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_pty.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_libnetwork.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_manageg.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/loopback.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/modem.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/tunnel.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/rc.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/serialization.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/shell.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/text.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/nc.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/usr/man/pipes create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/arp.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/arp.lua new file mode 100644 index 0000000000..9857df60d3 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/arp.lua @@ -0,0 +1,25 @@ +local network = require "network" + +local function fillText(text, n) + for k = 1, n - #text do + text = text .. " " + end + return text +end + +local maxlen = {8, 5} + +for interface in pairs(network.info.getInfo().interfaces) do + maxlen[2] = maxlen[2] < #interface+1 and #interface+1 or maxlen[2] + for _, host in ipairs(network.info.getArpTable(interface)) do + maxlen[1] = maxlen[1] < #host+1 and #host+1 or maxlen[1] + end +end + +print(fillText("Address", maxlen[1])..fillText("Iface", maxlen[2])) + +for interface in pairs(network.info.getInfo().interfaces) do + for _, host in ipairs(network.info.getArpTable(interface)) do + print(fillText(host, maxlen[1])..fillText(interface, maxlen[2])) + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua new file mode 100644 index 0000000000..b292f2291b --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cat.lua @@ -0,0 +1,25 @@ +local args = {...} +if #args == 0 then + repeat + local read = require("term").read() + if read then + io.write(read) + end + until not read +else + for i = 1, #args do + local file, reason = io.open(args[i]) + if not file then + io.stderr:write(reason .. "\n") + return + end + repeat + local line = file:read("*L") + if line then + io.write(line) + end + until not line + file:close() + io.write("\n") + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/clear.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/clear.lua new file mode 100644 index 0000000000..cc51d412a3 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/clear.lua @@ -0,0 +1,3 @@ +local term = require("term") + +term.clear() diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/components.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/components.lua new file mode 100644 index 0000000000..7039f40641 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/components.lua @@ -0,0 +1,52 @@ +local component = require("component") +local shell = require("shell") +local text = require("text") + +local args, options = shell.parse(...) +local count = tonumber(options.limit) or math.huge + +local components = {} +local padTo = 1 + +if #args == 0 then -- get all components if no filters given. + args[1] = "" +end +for _, filter in ipairs(args) do + for address, name in component.list(filter) do + if name:len() > padTo then + padTo = name:len() + 2 + end + components[address] = name + end +end + +padTo = padTo + 8 - padTo % 8 +for address, name in pairs(components) do + io.write(text.padRight(name, padTo) .. address .. '\n') + + if options.l then + local proxy = component.proxy(address) + local padTo = 1 + local methods = {} + for name, member in pairs(proxy) do + if type(member) == "table" or type(member) == "function" then + if name:len() > padTo then + padTo = name:len() + 2 + end + table.insert(methods, name) + end + end + table.sort(methods) + padTo = padTo + 8 - padTo % 8 + + for _, name in ipairs(methods) do + local doc = tostring(proxy[name]) + io.write(" " .. text.padRight(name, padTo) .. doc .. '\n') + end + end + + count = count - 1 + if count <= 0 then + break + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cp.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cp.lua new file mode 100644 index 0000000000..659348b152 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/cp.lua @@ -0,0 +1,140 @@ +local fs = require("filesystem") +local shell = require("shell") + +local args, options = shell.parse(...) +if #args < 2 then + io.write("Usage: cp [-inrv] \n") + io.write(" -i: prompt before overwrite (overrides -n option).\n") + io.write(" -n: do not overwrite an existing file.\n") + io.write(" -r: copy directories recursively.\n") + io.write(" -u: copy only when the SOURCE file differs from the destination\n") + io.write(" file or when the destination file is missing.\n") + io.write(" -v: verbose output.\n") + io.write(" -x: stay on original source file system.") + return +end + +local from = {} +for i = 1, #args - 1 do + table.insert(from, fs.resolve(args[i])) +end +local to = fs.resolve(args[#args]) + +local function status(from, to) + if options.v then + io.write(from .. " -> " .. to .. "\n") + end + os.sleep(0) -- allow interrupting +end + +local result, reason + +local function prompt(message) + io.write(message .. " [Y/n]\n") + local result = io.read() + return result and (result == "" or result:sub(1, 1):lower() == "y") +end + +local function areEqual(path1, path2) + local f1 = io.open(path1, "rb") + if not f1 then + return nil, "could not open `" .. path1 .. "' for update test" + end + local f2 = io.open(path2, "rb") + if not f2 then + f1:close() + return nil, "could not open `" .. path2 .. "' for update test" + end + local result = true + local chunkSize = 4 * 1024 + repeat + local s1, s2 = f1:read(chunkSize), f2:read(chunkSize) + if s1 ~= s2 then + result = false + break + end + until not s1 or not s2 + f1:close() + f2:close() + return result +end + +local function isMount(path) + path = fs.canonical(path) + for _, mountPath in fs.mounts() do + if path == fs.canonical(mountPath) then + return true + end + end +end + +local function recurse(fromPath, toPath) + status(fromPath, toPath) + if fs.isDirectory(fromPath) then + if not options.r then + io.write("omitting directory `" .. fromPath .. "'\n") + return true + end + if fs.canonical(fromPath) == fs.canonical(fs.path(toPath)) then + return nil, "cannot copy a directory, `" .. fromPath .. "', into itself, `" .. toPath .. "'\n" + end + if fs.exists(toPath) and not fs.isDirectory(toPath) then + -- my real cp always does this, even with -f, -n or -i. + return nil, "cannot overwrite non-directory `" .. toPath .. "' with directory `" .. fromPath .. "'" + end + if options.x and isMount(fromPath) then + return true + end + fs.makeDirectory(toPath) + for file in fs.list(fromPath) do + local result, reason = recurse(fs.concat(fromPath, file), fs.concat(toPath, file)) + if not result then + return nil, reason + end + end + return true + else + if fs.exists(toPath) then + if fs.canonical(fromPath) == fs.canonical(toPath) then + return nil, "`" .. fromPath .. "' and `" .. toPath .. "' are the same file" + end + if fs.isDirectory(toPath) then + if options.i then + if not prompt("overwrite `" .. toPath .. "'?") then + return true + end + elseif options.n then + return true + else -- yes, even for -f + return nil, "cannot overwrite directory `" .. toPath .. "' with non-directory" + end + else + if options.u then + if areEqual(fromPath, toPath) then + return true + end + end + if options.i then + if not prompt("overwrite `" .. toPath .. "'?") then + return true + end + elseif options.n then + return true + end + -- else: default to overwriting + end + fs.remove(toPath) + end + return fs.copy(fromPath, toPath) + end +end +for _, fromPath in ipairs(from) do + local toPath = to + if fs.isDirectory(toPath) then + toPath = fs.concat(toPath, fs.name(fromPath)) + end + result, reason = recurse(fromPath, toPath) + if not result then + error(reason, 0) + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua new file mode 100644 index 0000000000..b94ad8cfe3 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dd.lua @@ -0,0 +1,76 @@ +local shell = require "shell" +local filesystem = require "filesystem" + +local args = {...} +local options = {} +options.count = math.huge +options.bs = 1 +options["if"] = "-" +options.of = "-" + +for _, arg in pairs(args) do + local k, v = arg:match("(%w+)=(.+)") + if not k then + print("Ilegal argument: " .. arg) + return + end + options[k] = v +end + +if type(options.count) == "string" then options.count = tonumber(options.count) or math.huge end +if type(options.bs) == "string" then options.bs = tonumber(options.bs) or 1 end + +local reader +local writer + +if options["if"] == "-" then + reader = { + read = io.read, + close = function()end + } +else + local inHnd = filesystem.open(options["if"], "r") + reader = { + read = function(...)return inHnd:read(...)end, + close = function()inHnd:close()end + } +end + +if options.of == "-" then + io.output():setvbuf("full", options.bs) + writer = { + write = function(data)return io.write(data) end, + close = function()io.output():setvbuf("no")end + } +else + local outHnd = filesystem.open(options.of, "w") + writer = { + write = function(...)return outHnd:write(...)end, + close = function()outHnd:close()end + } +end + +local start = computer.uptime() +local dcount = 0 + +for n = 1, options.count do + local data = reader.read(options.bs) + if not data then + print("End of input") + break + end + local wrote = writer.write(data) + dcount = dcount + (wrote and options.bs or 0) + if not wrote then + print("Output full") + break + end +end + +local time = computer.uptime() - start + +reader.close() +writer.close() + +print(dcount .. " bytes (" .. (dcount / 1024) .. " KB) copied, " .. time .. "s, " .. (dcount / time / 1024) .. " KB/s") + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua new file mode 100644 index 0000000000..9cbc741075 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/df.lua @@ -0,0 +1,71 @@ +local fs = require("filesystem") +local shell = require("shell") +local text = require("text") + +local args, options = shell.parse(...) + +local function formatSize(size) + if not options.h then + return tostring(size) + end + local sizes = {"", "K", "M", "G"} + local unit = 1 + local power = options.si and 1000 or 1024 + while size > power and unit < #sizes do + unit = unit + 1 + size = size / power + end + return math.floor(size * 10) / 10 .. sizes[unit] +end + +local mounts = {} +if #args == 0 then + for proxy, path in fs.mounts() do + mounts[path] = proxy + end +else + for i = 1, #args do + local proxy, path = fs.get(args[i]) + if not proxy then + io.stderr:write(args[i], ": no such file or directory\n") + else + mounts[path] = proxy + end + end +end + +local result = {{"Filesystem", "Used", "Available", "Use%", "Mounted on"}} +for path, proxy in pairs(mounts) do + local label = proxy.getLabel() or proxy.address + local used, total = proxy.spaceUsed(), proxy.spaceTotal() + local available, percent + if total == math.huge then + used = used or "N/A" + available = "unlimited" + percent = "0%" + else + available = total - used + percent = used / total + if percent ~= percent then -- NaN + available = "N/A" + percent = "N/A" + else + percent = math.ceil(percent * 100) .. "%" + end + end + table.insert(result, {label, formatSize(used), formatSize(available), tostring(percent), path}) +end + +local m = {} +for _, row in ipairs(result) do + for col, value in ipairs(row) do + m[col] = math.max(m[col] or 1, value:len()) + end +end + +for _, row in ipairs(result) do + for col, value in ipairs(row) do + io.write(text.padRight(value, m[col] + 2)) + end + io.write("\n") +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dmesg.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dmesg.lua new file mode 100644 index 0000000000..7a5913c3cd --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/dmesg.lua @@ -0,0 +1,32 @@ +local event = require "event" +local component = require "component" +local keyboard = require "keyboard" + +local args = {...} +local color, isPal, evt + +io.write("Press 'Ctrl-C' to exit\n") +pcall(function() + repeat + if #args > 0 then + evt = table.pack(event.pullMultiple("interrupted", table.unpack(args))) + else + evt = table.pack(event.pull()) + end + if evt[1] then + io.write("[" .. os.date("%T") .. "] ") + io.write(tostring(evt[1]) .. string.rep(" ", math.max(10 - #tostring(evt[1]), 0) + 1)) + io.write(tostring(evt[2]) .. string.rep(" ", 37 - #tostring(evt[2]))) + if evt.n > 2 then + for i = 3, evt.n do + io.write(" " .. tostring(evt[i]):gsub("\x1b", "")) + end + end + + io.write("\n") + end + until evt[1] == "interrupted" +end) + + + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/echo.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/echo.lua new file mode 100644 index 0000000000..8e7365fdfc --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/echo.lua @@ -0,0 +1,8 @@ +local args = table.pack(...) +for i = 1, #args do + if i > 1 then + io.write(" ") + end + io.write(args[i]) +end +io.write("\n") \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua new file mode 100644 index 0000000000..4d5489d8ae --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua @@ -0,0 +1,311 @@ +local pipes = require("pipes") +local blinkState = false +local args = {...} + +--local screen = component.list('screen')() +--for address in component.list('screen') do +-- if #component.invoke(address, 'getKeyboards') > 0 then +-- screen = address +-- end +--end + +local gpu = args[1] --component.list("gpu", true)() +local w, h +if gpu then + --component.invoke(gpu, "bind", screen) + w, h = component.invoke(gpu, "getResolution") + component.invoke(gpu, "setResolution", w, h) + component.invoke(gpu, "setBackground", 0x000000) + component.invoke(gpu, "setForeground", 0xFFFFFF) + component.invoke(gpu, "fill", 1, 1, w, h, " ") +end +local y = 1 +local x = 1 + +local function checkCoord() + if x < 1 then x = 1 end + if x > w then x = w end + if y < 1 then y = 1 end + if y > h then y = h end +end + +local function unblink() + if blinkState then + blinkState = not blinkState + component.invoke(gpu, "setBackground", blinkState and 0xFFFFFF or 0x000000) + component.invoke(gpu, "set", x, y, component.invoke(gpu, "get", x, y) or " ") + component.invoke(gpu, "setBackground", 0x000000) + end +end + +local scrTop = 1 +local scrBot = nil + +local function scroll() + unblink() + scrBot = scrBot or h + x = 1 + if y == h then + component.invoke(gpu, "copy", 1, scrTop + 1, w, scrBot - scrTop, 0, -1) + component.invoke(gpu, "fill", 1, scrBot, w, 1, " ") + else + y = y + 1 + end +end + +local printBuf = "" + +local function printBuffer() + if #printBuf < 1 then return end + component.invoke(gpu, "set", x, y, printBuf) + if x == w then + scroll() + else + x = x + unicode.len(printBuf) + checkCoord() + end + printBuf = "" + if pipes.shouldYield() then + os.sleep() + end +end + +local function backDelChar() + if #printBuf > 0 then + printBuf = unicode.sub(printBuf, 1, unicode.len(printBuf) - 1) + else + x = x - 1 + unblink() + component.invoke(gpu, "set", x, y, " ") + end +end + +---Char handlers + +local charHandlers = {} + +function charHandlers.base(char) + if char == "\n" then + printBuffer() + scroll() + elseif char == "\r" then + unblink() + printBuffer() + x = 1 + elseif char == "\t" then + printBuf = printBuf .. " " + elseif char == "\b" then + backDelChar() + elseif char == "\x1b" then + charHandlers.active = charHandlers.control + charHandlers.control(char) + elseif char:match("[%g%s]") then + printBuf = printBuf .. char + end +end + +local mcommands = {} +local swap = false + +mcommands["7"] = function() + local fc, fp = component.invoke(gpu, "getForeground") + local bc, bp = component.invoke(gpu, "getBackground") + + component.invoke(gpu, "setForeground", bc, bp) + component.invoke(gpu, "setBackground", fc, fp) + swap = true +end + +mcommands["0"] = function() + if swap then + local fc, fp = component.invoke(gpu, "getForeground") + local bc, bp = component.invoke(gpu, "getBackground") + + component.invoke(gpu, "setForeground", bc, bp) + component.invoke(gpu, "setBackground", fc, fp) + end + swap = false +end + +mcommands["1"] = function()end --Bold font +mcommands["2"] = function()end --Dim font +mcommands["3"] = function()end --Italic +mcommands["4"] = function()end --Underscore +mcommands["10"] = function()end --Select primary font (LA100) + + +mcommands["30"] = function()component.invoke(gpu, "setForeground", 0x000000)end +mcommands["31"] = function()component.invoke(gpu, "setForeground", 0xFF0000)end +mcommands["32"] = function()component.invoke(gpu, "setForeground", 0x00FF00)end +mcommands["33"] = function()component.invoke(gpu, "setForeground", 0xFFFF00)end +mcommands["34"] = function()component.invoke(gpu, "setForeground", 0x0000FF)end +mcommands["35"] = function()component.invoke(gpu, "setForeground", 0xFF00FF)end +mcommands["36"] = function()component.invoke(gpu, "setForeground", 0x00FFFF)end +mcommands["37"] = function()component.invoke(gpu, "setForeground", 0xFFFFFF)end + +mcommands["40"] = function()component.invoke(gpu, "setBackground", 0x000000)end +mcommands["41"] = function()component.invoke(gpu, "setBackground", 0xFF0000)end +mcommands["42"] = function()component.invoke(gpu, "setBackground", 0x00FF00)end +mcommands["43"] = function()component.invoke(gpu, "setBackground", 0xFFFF00)end +mcommands["44"] = function()component.invoke(gpu, "setBackground", 0x0000FF)end +mcommands["45"] = function()component.invoke(gpu, "setBackground", 0xFF00FF)end +mcommands["46"] = function()component.invoke(gpu, "setBackground", 0x00FFFF)end +mcommands["47"] = function()component.invoke(gpu, "setBackground", 0xFFFFFF)end + +mcommands["39"] = function()component.invoke(gpu, "setForeground", 0xFFFFFF)end +mcommands["49"] = function()component.invoke(gpu, "setBackground", 0x000000)end + +local lcommands = {} + +lcommands["4"] = function()end --Reset to replacement mode + + +local commandMode = "" +local commandBuf = "" +local commandList = {} + +function charHandlers.control(char) + if char == "\x1b" then + commandList = {} + commandBuf = "" + commandMode = "" + unblink() + return + elseif char == "[" then + if commandMode ~= "" or commandBuf ~= "" then + charHandlers.active = charHandlers.base + return + end + commandMode = "[" + return + elseif char == "(" then + if commandMode ~= "" or commandBuf ~= "" then + charHandlers.active = charHandlers.base + return + end + commandMode = "(" + return + elseif char == ";" then + commandList[#commandList + 1] = commandBuf + commandBuf = "" + return + elseif char == "m" then + commandList[#commandList + 1] = commandBuf + if not commandList[1] or commandList[1] == "" then + commandList[1] = "0" + end + for _, command in ipairs(commandList) do + if not mcommands[command] then + pipes.log("Unknown escape code: " .. tostring(command)) + break + end + mcommands[command]() + end + elseif char == "l" then + commandList[#commandList + 1] = commandBuf + if not commandList[1] or commandList[1] == "" then + commandList[1] = "0" + end + for _, command in ipairs(commandList) do + if not lcommands[command] then + pipes.log("Unknown escape code: " .. tostring(command)) + break + end + lcommands[command]() + end + elseif char == "d" then + commandList[#commandList + 1] = commandBuf + local n = tonumber(commandList[1]) or 1 + y = math.max(n, 1) + checkCoord() + elseif char == "r" then + commandList[#commandList + 1] = commandBuf + local nt, nb = tonumber(commandList[1]) or 1, tonumber(commandList[2]) or h + scrTop = nt + scrBot = nb + elseif char == "H" or char == "f" then --set pos + commandList[#commandList + 1] = commandBuf + local nx, ny = tonumber(commandList[1]), tonumber(commandList[2]) + x = nx or 1 + y = ny or 1 + checkCoord() + elseif char == "A" then --move up + commandList[#commandList + 1] = commandBuf + local n = tonumber(commandList[1]) or 1 + y = y - n + checkCoord() + elseif char == "B" then --move down + if commandMode == "(" then + charHandlers.active = charHandlers.base + return + end + commandList[#commandList + 1] = commandBuf + local n = tonumber(commandList[1]) or 1 + y = math.max(y - n, 1) + checkCoord() + elseif char == "C" then --move fwd + commandList[#commandList + 1] = commandBuf + local n = tonumber(commandList[1]) or 1 + x = x + n + checkCoord() + elseif char == "D" then --move back + commandList[#commandList + 1] = commandBuf + local n = tonumber(commandList[1]) or 1 + x = math.max(x - n, 1) + checkCoord() + elseif char == "G" then --Cursor Horizontal position Absolute + commandList[#commandList + 1] = commandBuf + x = tonumber(commandList[1]) or 1 + checkCoord() + elseif char == "J" then --clear + commandList[#commandList + 1] = commandBuf + if commandList[1] == "2" then + component.invoke(gpu, "fill", 1, 1, w, h, " ") + x, y = 1, 1 + end + elseif char == "K" then --Erase to end of line + commandList[#commandList + 1] = commandBuf + component.invoke(gpu, "fill", x, y, w - x, 1, " ") + elseif char == "X" then --Erase next chars + commandList[#commandList + 1] = commandBuf + component.invoke(gpu, "fill", x, y, tonumber(commandList[1]) or 1, 1, " ") + else + commandBuf = commandBuf .. char + return + end + charHandlers.active = charHandlers.base + commandList = {} + commandBuf = "" + commandMode = "" +end + +---Char handler end + +charHandlers.active = charHandlers.base + +local function _print(msg) + if gpu then + + for char in msg:gmatch(".") do + charHandlers.active(char) + end + + printBuffer() + end +end + +pipes.setTimer(function() + blinkState = not blinkState + component.invoke(gpu, "setBackground", blinkState and 0xFFFFFF or 0x000000) + component.invoke(gpu, "set", x, y, component.invoke(gpu, "get", x, y) or " ") + component.invoke(gpu, "setBackground", 0x000000) +end, 0.5) + +while true do + local data = io.read(1) + if io.input().remaining() > 0 then + data = data .. io.read(io.input().remaining()) + end + unblink() + _print(data) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/hostname.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/hostname.lua new file mode 100644 index 0000000000..40868878ac --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/hostname.lua @@ -0,0 +1,21 @@ +local args = {...} +if args[1] then + local file, reason = io.open("/etc/hostname", "w") + if not file then + io.stderr:write(reason .. "\n") + else + file:write(args[1]) + file:close() + os.setenv("HOSTNAME", args[1]) + os.setenv("PS1", "\x1b[33m$HOSTNAME\x1b[32m:\x1b[33m$PWD\x1b[31m#\x1b[39m ") + computer.pushSignal("hostname", args[1]) + end +else + local file = io.open("/etc/hostname") + if file then + io.write(file:read("*l"), "\n") + file:close() + else + io.stderr:write("Hostname not set\n") + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua new file mode 100644 index 0000000000..e7a9ba5cd6 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ifconfig.lua @@ -0,0 +1,26 @@ +local network = require "network" +local computer = require "computer" +local args = {...} + +local function align(txt)return txt .. (" "):sub(#txt+1)end + +if #args < 1 then + print("Network interfaces:") + local info = network.info.getInfo() + for node, info in pairs(info.interfaces)do + print(align(node).."Link encap:"..info.linkName) + print(" HWaddr "..info.selfAddr) + if node == "lo" then print(" HWaddr "..computer.address()) end + local pktIn, pktOut, bytesIn, bytesOut = network.info.getInterfaceInfo(node) + print(" RX packets:"..tostring(pktIn)) + print(" TX packets:"..tostring(pktOut)) + print(" RX bytes:"..tostring(bytesIn).." TX bytes:"..tostring(bytesOut)) + end +elseif args[1] == "bind" and args[2] then + print("Address attached") + network.ip.bind(args[2]) +else + print("Usage:") + print(" ifconfig - view network summary") + print(" ifconfig bind [addr] - 'attach' addnitional address to computer") +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua new file mode 100644 index 0000000000..86be5a6c8d --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua @@ -0,0 +1,111 @@ +--Plan9k userspace init for pipes kernel +local pipes = require("pipes") +local component = require("component") +local filesystem = require("filesystem") + +os.setenv("LIBPATH", "/usr/local/lib:/usr/lib:/lib:.") +os.setenv("PATH", "/usr/local/bin:/usr/bin:/bin:.") +os.setenv("PWD", "/") +os.setenv("PS1", "\x1b[33m$PWD\x1b[31m#\x1b[39m ") + +if not filesystem.exists("/root") then + filesystem.makeDirectory("/root") +end +os.setenv("HOME", "/root") +os.setenv("PWD", "/root") + +local hostname = io.open("/etc/hostname") +if hostname then + local name = hostname:read("*l") + hostname:close() + os.setenv("HOSTNAME", name) + os.setenv("PS1", "\x1b[33m$HOSTNAME\x1b[32m:\x1b[33m$PWD\x1b[31m#\x1b[39m ") + computer.pushSignal("hostname", name) +end + +local sin, sout + +local screens = component.list("screen") +for gpu in component.list("gpu") do + local screen = screens() + if not screen then break end + component.invoke(gpu, "bind", screen) + + local pty, mi, mo, si, so = pipes.openPty() + + local interruptHandler = function() + print("SIGINT!!") + end + + os.spawnp("/bin/getty.lua", mi, nil, nil, gpu) + os.spawnp("/bin/readkey.lua", nil, mo, mo, screen, interruptHandler) + + if not sout then + sin = si + sout = so + + io.output(sout) + io.input(sin) + + print("\x1b[32m>>\x1b[39m Starting services") + local results = require('rc').allRunCommand('start') + + for name, result in pairs(results) do + local ok, reason = table.unpack(result) + if not ok then + io.stdout:write("\x1b[31m" .. reason .. "\x1b[39m\n") + end + end + end + + io.output(so) + io.input(si) + + print("\x1b[32m>>\x1b[39m Starting Plan9k shell") + + os.spawnp("/bin/sh.lua", si, so, so) + +end + + +--local ttyout = io.popen("/bin/getty.lua", "w", ttyconfig) +--local ttyin = io.popen("/bin/readkey.lua", "r", ttyconfig) + +local kout = io.popen(function() + pipes.setThreadName("/bin/tee.lua") + io.output(sout) + loadfile("/bin/tee.lua", nil, _G)("/kern.log") +end, "w") + +pipes.setKernelOutput(kout) + +--computer.pullSignal() + +if not filesystem.isDirectory("/mnt") then + filesystem.makeDirectory("/mnt") +end + +for address, ctype in component.list() do + computer.pushSignal("component_added", address, ctype) +end + +while true do + local sig = {computer.pullSignal()} + if sig[1] == "component_added" then + if sig[3] == "filesystem" then + local proxy = component.proxy(sig[2]) + if proxy then + local name = sig[2]:sub(1, 3) + while filesystem.exists(filesystem.concat("/mnt", name)) and name:len() < sig[2]:len() do + name = sig[2]:sub(1, name:len() + 1) + end + name = filesystem.concat("/mnt", name) + filesystem.mount(proxy, name) + end + end + elseif sig[1] == "component_removed" then + if sig[3] == "filesystem" then + filesystem.umount(sig[2]) + end + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/install.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/install.lua new file mode 100644 index 0000000000..46183100b0 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/install.lua @@ -0,0 +1,77 @@ +local component = require("component") +local computer = require("computer") +local filesystem = require("filesystem") +local unicode = require("unicode") +local shell = require("shell") +local term = require("term") +local kernel = require("pipes") + +local args, options = shell.parse(...) + +local fromAddress = options.from and component.get(options.from) or filesystem.get(os.getenv("_")).address +local candidates = {} +for address in component.list("filesystem") do + local dev = component.proxy(address) + if not dev.isReadOnly() and dev.address ~= computer.tmpAddress() and dev.address ~= fromAddress then + table.insert(candidates, dev) + end +end + +if #candidates == 0 then + io.write("No writable disks found, aborting.\n") + os.exit() +end + +for i = 1, #candidates do + local label = candidates[i].getLabel() + if label then + label = label .. " (" .. candidates[i].address:sub(1, 8) .. "...)" + else + label = candidates[i].address + end + io.write(i .. ") " .. label .. "\n") +end + +io.write("To select the device to install to, please enter a number between 1 and " .. #candidates .. ".\n") +io.write("Press 'q' to cancel the installation.\n") +local choice +while not choice do + result = term.read() + if result:sub(1, 1):lower() == "q" then + os.exit() + end + local number = tonumber(result) + if number and number > 0 and number <= #candidates then + choice = candidates[number] + else + io.write("Invalid input, please try again.\n") + end +end + +local function findMount(address) + for fs, path in filesystem.mounts() do + if fs.address == component.get(address) then + return path + end + end +end + +local name = options.name or "Plan9k" +io.write("Installing " .. name .." to device " .. (choice.getLabel() or choice.address) .. "\n") + +local dest = findMount(choice.address) .. "/" +--We only install base system +local pid = os.spawn("/usr/bin/mpt.lua", "-v", "--offline", "--root="..dest, "--mirror=/", "-S", "plan9k" ) +kernel.joinThread(pid) + +if not options.nolabelset then pcall(choice.setLabel, name) end + +if not options.noreboot then + io.write("All done! Reboot now? [Y/n]\n") + local result = term.read() + if not result or result == "" or result:sub(1, 1):lower() == "y" then + io.write("\nRebooting now!\n") + computer.shutdown(true) + end +end +io.write("Returning to shell.\n") diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/kill.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/kill.lua new file mode 100644 index 0000000000..7f9116c247 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/kill.lua @@ -0,0 +1,8 @@ +local args = {...} +local pid = tonumber(args[1]) +if not pid then error("Usage: kill [pid]") end +local res, reason = os.kill(pid, "kill") + +if not res then + error(reason) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ln.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ln.lua new file mode 100644 index 0000000000..0384be3e91 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ln.lua @@ -0,0 +1,23 @@ +local component = require("component") +local fs = require("filesystem") +local shell = require("shell") + +local dirs = shell.parse(...) +if #dirs == 0 then + io.write("Usage: ln []") + return +end + +local target = shell.resolve(dirs[1]) +local linkpath +if #dirs > 1 then + linkpath = shell.resolve(dirs[2]) +else + linkpath = fs.concat(shell.getWorkingDirectory(), fs.name(target)) +end + +local result, reason = fs.link(target, linkpath) +if not result then + io.stderr:write(reason) +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ls.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ls.lua new file mode 100644 index 0000000000..95d70e94e5 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ls.lua @@ -0,0 +1,110 @@ +local component = require("component") +local fs = require("filesystem") +local shell = require("shell") +local text = require('text') + +local dirs, options = shell.parse(...) +if #dirs == 0 then + table.insert(dirs, ".") +end + +local function formatOutput() + return true +end + +io.output():setvbuf("line") +for i = 1, #dirs do + local path = shell.resolve(dirs[i]) + if #dirs > 1 then + if i > 1 then + io.write("\n") + end + io.write(path, ":\n") + end + local list, reason = fs.list(path) + if not list then + io.write(reason .. "\n") + else + local lsd = {} + local lsf = {} + local m = 1 + for f in list do + m = math.max(m, f:len() + 2) + if f:sub(-1) == "/" then + if options.p then + table.insert(lsd, f) + else + table.insert(lsd, f:sub(1, -2)) + end + else + table.insert(lsf, f) + end + end + table.sort(lsd) + table.sort(lsf) + io.write("\x1b[36m") + --setColor(0x66CCFF) + + local col = 1 + local columns = math.huge + if formatOutput() then + columns = math.max(1, math.floor((component.gpu.getResolution() - 1) / m)) + end + + for _, d in ipairs(lsd) do + if options.a or d:sub(1, 1) ~= "." then + if options.l or not formatOutput() or col % columns == 0 then + io.write(d .. "\n") + else + io.write(text.padRight(d, m)) + end + col = col + 1 + end + end + + for _, f in ipairs(lsf) do + if fs.isLink(fs.concat(path, f)) then + io.write("\x1b[33m") + --setColor(0xFFAA00) + elseif f:sub(-4) == ".lua" then + io.write("\x1b[32m") + --setColor(0x00FF00) + else + io.write("\x1b[39m") + --setColor(0xFFFFFF) + end + if options.a or f:sub(1, 1) ~= "." then + if not formatOutput() then + io.write(f) + if options.l then + io.write(" " .. fs.size(fs.concat(path, f))) + end + io.write("\n") + else + io.write(text.padRight(f, m)) + if options.l then + io.write("\x1b[39m") + --setColor(0xFFFFFF) + io.write(fs.size(fs.concat(path, f)), "\n") + elseif col % columns == 0 then + io.write("\n") + end + end + col = col + 1 + end + end + + io.write("\x1b[39m") + --setColor(0xFFFFFF) + if options.M then + io.write("\n" .. tostring(#lsf) .. " File(s)") + io.write("\n" .. tostring(#lsd) .. " Dir(s)") + end + if not options.l then + io.write("\n") + end + end +end +io.output():setvbuf("no") +io.output():flush() + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua new file mode 100644 index 0000000000..ec96ba6115 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua @@ -0,0 +1,39 @@ +local serialization = require("serialization") +local term = require("term") + +local env = setmetatable({}, {__index = _ENV}) + +local function optrequire(...) + local success, module = pcall(require, ...) + if success then + return module + end +end + +local hist = {} +while true do + io.write("lua> ") + local command = term.read(hist) + local code, reason + 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) + end + + if code then + local result = table.pack(xpcall(code, debug.traceback)) + if not result[1] then + if type(result[2]) == "table" and result[2].reason == "terminated" then + os.exit(result[2].code) + end + io.stderr:write(tostring(result[2]) .. "\n") + else + for i = 2, result.n do + io.write(serialization.serialize(result[i], true) .. "\n") + end + end + else + io.stderr:write(tostring(reason) .. "\n") + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mv.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mv.lua new file mode 100644 index 0000000000..d94484c959 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mv.lua @@ -0,0 +1,27 @@ +local fs = require("filesystem") +local shell = require("shell") + +local args, options = shell.parse(...) +if #args < 2 then + io.write("Usage: mv [-f] \n") + io.write(" -f: overwrite file if it already exists.") + return +end + +local from = shell.resolve(args[1]) +local to = shell.resolve(args[2]) +if fs.isDirectory(to) then + to = to .. "/" .. fs.name(from) +end +if fs.exists(to) then + if not options.f then + io.stderr:write("target file exists") + return + end + fs.remove(to) +end +local result, reason = os.rename(from, to) +if not result then + io.stderr:write(reason or "unknown error") +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/pastebin.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/pastebin.lua new file mode 100644 index 0000000000..6097d83c2e --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/pastebin.lua @@ -0,0 +1,153 @@ +--[[ This program allows downloading and uploading from and to pastebin.com. + Authors: Sangar, Vexatos ]] +local component = require("component") +local fs = require("filesystem") +local internet = require("internet") +local shell = require("shell") + +if not component.isAvailable("internet") then + io.stderr:write("This program requires an internet card to run.") + return +end + +local args, options = shell.parse(...) + +-- This gets code from the website and stores it in the specified file. +local function get(pasteId, filename) + local f, reason = io.open(filename, "w") + if not f then + io.stderr:write("Failed opening file for writing: " .. reason) + return + end + + io.write("Downloading from pastebin.com... ") + local url = "http://pastebin.com/raw.php?i=" .. pasteId + local result, response = pcall(internet.request, url) + if result then + io.write("success.\n") + for chunk in response do + if not options.k then + string.gsub(chunk, "\r\n", "\n") + end + f:write(chunk) + end + + f:close() + io.write("Saved data to " .. filename .. "\n") + else + io.write("failed.\n") + f:close() + fs.remove(filename) + io.stderr:write("HTTP request failed: " .. response .. "\n") + end +end + +-- This makes a string safe for being used in a URL. +function encode(code) + if code then + code = string.gsub(code, "([^%w ])", function (c) + return string.format("%%%02X", string.byte(c)) + end) + code = string.gsub(code, " ", "+") + end + return code +end + +-- This stores the program in a temporary file, which it will +-- delete after the program was executed. +function run(pasteId, ...) + local tmpFile = os.tmpname() + get(pasteId, tmpFile) + io.write("Running...\n") + + local success, reason = shell.execute(tmpFile, nil, ...) + if not success then + io.stderr:write(reason) + end + fs.remove(tmpFile) +end + +-- Uploads the specified file as a new paste to pastebin.com. +function put(path) + local config = {} + local configFile = loadfile("/etc/pastebin.conf", "t", config) + if configFile then + local result, reason = pcall(configFile) + if not result then + io.stderr:write("Failed loading config: " .. reason) + end + end + config.key = config.key or "fd92bd40a84c127eeb6804b146793c97" + local file, reason = io.open(path, "r") + + if not file then + io.stderr:write("Failed opening file for reading: " .. reason) + return + end + + local data = file:read("*a") + file:close() + + io.write("Uploading to pastebin.com... ") + local result, response = pcall(internet.request, + "http://pastebin.com/api/api_post.php", + "api_option=paste&" .. + "api_dev_key=" .. config.key .. "&" .. + "api_paste_format=lua&" .. + "api_paste_expire_date=N&" .. + "api_paste_name=" .. encode(fs.name(path)) .. "&" .. + "api_paste_code=" .. encode(data)) + + if result then + local info = "" + for chunk in response do + info = info .. chunk + end + if string.match(info, "^Bad API request, ") then + io.write("failed.\n") + io.write(info) + else + io.write("success.\n") + local pasteId = string.match(info, "[^/]+$") + io.write("Uploaded as " .. info .. "\n") + io.write('Run "pastebin get ' .. pasteId .. '" to download anywhere.') + end + else + io.write("failed.\n") + io.stderr:write(response) + end +end + +local command = args[1] +if command == "put" then + if #args == 2 then + put(shell.resolve(args[2])) + return + end +elseif command == "get" then + if #args == 3 then + local path = shell.resolve(args[3]) + if fs.exists(path) then + if not options.f or not os.remove(path) then + io.stderr:write("file already exists") + return + end + end + get(args[2], path) + return + end +elseif command == "run" then + if #args >= 2 then + run(args[2], table.unpack(args, 3)) + return + end +end + +-- If we come here there was some invalid input. +io.write("Usages:\n") +io.write("pastebin put [-f] \n") +io.write("pastebin get [-f] \n") +io.write("pastebin run [-f] []\n") +io.write(" -f: Force overwriting existing files.\n") +io.write(" -k: keep line endings as-is (will convert\n") +io.write(" Windows line endings to Unix otherwise).") diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ping.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ping.lua new file mode 100644 index 0000000000..57b7a7b654 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ping.lua @@ -0,0 +1,107 @@ +local network = require "network" +local event = require "event" +local computer = require "computer" +local shell = require "shell" + +local args, options = shell.parse(...) + +if #args < 1 or options.h or options.help then + print("Usage: ping: [addr]") + print(" --c= --count=[ping count] Amount of pings to send(default 6)") + print(" --s= --size=[data size] Payload size(default 56 bytes)") + print(" --i= --interval=[seconds] Ping interval(default 1s)") + --print(" -d --duplicates Check for duplicate messages") + print(" --t= --droptime=[seconds] Amount of time after which ping is") + print(" Considered to be lost[default 8s]") + print(" -v --verbose Output more details") + return +end + +local len = tonumber(options.s) or tonumber(options.size) or 56 + +local function round(n,r) return math.floor(n*(10^r))/(10^r) end + +local function verbose(...) + if options.v or options.verbose then + print(...) + end +end + +local function generatePayload() + local payload = "" + for i = 1, len do + local ch = string.char(math.random(0, 255)) + ch = ch == ":" and "_" or ch --Ping matcher derps hard when it finds ':' in payload + payload = payload .. ch + end + return payload +end + + +print("PING "..args[1].." with "..tostring(len) .." bytes of data") + +local stats = { + transmitted = 0, + received = 0, + malformed = 0 +} + +local function doSleep() + + local deadline = computer.uptime() + (tonumber(options.i) or tonumber(options.interval) or 1) + repeat + computer.pullSignal(deadline - computer.uptime()) + until computer.uptime() >= deadline +end + +local function doPing() + + local payload = generatePayload() + local icmp_seq = network.icmp.ping(args[1], payload) + stats.transmitted = stats.transmitted + 1 + verbose(tostring(len).." bytes to "..args[1]..": icmp_seq="..tostring(icmp_seq)) + local start = computer.uptime() + + local deadline = start + (tonumber(options.t) or tonumber(options.droptime) or 8) + local e, replier, id, inpayload + repeat + e, replier, id, inpayload = event.pull(deadline - computer.uptime(), "ping_reply") + until computer.uptime() >= deadline or (e == "ping_reply" and id == icmp_seq) + + if computer.uptime() >= deadline and e ~= "ping_reply" then + verbose(tostring(len).." bytes lost: icmp_seq="..tostring(icmp_seq)) + elseif inpayload == payload then + stats.received = stats.received + 1 + print(tostring(len).." bytes from "..args[1]..": icmp_seq="..tostring(icmp_seq).." time="..tostring(round(computer.uptime()-start,2)).." s") + else + stats.malformed = stats.malformed + 1 + verbose(tostring(#inpayload).." bytes malformed: icmp_seq="..tostring(icmp_seq).." time="..tostring(round(computer.uptime()-start,2)).." s") + end +end + +local begin = computer.uptime() + +local function outputStats() + print("--- "..args[1].." ping statistics ---") + print(tostring(stats.transmitted) .. " packets transmitted, " + ..tostring(stats.received) .. " received, " + ..tostring(100 - math.floor((stats.received / stats.transmitted) * 100)) .. "% packet loss, time " .. tostring(round(computer.uptime()-begin,2))) +end + +local state, reason = pcall(function() + local c = 0 + repeat + doPing() + doSleep() + c = c + 1 + until c == 0 or (tonumber(options.c) and c >= tonumber(options.c)) + or (tonumber(options.count) and c >= tonumber(options.count)) + or ((not tonumber(options.c)) and (not tonumber(options.count)) and c >= 8) +end) + +if not state then + verbose("Stopped by: "..tostring(reason)) +end + +outputStats() + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ps.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ps.lua new file mode 100644 index 0000000000..f5ef2c0f74 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/ps.lua @@ -0,0 +1,6 @@ +local pipes = require("pipes") + +print("PID NAME") +for _, thread in pairs(pipes.getThreadInfo()) do + print(thread.pid, " ", thread.name) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/rc.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/rc.lua new file mode 100644 index 0000000000..3591c7074b --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/rc.lua @@ -0,0 +1,13 @@ +local rc = require('rc') + +local args = table.pack(...) +if args.n < 1 then + io.write("Usage: rc [command] [args...]\n") + return +end + +local result, reason = rc.runCommand(table.unpack(args)) + +if not result then + io.stderr:write(reason .. "\n") +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua new file mode 100644 index 0000000000..1dab8eb1e6 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua @@ -0,0 +1,191 @@ +local component = require("component") + +local args = {...} +local screen = args[1] + +local keyboards = {} +for _, kbd in pairs(component.invoke(screen, "getKeyboards")) do + keyboards[kbd] = true +end + +local keyboard = {pressedChars = {}, pressedCodes = {}} + +keyboard.keys = { + ["1"] = 0x02, + ["2"] = 0x03, + ["3"] = 0x04, + ["4"] = 0x05, + ["5"] = 0x06, + ["6"] = 0x07, + ["7"] = 0x08, + ["8"] = 0x09, + ["9"] = 0x0A, + ["0"] = 0x0B, + a = 0x1E, + b = 0x30, + c = 0x2E, + d = 0x20, + e = 0x12, + f = 0x21, + g = 0x22, + h = 0x23, + i = 0x17, + j = 0x24, + k = 0x25, + l = 0x26, + m = 0x32, + n = 0x31, + o = 0x18, + p = 0x19, + q = 0x10, + r = 0x13, + s = 0x1F, + t = 0x14, + u = 0x16, + v = 0x2F, + w = 0x11, + x = 0x2D, + y = 0x15, + z = 0x2C, + + apostrophe = 0x28, + at = 0x91, + back = 0x0E, -- backspace + backslash = 0x2B, + colon = 0x92, + comma = 0x33, + enter = 0x1C, + equals = 0x0D, + grave = 0x29, -- accent grave + lbracket = 0x1A, + lcontrol = 0x1D, + lmenu = 0x38, -- left Alt + lshift = 0x2A, + minus = 0x0C, + numlock = 0x45, + pause = 0xC5, + period = 0x34, + rbracket = 0x1B, + rcontrol = 0x9D, + rmenu = 0xB8, -- right Alt + rshift = 0x36, + scroll = 0x46, -- Scroll Lock + semicolon = 0x27, + slash = 0x35, -- / on main keyboard + space = 0x39, + stop = 0x95, + tab = 0x0F, + underline = 0x93, + + -- Keypad (and numpad with numlock off) + up = 0xC8, + down = 0xD0, + left = 0xCB, + right = 0xCD, + home = 0xC7, + ["end"] = 0xCF, + pageUp = 0xC9, + pageDown = 0xD1, + insert = 0xD2, + delete = 0xD3, + + -- Function keys + f1 = 0x3B, + f2 = 0x3C, + f3 = 0x3D, + f4 = 0x3E, + f5 = 0x3F, + f6 = 0x40, + f7 = 0x41, + f8 = 0x42, + f9 = 0x43, + f10 = 0x44, + f11 = 0x57, + f12 = 0x58, + f13 = 0x64, + f14 = 0x65, + f15 = 0x66, + f16 = 0x67, + f17 = 0x68, + f18 = 0x69, + f19 = 0x71, + + -- Japanese keyboards + kana = 0x70, + kanji = 0x94, + convert = 0x79, + noconvert = 0x7B, + yen = 0x7D, + circumflex = 0x90, + ax = 0x96, + + -- Numpad + numpad0 = 0x52, + numpad1 = 0x4F, + numpad2 = 0x50, + numpad3 = 0x51, + numpad4 = 0x4B, + numpad5 = 0x4C, + numpad6 = 0x4D, + numpad7 = 0x47, + numpad8 = 0x48, + numpad9 = 0x49, + numpadmul = 0x37, + numpaddiv = 0xB5, + numpadsub = 0x4A, + numpadadd = 0x4E, + numpaddecimal = 0x53, + numpadcomma = 0xB3, + numpadenter = 0x9C, + numpadequals = 0x8D, +} + +do + local keys = {} + for k in pairs(keyboard.keys) do + table.insert(keys, k) + end + for _, k in pairs(keys) do + keyboard.keys[keyboard.keys[k]] = k + end +end + +function keyboard.isControl(char) + return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F)) +end + +local on = {} + +function on.key_down(_, source, ascii, keycode, user) + if not keyboards[source] then return end + keyboard.pressedChars[ascii] = true + keyboard.pressedCodes[keycode] = true + + if ascii == 13 then ascii = 10 end + if ascii ~= 0 then + io.stdout:write(unicode.char(ascii)) + else + if keycode == 200 then io.stdout:write("\x1b[A") + elseif keycode == 208 then io.stdout:write("\x1b[B") + elseif keycode == 205 then io.stdout:write("\x1b[C") + elseif keycode == 203 then io.stdout:write("\x1b[D") + end + end +end + +function on.key_up(_, source, ascii, keycode, user) + if not keyboards[source] then return end + keyboard.pressedChars[ascii] = nil + keyboard.pressedCodes[keycode] = nil +end + +function on.clipboard(_, source, data, user) + io.stdout:write(data) +end + +while true do + local signal = {computer.pullSignal()} + if on[signal[1]] then + on[signal[1]](table.unpack(signal)) + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/reboot.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/reboot.lua new file mode 100644 index 0000000000..a6459c0b04 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/reboot.lua @@ -0,0 +1,4 @@ +local computer = require("computer") + +io.write("Rebooting...") +computer.shutdown(true) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/rm.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/rm.lua new file mode 100644 index 0000000000..a3dd6d6f74 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/rm.lua @@ -0,0 +1,18 @@ +local shell = require("shell") + +local args, options = shell.parse(...) +if #args == 0 then + io.write("Usage: rm [-v] [ [...]]\n") + io.write(" -v: verbose output.") + return +end + +for i = 1, #args do + local path = shell.resolve(args[i]) + if not os.remove(path) then + io.stderr:write(path .. ": no such file, or permission denied\n") + end + if options.v then + io.write("removed '" .. path .. "'\n") + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/route.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/route.lua new file mode 100644 index 0000000000..dfb8189b43 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/route.lua @@ -0,0 +1,25 @@ +local network = require "network" + +local function fillText(text, n) + for k = 1, n - #text do + text = text .. " " + end + return text +end + +print("MCNET routing table") +local routes = network.info.getRoutes() +local maxlen = {12, 8, 5} + +for host, route in pairs(routes) do + maxlen[1] = maxlen[1] < #host+1 and #host+1 or maxlen[1] + maxlen[2] = maxlen[2] < #route.router+1 and #route.router+1 or maxlen[2] + maxlen[3] = maxlen[3] < #route.interface+1 and #route.interface+1 or maxlen[3] +end + +print(fillText("Destination", maxlen[1])..fillText("Gateway", maxlen[2])..fillText("Iface", maxlen[3])) + +for host, route in pairs(routes) do + print(fillText(host, maxlen[1])..fillText(route.router, maxlen[2])..fillText(route.interface, maxlen[3])) +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua new file mode 100644 index 0000000000..73bb305533 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sh.lua @@ -0,0 +1,199 @@ +local kernel = require("pipes") +local fs = require("filesystem") + +local builtin = {} + +local function tokenize(cmd) + local res = {} + + local currentWord = "" + + for char in string.gmatch(cmd, ".") do + if char:match("[%w%._%-/~=:]") then + currentWord = currentWord .. char + elseif char:match("[|><&]") then + if #currentWord > 0 then + res[#res + 1] = currentWord + currentWord = "" + end + res[#res + 1] = char + elseif char:match("%s") and #currentWord > 0 then + res[#res + 1] = currentWord + currentWord = "" + end + end + if #currentWord > 0 then + res[#res + 1] = currentWord + end + --print("Tokenized: ", table.unpack(res)) + return res +end + +local function parse(tokens) + -- {{arg = {"cat", "file.txt"}, stdin = "-", stdout = 1}, {arg = {"wc", "-c"}, stdin = 1, stdout = "out.txt"}}, 1 + + local res = {} + local pipes = 0 + + local nextin = "-" + local nextout = "-" + local arg = {} + + local skip = false + + for k, token in ipairs(tokens) do + if not skip then + if token:match("[%w%._%-/~]+") and not notarg then + arg[#arg + 1] = token + elseif token == "|" then + pipes = pipes + 1 + res[#res + 1] = {arg = arg, stdin = nextin, stdout = pipes} + nextin = pipes + nextout = "-" + arg = {} + elseif token == "<" and type(nextin) ~= "number" then + if not tokens[k + 1]:match("[%w%._%-/~]+") then error("Syntax error") end + nextin = tokens[k + 1] + skip = true + elseif token == ">" then + if not tokens[k + 1]:match("[%w%._%-/~]+") then error("Syntax error") end + nextout = tokens[k + 1] + skip = true + elseif token == "&" then + res[#res + 1] = {arg = arg, stdin = nextin, stdout = nextout, nowait = true} + nextout = "-" + nextin = "-" + arg = {} + end + else + skip = false + end + end + + if #arg > 0 then + res[#res + 1] = {arg = arg, stdin = nextin, stdout = nextout} + end + + return res, pipes +end + +local function resolveProgram(name) + if builtin[name] then + return builtin[name] + end + for dir in string.gmatch(os.getenv("PATH"), "[^:$]+") do + if dir:sub(1,1) ~= "/" then + local dir = fs.concat(os.getenv("PWD") or "/", dir) + end + local file = fs.concat(dir, name .. ".lua") + if fs.exists(file) then + return file + end + end +end + +local function execute(cmd) + local tokens = tokenize(cmd) + local programs, npipes = parse(tokens) + + local pipes = {} + + for i=1, npipes do + pipes[i] = {io.pipe()} + end + + local processes = {} + + for n, program in pairs(programs) do + local prog = program.arg[1] + program.arg[1] = resolveProgram(prog) + if not program.arg[1] then + print("\x1b[31mProgram '" .. tostring(prog) .. "' was not found\x1b[39m") + return + end + end + + local res, message = pcall(function() + for n, program in pairs(programs) do + local sin = type(program.stdin) == "number" and pipes[program.stdin][1] or program.stdin == "-" and io.input() or io.open(program.stdin, "r") + local sout = type(program.stdout) == "number" and pipes[program.stdout][2] or program.stdout == "-" and io.output() or io.open(program.stdout, "w") + + processes[n] = { + pid = os.spawnp(program.arg[1], sin, sout, io.stderr, table.unpack(program.arg, 2)), + stdin = sin, + stdout = sout + } + end + end) + + for n, process in pairs(processes) do + if not programs[n].nowait then + kernel.joinThread(process.pid) + end + + if io.output() ~= process.stdout then pcall(function() process.stdout:close() end) end + if io.input() ~= process.stdin then pcall(function() process.stdin:close() end) end + end + + if not res then + io.write("\x1b[31m") + print(message) + io.write("\x1b[39m") + end +end + +local function expand(value) + local result = value:gsub("%$(%w+)", os.getenv):gsub("%$%b{}", + function(match) return os.getenv(expand(match:sub(3, -2))) or match end) + return result +end + +local run = true + +builtin.cd = function(dir) + if not dir then + os.setenv("PWD", os.getenv("HOME") or "/") + else + if dir:sub(1,1) ~= "/" then + dir = fs.concat(os.getenv("PWD"), dir) + end + if not fs.isDirectory(dir) then + io.stderr:write("cd: " .. tostring(dir) .. ": Not a directory\n") + return 1 + end + os.setenv("PWD", dir) + end +end + +builtin.exit = function() + run = false +end + +local term = require("term") +local history = {} + +if fs.exists("~/.history") then + for line in io.lines("~/.history") do + if #line > 0 then + table.insert(history, 1, line) + end + end + table.insert(history, 1, "") +end + +local function log(cmd) + local hisfile = io.open("~/.history", "a") + if #cmd > 0 then + hisfile:write(cmd .. "\n") + end + hisfile:close() +end + +while run do + io.write(expand(os.getenv("PS1"))) + local cmd = term.read(history)--io.read("*l") + --print("--IN: ", cmd) + execute(cmd) + --print("--OUT: ", cmd) + log(cmd) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sleep.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sleep.lua new file mode 100644 index 0000000000..d6de0e9622 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/sleep.lua @@ -0,0 +1,13 @@ +local event = require("event") + +function sleep(timeout) + checkArg(1, timeout, "number", "nil") + local deadline = computer.uptime() + (timeout or 0) + repeat + event.pull(deadline - computer.uptime()) + until computer.uptime() >= deadline +end + +local args = {...} + +sleep(tonumber(args[1])) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua new file mode 100644 index 0000000000..828335ac39 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/tee.lua @@ -0,0 +1,17 @@ +local args = {...} + +local f = io.open(args[1], "a") + +while true do + local data = io.read(1) + if not data then + f:close() + return + end + if io.input().remaining() > 0 then + data = data .. io.read(io.input().remaining()) + end + f:write(data) + f:flush() + io.write(data) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/wc.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/wc.lua new file mode 100644 index 0000000000..fa0f535322 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/wc.lua @@ -0,0 +1,41 @@ +local unicode = require("unicode") +local shell = require("shell") + +local args, options = shell.parse(...) + +local bytes = 0 +local chars = 0 +local words = 0 +local lines = 0 + +local word = false +while true do + local data = io.read(1) + if not data then break end + if io.input().remaining() > 0 then + data = data .. io.read(io.input().remaining()) + end + bytes = bytes + #data + chars = chars + unicode.len(data) + for char in data:gmatch(".") do + if char == "\n" then + lines = lines + 1 + end + if data:match("%s") and word then + word = false + words = words + 1 + else + word = true + end + end +end + +if options.c or options.bytes then + print(bytes) +elseif options.m or options.chars then + print(chars) +elseif options.l or options.lines then + print(lines) +else + print(words) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/wget.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/wget.lua new file mode 100644 index 0000000000..8b23c8d073 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/wget.lua @@ -0,0 +1,102 @@ +local component = require("component") +local fs = require("filesystem") +local internet = require("internet") +local shell = require("shell") +local text = require("text") + +if not component.isAvailable("internet") then + io.stderr:write("This program requires an internet card to run.") + return +end + +local args, options = shell.parse(...) +options.q = options.q or options.Q + +if #args < 1 then + io.write("Usage: wget [-fq] []\n") + io.write(" -f: Force overwriting existing files.\n") + io.write(" -q: Quiet mode - no status messages.\n") + io.write(" -Q: Superquiet mode - no error messages.") + return +end + +local url = text.trim(args[1]) +local filename = args[2] +if not filename then + filename = url + local index = string.find(filename, "/[^/]*$") + if index then + filename = string.sub(filename, index + 1) + end + index = string.find(filename, "?", 1, true) + if index then + filename = string.sub(filename, 1, index - 1) + end +end +filename = text.trim(filename) +if filename == "" then + if not options.Q then + io.stderr:write("could not infer filename, please specify one") + end + return nil, "missing target filename" -- for programs using wget as a function +end +filename = shell.resolve(filename) + +if fs.exists(filename) then + if not options.f or not os.remove(filename) then + if not options.Q then + io.stderr:write("file already exists") + end + return nil, "file already exists" -- for programs using wget as a function + end +end + +local f, reason = io.open(filename, "wb") +if not f then + if not options.Q then + io.stderr:write("failed opening file for writing: " .. reason) + end + return nil, "failed opening file for writing: " .. reason -- for programs using wget as a function +end + +if not options.q then + io.write("Downloading... ") +end +local result, response = pcall(internet.request, url) +if result then + local result, reason = pcall(function() + for chunk in response do + f:write(chunk) + end + end) + if not result then + if not options.q then + io.stderr:write("failed.\n") + end + f:close() + fs.remove(filename) + if not options.Q then + io.stderr:write("HTTP request failed: " .. reason .. "\n") + end + return nil, reason -- for programs using wget as a function + end + if not options.q then + io.write("success.\n") + end + + f:close() + if not options.q then + io.write("Saved data to " .. filename .. "\n") + end +else + if not options.q then + io.write("failed.\n") + end + f:close() + fs.remove(filename) + if not options.Q then + io.stderr:write("HTTP request failed: " .. response .. "\n") + end + return nil, response -- for programs using wget as a function +end +return true -- for programs using wget as a function diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes b/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes new file mode 100644 index 0000000000..470abc3562 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes @@ -0,0 +1,172 @@ +local computer = computer +local component = component +local unicode = unicode +local kernel = {} + +kernel.io = {} +kernel._K = _G + +_G._OSVERSION = "Pipes/1.0" + +local runlevel, shutdown = "S", computer.shutdown +computer.runlevel = function() return runlevel end +computer.shutdown = function(reboot) + runlevel = reboot and 6 or 0 + if os.sleep then + computer.pushSignal("shutdown") + os.sleep(0.1) -- Allow shutdown processing. + end + shutdown(reboot) +end + +--Setup initial drive + +kernel.initd = {} +function kernel.initd.invoke(method, ...) + return component.invoke(computer.getBootAddress(), method, ...) +end +function kernel.initd.open(file) return kernel.initd.invoke("open", file) end +function kernel.initd.read(handle) return kernel.initd.invoke("read", handle, math.huge) end +function kernel.initd.close(handle) return kernel.initd.invoke("close", handle) end +function kernel.initd.isDirectory(path) return kernel.initd.invoke("isDirectory", path) end + +--Setup early kernel logging + +do + local screen = component.list('screen')() + for address in component.list('screen') do + if #component.invoke(address, 'getKeyboards') > 0 then + screen = address + end + end + + local gpu = component.list("gpu", true)() + local w, h + if gpu and screen then + component.invoke(gpu, "bind", screen) + w, h = component.invoke(gpu, "getResolution") + component.invoke(gpu, "setResolution", w, h) + component.invoke(gpu, "setBackground", 0x000000) + component.invoke(gpu, "setForeground", 0xFFFFFF) + component.invoke(gpu, "fill", 1, 1, w, h, " ") + end + local y = 1 + + function kernel._println(msg) + if gpu and screen then + kernel._K.component.invoke(gpu, "set", 1, y, msg) + if y == h then + kernel._K.component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) + kernel._K.component.invoke(gpu, "fill", 1, h, w, 1, " ") + else + y = y + 1 + end + end + end + kernel.io.println = kernel._println +end + +kernel.io.println(_OSVERSION .. " Starting") + +--The most important function + +local panicPull = kernel._K.computer.pullSignal +function kernel.panic() + kernel._println("--------------------------------------------------------") + pcall(function() + for s in string.gmatch(debug.traceback(), "[^\r\n]+") do + kernel._println(s) + end + end) + pcall(function() + kernel._println("--------------------------------------------------------") + kernel._println("System halted: Panic") + end) + while true do panicPull(0.5) kernel._K.computer.beep() end +end + +-- Custom low-level loadfile/dofile implementation reading from our Init disk. +local function loadfile(file, _, _, env) + --kernel.io.println("> " .. file) + local handle, reason = kernel.initd.open(file) + if not handle then + error(reason) + end + local buffer = "" + repeat + local data, reason = kernel.initd.read(handle) + if not data and reason then + error(reason) + end + buffer = buffer .. (data or "") + until not data + kernel.initd.close(handle) + return load(buffer, "=" .. file, nil, env) +end + +local function dofile(file) + local program, reason = loadfile(file) + if program then + local result = table.pack(pcall(program)) + if result[1] then + return table.unpack(result, 2, result.n) + else + error(result[2]) + end + else + error(reason) + end +end + +--Setup early managed _G +local _K = _G +_G = _G.setmetatable({}, {__index = _K}) +kernel._K = _K +kernel._G = _G + +kernel.io.println("Loading base modules") + +local modules = {} +for _, file in ipairs(kernel.initd.invoke("list", "lib/modules/base")) do + local path = "lib/modules/base/" .. file + if not kernel.initd.isDirectory(path) then + table.insert(modules, {path = path, file = file, name = file:match("[^0-9_%.]+")}) + end +end + +local moduleEnv = setmetatable({kernel = kernel}, {__index = _K}) +kernel.modules = {} +table.sort(modules, function(a, b) return a.file < b.file end) +for i = 1, #modules do + kernel.io.println("Load module " .. modules[i].name) + + kernel.modules[modules[i].name] = {} + kernel.modules[modules[i].name]._G = kernel.modules[modules[i].name] + local program, reason = loadfile(modules[i].path, nil, nil, setmetatable(kernel.modules[modules[i].name], {__index = moduleEnv})) + if program then + local result = table.pack(pcall(program)) + if not result[1] then + kernel.io.println(result[2]) + kernel.panic() + end + + else + kernel.io.println(reason) + kernel.panic() + end +end + +kernel.io.println("Starting base modules") + +for i = 1, #modules do + if kernel.modules[modules[i].name].start then + kernel.io.println("Start module " .. modules[i].name) + local result = table.pack(pcall(kernel.modules[modules[i].name].start)) + if not result[1] then + pcall(kernel._println, result[2]) + kernel.panic() + end + end +end + +kernel.panic() diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/etc/rc.cfg b/src/main/resources/assets/opencomputers/loot/Plan9k/etc/rc.cfg new file mode 100644 index 0000000000..b7ddd2e352 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/etc/rc.cfg @@ -0,0 +1 @@ +enabled = {} diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua new file mode 100644 index 0000000000..588b8d0e31 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua @@ -0,0 +1,134 @@ +_G._OSVERSION = "OpenLoader 0.2" +local component = component or require('component') +local computer = computer or require('computer') +local unicode = unicode or require('unicode') + +local eeprom = component.list("eeprom")() +computer.getBootAddress = function() + return component.invoke(eeprom, "getData") +end +computer.setBootAddress = function(address) + return component.invoke(eeprom, "setData", address) +end + +local gpu = component.list("gpu")() +local w, h + +local screen = component.list('screen')() +for address in component.list('screen') do + if #component.invoke(address, 'getKeyboards') > 0 then + screen = address + end +end + +local cls = function()end +if gpu and screen then + component.invoke(gpu, "bind", screen) + w, h = component.invoke(gpu, "getResolution") + component.invoke(gpu, "setResolution", w, h) + component.invoke(gpu, "setBackground", 0x000000) + component.invoke(gpu, "setForeground", 0xFFFFFF) + component.invoke(gpu, "fill", 1, 1, w, h, " ") + cls = function()component.invoke(gpu,"fill", 1, 1, w, h, " ")end +end +local y = 1 +local function status(msg) + if gpu and screen then + component.invoke(gpu, "set", 1, y, msg) + if y == h then + component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) + component.invoke(gpu, "fill", 1, h, w, 1, " ") + else + y = y + 1 + end + end +end + +local function loadfile(fs, file) + --status("> " .. file) + local handle, reason = component.invoke(fs,"open",file) + if not handle then + error(reason) + end + local buffer = "" + repeat + local data, reason = component.invoke(fs,"read",handle,math.huge) + if not data and reason then + error(reason) + end + buffer = buffer .. (data or "") + until not data + component.invoke(fs,"close",handle) + return load(buffer, "=" .. file) +end + +local function dofile(fs, file) + local program, reason = loadfile(fs, file) + if program then + local result = table.pack(pcall(program)) + if result[1] then + return table.unpack(result, 2, result.n) + else + error(result[2]) + end + else + error(reason) + end +end + +local function boot(kernel) + status("BOOTING") + _G.computer.getBootAddress = function()return kernel.address end + cls() + dofile(kernel.address, kernel.fpx .. kernel.file) +end + +status(_OSVERSION) +status("Select what to boot:") + +local osList = {} + +for fs in component.list("filesystem") do + if component.invoke(fs, "isDirectory", "boot/kernel/")then + for _,file in ipairs(component.invoke(fs, "list", "boot/kernel/")) do + osList[#osList+1] = {fpx = "boot/kernel/", file = file, address = fs} + status(tostring(#osList).."."..file.." from "..(fs:sub(1,3))) + end + end + if fs ~= computer.getBootAddress() and component.invoke(fs, "exists", "init.lua") then + local osName = "init.lua" + if component.invoke(fs, "exists", ".osprop") then + pcall(function() + local prop = dofile(fs, ".osprop") + osName = (prop and prop.name) or "init.lua" + end) + end + osList[#osList+1] = {fpx = "", file = "init.lua", address = fs} + status(tostring(#osList).."."..osName.." from "..(fs:sub(1,3))) + end +end +status("Select os: ") +if #osList == 1 then + boot(osList[1]) +end +if #osList == 0 then + error("No OS found") + while true do computer.pullSignal() end +end +while true do + local sig = {computer.pullSignal()} + if sig[1] == "key_down" then + if sig[4] >= 2 and sig[4] <= 11 then + if osList[sig[4]-1] then + boot(osList[sig[4]-1]) + else + status("Not found!") + end + end + end +end +error("System crashed") +while true do computer.pullSignal() end + + + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/event.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/event.lua new file mode 100644 index 0000000000..c9521395e2 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/event.lua @@ -0,0 +1,248 @@ +local computer = require("computer") +local keyboard = require("keyboard") + +local event, listeners, timers = {}, {}, {} +local lastInterrupt = -math.huge + +local function call(callback, ...) + local result, message = pcall(callback, ...) + if not result and type(event.onError) == "function" then + pcall(event.onError, message) + return + end + return message +end + +local function dispatch(signal, ...) + if listeners[signal] then + local function callbacks() + local list = {} + for index, listener in ipairs(listeners[signal]) do + list[index] = listener + end + return list + end + for _, callback in ipairs(callbacks()) do + if call(callback, signal, ...) == false then + event.ignore(signal, callback) -- alternative method of removing a listener + end + end + end +end + +local function tick() + local function elapsed() + local list = {} + for id, timer in pairs(timers) do + if timer.after <= computer.uptime() then + table.insert(list, timer.callback) + timer.times = timer.times - 1 + if timer.times <= 0 then + timers[id] = nil + else + timer.after = computer.uptime() + timer.interval + end + end + end + return list + end + for _, callback in ipairs(elapsed()) do + call(callback) + end +end + +local function createPlainFilter(name, ...) + local filter = table.pack(...) + if name == nil and filter.n == 0 then + return nil + end + + return function(...) + local signal = table.pack(...) + if name and not (type(signal[1]) == "string" and signal[1]:match(name)) then + return false + end + for i = 1, filter.n do + if filter[i] ~= nil and filter[i] ~= signal[i + 1] then + return false + end + end + return true + 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 timers[timerId] then + timers[timerId] = nil + return true + end + return false +end + +function event.ignore(name, callback) + checkArg(1, name, "string") + checkArg(2, callback, "function") + if listeners[name] then + for i = 1, #listeners[name] do + if listeners[name][i] == callback then + table.remove(listeners[name], i) + if #listeners[name] == 0 then + listeners[name] = nil + end + return true + end + end + end + return false +end + +function event.listen(name, callback) + checkArg(1, name, "string") + checkArg(2, callback, "function") + if listeners[name] then + for i = 1, #listeners[name] do + if listeners[name][i] == callback then + return false + end + end + else + listeners[name] = {} + end + table.insert(listeners[name], callback) + return true +end + +function event.onError(message) + local log = io.open("/tmp/event.log", "a") + if log then + log:write(message .. "\n") + log:close() + end +end + +function event.pull(...) + local args = table.pack(...) + if type(args[1]) == "string" then + return event.pullFiltered(createPlainFilter(...)) + else + checkArg(1, args[1], "number", "nil") + checkArg(2, args[2], "string", "nil") + return event.pullFiltered(args[1], createPlainFilter(select(2, ...))) + 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 + + if type(args[1]) == "function" then + filter = args[1] + else + checkArg(1, args[1], "number", "nil") + checkArg(2, args[2], "function", "nil") + seconds = args[1] + filter = args[2] + end + + local deadline = seconds and + (computer.uptime() + seconds) or + (filter and math.huge or 0) + repeat + local closest = seconds and deadline or math.huge + for _, timer in pairs(timers) do + closest = math.min(closest, timer.after) + end + local signal = table.pack(computer.pullSignal(closest - computer.uptime())) + if signal.n > 0 then + dispatch(table.unpack(signal, 1, signal.n)) + end + tick() + if event.shouldInterrupt() then + lastInterrupt = computer.uptime() + error("interrupted", 0) + end + if event.shouldSoftInterrupt() and (filter == nil or filter("interrupted", computer.uptime() - lastInterrupt)) then + local awaited = computer.uptime() - lastInterrupt + lastInterrupt = computer.uptime() + return "interrupted", awaited + end + if not (seconds or filter) or filter == nil or filter(table.unpack(signal, 1, signal.n)) then + return table.unpack(signal, 1, signal.n) + end + until computer.uptime() >= deadline +end + +function event.shouldInterrupt() + return computer.uptime() - lastInterrupt > 1 and + keyboard.isControlDown() and + keyboard.isAltDown() and + keyboard.isKeyDown(keyboard.keys.c) +end + +function event.shouldSoftInterrupt() + return computer.uptime() - lastInterrupt > 1 and + keyboard.isControlDown() and + keyboard.isKeyDown(keyboard.keys.c) +end + +function event.timer(interval, callback, times) + checkArg(1, interval, "number") + checkArg(2, callback, "function") + checkArg(3, times, "number", "nil") + local id + repeat + id = math.floor(math.random(1, 0x7FFFFFFF)) + until not timers[id] + timers[id] = { + interval = interval, + after = computer.uptime() + interval, + callback = callback, + times = times or 1 + } + return id +end + +------------------------------------------------------------------------------- + +return event diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/internet.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/internet.lua new file mode 100644 index 0000000000..886ff6f4e4 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/internet.lua @@ -0,0 +1,124 @@ +local buffer = require("buffer") +local component = require("component") + +local internet = {} + +------------------------------------------------------------------------------- + +function internet.request(url, data) + checkArg(1, url, "string") + checkArg(2, data, "string", "table", "nil") + + local inet = component.internet + if not inet then + error("no primary internet card found", 2) + end + + local post + if type(data) == "string" then + post = data + elseif type(data) == "table" then + for k, v in pairs(data) do + post = post and (post .. "&") or "" + post = post .. tostring(k) .. "=" .. tostring(v) + end + end + + local request, reason = inet.request(url, post) + if not request then + error(reason, 2) + end + + return function() + while true do + local data, reason = request.read() + if not data then + request.close() + if reason then + error(reason, 2) + else + return nil -- eof + end + elseif #data > 0 then + return data + end + -- else: no data, block + os.sleep(0) + end + end +end + +------------------------------------------------------------------------------- + +local socketStream = {} + +function socketStream:close() + if self.socket then + self.socket.close() + self.socket = nil + end +end + +function socketStream:seek() + return nil, "bad file descriptor" +end + +function socketStream:read(n) + if not self.socket then + return nil, "connection is closed" + end + return self.socket.read(n) +end + +function socketStream:write(value) + if not self.socket then + return nil, "connection is closed" + end + while #value > 0 do + local written, reason = self.socket.write(value) + if not written then + return nil, reason + end + value = string.sub(value, written + 1) + end + return true +end + +function internet.socket(address, port) + checkArg(1, address, "string") + checkArg(2, port, "number", "nil") + if port then + address = address .. ":" .. port + end + + local inet = component.internet + local socket, reason = inet.connect(address) + if not socket then + return nil, reason + end + + local stream = {inet = inet, socket = socket} + + -- stream:close does a syscall, which yields, and that's not possible in + -- the __gc metamethod. So we start a timer to do the yield/cleanup. + local function cleanup(self) + if not self.socket then return end + pcall(self.socket.close) + end + local metatable = {__index = socketStream, + __gc = cleanup, + __metatable = "socketstream"} + return setmetatable(stream, metatable) +end + +function internet.open(address, port) + local stream, reason = internet.socket(address, port) + if not stream then + return nil, reason + end + return buffer.new("rwb", stream) +end + +------------------------------------------------------------------------------- + +return internet diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/01_util.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/01_util.lua new file mode 100644 index 0000000000..f97b52dc5d --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/01_util.lua @@ -0,0 +1,19 @@ + +function getAllocator() + local allocator = {next = 1} + local list = {} + function allocator:get() + local n = self.next + self.next = (list[n] and list[n].next) or (#list + 2) + list[n] = {id = n} + return list[n] + end + + function allocator:unset(e) + local eid = e.id + list[eid] = {next = self.next} + self.next = eid + return list[n] + end + return allocator, list +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua new file mode 100644 index 0000000000..1a45be7626 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua @@ -0,0 +1,552 @@ +local unicode = unicode + +local filesystem, fileStream = {}, {} +local isAutorunEnabled = nil +local mtab = {name="", children={}, links={}} + +local function segments(path) + path = path:gsub("\\", "/") + repeat local n; path, n = path:gsub("//", "/") until n == 0 + local parts = {} + for part in path:gmatch("[^/]+") do + table.insert(parts, part) + end + local i = 1 + while i <= #parts do + if parts[i] == "." then + table.remove(parts, i) + elseif parts[i] == ".." then + table.remove(parts, i) + i = i - 1 + if i > 0 then + table.remove(parts, i) + else + i = 1 + end + else + i = i + 1 + end + end + return parts +end + +local function saveConfig() + local root = filesystem.get("/") + if root and not root.isReadOnly() then + filesystem.makeDirectory("/etc") + local f = io.open("/etc/filesystem.cfg", "w") + if f then + f:write("autorun="..tostring(isAutorunEnabled)) + f:close() + end + end +end + +local function resolve(path) + if unicode.sub(path, 1, 1) == "/" then + return filesystem.canonical(path) + elseif unicode.sub(path, 1, 2) == "~/" then + return filesystem.concat(kernel.modules.threading.currentThread.env.HOME or "/", path:sub(2)) + else + return filesystem.concat(kernel.modules.threading.currentThread.env.PWD or "/", path) + end +end + +local function findNode(path, create, depth) + checkArg(1, path, "string") + depth = depth or 0 + if depth > 100 then + error("link cycle detected") + end + local parts = segments(path) + local node = mtab + while #parts > 0 do + local part = parts[1] + if not node.children[part] then + if node.links[part] then + return findNode(filesystem.concat(node.links[part], table.concat(parts, "/", 2)), create, depth + 1) + else + if create then + node.children[part] = {name=part, parent=node, children={}, links={}} + else + local vnode, vrest = node, table.concat(parts, "/") + local rest = vrest + while node and not node.fs do + rest = filesystem.concat(node.name, rest) + node = node.parent + end + return node, rest, vnode, vrest + end + end + end + node = node.children[part] + table.remove(parts, 1) + end + local vnode, vrest = node, nil + local rest = nil + while node and not node.fs do + rest = rest and filesystem.concat(node.name, rest) or node.name + node = node.parent + end + return node, rest, vnode, vrest +end + +local function removeEmptyNodes(node) + while node and node.parent and not node.fs and not next(node.children) and not next(node.links) do + node.parent.children[node.name] = nil + node = node.parent + end +end + +------------------------------------------------------------------------------- + +filesystem.resolve = resolve + +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.segments(path) + return segments(path) +end + +function filesystem.canonical(path) + local result = table.concat(segments(path), "/") + if unicode.sub(path, 1, 1) == "/" then + return "/" .. result + else + return result + end +end + +function filesystem.concat(pathA, pathB, ...) + checkArg(1, pathA, "string") + local function concat(n, a, b, ...) + if not b then + return a + end + checkArg(n, b, "string") + return concat(n + 1, a .. "/" .. b, ...) + end + return filesystem.canonical(concat(2, pathA, pathB, ...)) +end + +function filesystem.get(path) + local node, rest = findNode(resolve(path)) + if node.fs then + local proxy = node.fs + path = "" + while node and node.parent do + path = filesystem.concat(node.name, path) + node = node.parent + end + path = filesystem.canonical(path) + if path ~= "/" then + path = "/" .. path + end + return proxy, path + end + return nil, "no such file system" +end + +function filesystem.isLink(path) + local node, rest, vnode, vrest = findNode(filesystem.path(resolve(path))) + if not vrest and vnode.links[filesystem.name(path)] ~= nil then + return true, vnode.links[filesystem.name(path)] + end + return false +end + +function filesystem.link(target, linkpath) + checkArg(1, target, "string") + checkArg(2, linkpath, "string") + + linkpath = resolve(linkpath) + if filesystem.exists(linkpath) then + return nil, "file already exists" + end + + local node, rest, vnode, vrest = findNode(filesystem.path(linkpath), true) + vnode.links[filesystem.name(linkpath)] = target + return true +end + +function filesystem.mount(fs, path) + checkArg(1, fs, "string", "table") + if type(fs) == "string" then + fs = filesystem.proxy(fs) + end + assert(type(fs) == "table", "bad argument #1 (file system proxy or address expected)") + checkArg(2, path, "string") + + path = resolve(path) + if path ~= "/" and filesystem.exists(path) then + return nil, "file already exists" + end + + local node, rest, vnode, vrest = findNode(path, true) + if vnode.fs then + return nil, "another filesystem is already mounted here" + end + vnode.fs = fs + return true +end + +function filesystem.mounts() + local function path(node) + local result = "/" + while node and node.parent do + for name, child in pairs(node.parent.children) do + if child == node then + result = "/" .. name .. result + break + end + end + node = node.parent + end + return result + end + local queue = {mtab} + return function() + while #queue > 0 do + local node = table.remove(queue) + for _, child in pairs(node.children) do + table.insert(queue, child) + end + if node.fs then + return node.fs, path(node) + end + end + end +end + +function filesystem.path(path) + local parts = segments(path) + local result = table.concat(parts, "/", 1, #parts - 1) .. "/" + if unicode.sub(path, 1, 1) == "/" and unicode.sub(result, 1, 1) ~= "/" then + return "/" .. result + else + return result + end +end + +function filesystem.name(path) + local parts = segments(path) + return parts[#parts] +end + + +function filesystem.proxy(filter) + checkArg(1, filter, "string") + local address + for c in component.list("filesystem") 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 + return component.proxy(address) +end + + +function filesystem.umount(fsOrPath) + checkArg(1, fsOrPath, "string", "table") + if type(fsOrPath) == "string" then + local node, rest, vnode, vrest = findNode(fsOrPath) + if not vrest and vnode.fs then + vnode.fs = nil + removeEmptyNodes(vnode) + return true + end + end + local address = type(fsOrPath) == "table" and fsOrPath.address or fsOrPath + local result = false + for proxy, path in filesystem.mounts() do + local addr = type(proxy) == "table" and proxy.address or proxy + if string.sub(addr, 1, address:len()) == address then + local node, rest, vnode, vrest = findNode(path) + vnode.fs = nil + removeEmptyNodes(vnode) + result = true + end + end + return result +end + +function filesystem.exists(path) + local node, rest, vnode, vrest = findNode(resolve(path)) + if not vrest or vnode.links[vrest] then -- virtual directory or symbolic link + return true + end + if node and node.fs then + return node.fs.exists(rest) + end + return false +end + +function filesystem.size(path) + local node, rest, vnode, vrest = findNode(resolve(path)) + if not vnode.fs and (not vrest or vnode.links[vrest]) then + return 0 -- virtual directory or symlink + end + if node.fs and rest then + return node.fs.size(rest) + end + return 0 -- no such file or directory +end + +function filesystem.isDirectory(path) + local node, rest, vnode, vrest = findNode(resolve(path)) + if not vnode.fs and not vrest then + return true -- virtual directory + end + if node.fs then + return not rest or node.fs.isDirectory(rest) + end + return false +end + +function filesystem.lastModified(path) + local node, rest, vnode, vrest = findNode(resolve(path)) + if not vnode.fs and not vrest then + return 0 -- virtual directory + end + if node.fs and rest then + return node.fs.lastModified(rest) + end + return 0 -- no such file or directory +end + +function filesystem.list(path) + local node, rest, vnode, vrest = findNode(resolve(path)) + if not vnode.fs and vrest and not (node and node.fs) then + return nil, "no such file or directory" + end + local result, reason + if node and node.fs then + result, reason = node.fs.list(rest or "") + end + result = result or {} + if not vrest then + for k in pairs(vnode.children) do + table.insert(result, k .. "/") + end + for k in pairs(vnode.links) do + table.insert(result, k) + end + end + table.sort(result) + local i, f = 1, nil + while i <= #result do + if result[i] == f then + table.remove(result, i) + else + f = result[i] + i = i + 1 + end + end + local i = 0 + return function() + i = i + 1 + return result[i] + end +end + +function filesystem.makeDirectory(path) + if filesystem.exists(resolve(path)) then + return nil, "file or directory with that name already exists" + end + local node, rest = findNode(resolve(path)) + if node.fs and rest then + return node.fs.makeDirectory(rest) + end + if node.fs then + return nil, "virtual directory with that name already exists" + end + return nil, "cannot create a directory in a virtual directory" +end + +function filesystem.remove(path) + local function removeVirtual() + local node, rest, vnode, vrest = findNode(filesystem.path(resolve(path))) + local name = filesystem.name(resolve(path)) + if vnode.children[name] then + vnode.children[name] = nil + removeEmptyNodes(vnode) + return true + elseif vnode.links[name] then + vnode.links[name] = nil + removeEmptyNodes(vnode) + return true + end + return false + end + local function removePhysical() + node, rest = findNode(resolve(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) + oldPath = resolve(oldPath) + newPath = resolve(newPath) + if filesystem.isLink(oldPath) then + local node, rest, vnode, vrest = 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 + +function filesystem.copy(fromPath, toPath) + fromPath = resolve(fromPath) + toPath = resolve(toPath) + if filesystem.isDirectory(fromPath) then + return nil, "cannot copy folders" + end + local input, reason = kernel.modules.io.io.open(fromPath, "rb") + if not input then + return nil, reason + end + local output, reason = kernel.modules.io.io.open(toPath, "wb") + if not output then + input:close() + return nil, reason + end + repeat + local buffer, reason = input:read(1024) + if not buffer and reason then + return nil, reason + elseif buffer then + local result, reason = output:write(buffer) + if not result then + input:close() + output:close() + return nil, reason + end + end + until not buffer + input:close() + output:close() + return true +end + +function fileStream:close() + if self.handle then + self.fs.close(self.handle) + self.handle = nil + end +end + +function fileStream:read(n) + if not self.handle then + return nil, "file is closed" + end + return self.fs.read(self.handle, n) +end + +function fileStream:seek(whence, offset) + if not self.handle then + return nil, "file is closed" + end + return self.fs.seek(self.handle, whence, offset) +end + +function fileStream:write(str) + if not self.handle then + return nil, "file is closed" + end + return self.fs.write(self.handle, str) +end + +function filesystem.open(path, mode) + checkArg(1, path, "string") + mode = tostring(mode or "r") + checkArg(2, mode, "string") + assert(({r=true, rb=true, w=true, wb=true, a=true, ab=true})[mode], + "bad argument #2 (r[b], w[b] or a[b] expected, got " .. mode .. ")") + + local node, rest = findNode(resolve(path)) + if not node.fs or not rest then + return nil, "file not found" + end + + local handle, reason = node.fs.open(rest, mode) + if not handle then + return nil, reason + end + + local stream = {fs = node.fs, handle = handle} + + local function cleanup(self) + if not self.handle then return end + pcall(self.fs.close, self.handle) + end + local metatable = {__index = fileStream, + __gc = cleanup, + __metatable = "filestream"} + return setmetatable(stream, metatable) +end + +------------------------------------------------------------------------------- + +for k,v in pairs(filesystem) do + _G[k] = v +end + + +function start() + mount(computer.getBootAddress(), "/") +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua new file mode 100644 index 0000000000..5f2f1abff4 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua @@ -0,0 +1,102 @@ +proxy = {} +data = {} + +proxy.address = "devfs0000" +proxy.spaceUsed = function() return 0 end +proxy.spaceTotal = function() return 0 end +proxy.makeDirectory = function() error("Permission Denied") end +proxy.isReadOnly = function() return true end +proxy.rename = function() error("Permission Denied") end +proxy.remove = function() error("Permission Denied") end +proxy.setLabel = function() error("Permission Denied") end +proxy.seek = function() error("Not supported") end +proxy.size = function(path) + local seg = kernel.modules.vfs.segments(path) + local file = data + for _, d in pairs(seg) do + file = file[d] + end + return file.size and file.size() or 0 +end +proxy.getLabel = function() return "devfs" end + +local allocator, handles = kernel.modules.util.getAllocator() + +proxy.exists = function()end +proxy.open = function(path) + local seg = kernel.modules.vfs.segments(path) + local file = data + for _, d in pairs(seg) do + file = file[d] + end + local hnd = allocator:get() + hnd.file = file + if hnd.file.open then + hnd.file:open() + end + return hnd.id +end +proxy.read = function(h, ...) + return handles[h].file:read(...) +end +proxy.close = function(h) + allocator:unset(handles[h]) +end +proxy.write = function(h, ...) + return handles[h].file:write(...) +end +proxy.isDirectory = function(path) + local seg = kernel.modules.vfs.segments(path) + local dir = data + for _, d in pairs(seg) do + dir = dir[d] + end + if dir.__type then + return false + end + return true +end +proxy.list = function(path) + local seg = kernel.modules.vfs.segments(path) + local dir = data + for _, d in pairs(seg) do + dir = dir[d] + end + if dir.__type then + error("File is not a directory") + end + local list = {} + for f in pairs(dir) do + list[#list + 1] = f + end + return list +end + +data.pts = {} +data.null = { + __type = "f", + write = function()end +} +data.zero = { + __type = "f", + read = function(h, c) + c = c or 1 + return ("\0"):rep(c > (2^16) and (2^16) or c) + end +} +data.random = { + __type = "f", + read = function(h, c) + c = c or 1 + local s = "" + for i = 1, c do + s = s .. string.char(math.random(0, 255)) + end + return s + end +} + +function start() + kernel.modules.vfs.mount(proxy, "/dev") +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua new file mode 100644 index 0000000000..7c60579b2a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_procfs.lua @@ -0,0 +1,87 @@ +proxy = {} +data = {} + +proxy.address = "procfs000" +proxy.spaceUsed = function() return 0 end +proxy.spaceTotal = function() return 0 end +proxy.makeDirectory = function() error("Permission Denied") end +proxy.isReadOnly = function() return true end +proxy.rename = function() error("Permission Denied") end +proxy.remove = function() error("Permission Denied") end +proxy.setLabel = function() error("Permission Denied") end +proxy.seek = function() error("Not supported") end +proxy.getLabel = function() return "procfs" end + +local allocator, handles = kernel.modules.util.getAllocator() + +proxy.exists = function()end +proxy.open = function(path) + local seg = kernel.modules.vfs.segments(path) + local file = data + for _, d in pairs(seg) do + file = file[d] + end + local hnd = allocator:get() + hnd.data = file() + hnd.at = 1 + function hnd:read(n) + n = n or #self.data + local d = string.sub(self.data, self.at, self.at + n) + self.at = self.at + n + return #d > 0 and d or nil + end + return hnd.id +end +proxy.read = function(h, n) + return handles[h]:read(n) +end +proxy.close = function(h) + allocator:unset(handles[h]) +end +proxy.write = function() error("Permission Denied") end +proxy.isDirectory = function() + +end +proxy.list = function(path) + local seg = kernel.modules.vfs.segments(path) + local dir = data + for _, d in pairs(seg) do + dir = dir[d] + end + local list = {} + for pid, thr in pairs(kernel.modules.threading.threads) do + if thr.coro and dir == data then + list[#list + 1] = tostring(pid) + end + end + for f in pairs(dir) do + list[#list + 1] = f + end + return list +end + +data.meminfo = function() + return "MemTotal: " .. math.floor(computer.totalMemory() / 1024) .. " kB\n" + .. "MemFree: " .. math.floor(computer.freeMemory() / 1024) .. " kB\n" + --Buffers?? +end + +data.cpuinfo = function() + return "processor : 0\n" .. + "vendor_id : OpenComputersLua\n" .. + "cpu family : 1\n" .. + "model : 1\n" .. + "model name : OpenComputers Lua CPU @ unkown Tier\n" .. + "microcode : 0x52\n" .. + "physical id : 0\n" +end + +setmetatable(data, {__index = function(_, k) + if tonumber(k) and kernel.modules.threading.threads[tonumber(k)] and kernel.modules.threading.threads[tonumber(k)].coro then + return {comm = function()return kernel.modules.threading.threads[tonumber(k)].name end} + end +end}) + +function start() + kernel.modules.vfs.mount(proxy, "/proc") +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua new file mode 100644 index 0000000000..74eb587dd1 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua @@ -0,0 +1,23 @@ +listeners = {} + +function listen(signal, listener) + listeners[signal] = listeners[signal] or {} + listeners[signal][#listeners[signal] + 1] = listener +end + +function start() + thread = kernel.modules.threading.spawn(function() + while true do + local sig = {coroutine.yield("signal", dl)} + if listeners[sig[1]] then + for _, listener in pairs(listeners[sig[1]]) do + --pcall(kernel.io.println, "KEVD: "..sig[1]) + xpcall(listener, function(e) + kernel.io.println("keventd error("..tostring(sig[1]).."): "..tostring(e)) + pcall(kernel.io.println, debug.traceback()) + end, table.unpack(sig)) + end + end + end + end, 0, "[keventd]") +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua new file mode 100644 index 0000000000..4b56bfa9db --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua @@ -0,0 +1,139 @@ +kernel.userspace = setmetatable({}, {__index = kernel._K}) + +kernel.userspace.computer = {} + +kernel.userspace.computer.address = kernel._K.computer.address +kernel.userspace.computer.tmpAddress = kernel._K.computer.tmpAddress +kernel.userspace.computer.freeMemory = kernel._K.computer.freeMemory +kernel.userspace.computer.totalMemory = kernel._K.computer.totalMemory +kernel.userspace.computer.energy = kernel._K.computer.energy +kernel.userspace.computer.maxEnergy = kernel._K.computer.maxEnergy +kernel.userspace.computer.isAvailable = kernel._K.computer.isAvailable +kernel.userspace.computer.shutdown = kernel._K.computer.shutdown +kernel.userspace.computer.users = kernel._K.computer.users +kernel.userspace.computer.addUser = kernel._K.computer.addUser +kernel.userspace.computer.removeUser = kernel._K.computer.removeUser +kernel.userspace.computer.pushSignal = kernel._K.computer.pushSignal +kernel.userspace.computer.uptime = kernel._K.computer.uptime + +kernel.userspace.computer.pullSignal = function(timeout) + return coroutine.yield("signal", timeout) +end + +kernel.userspace.computer.hasSignal = function(sigType) + for _,v in ipairs(kernel.modules.threading.currentThread.eventQueue) do + if v[1] == (sigType or "signal") then + return true + end + end + return false +end + +kernel.userspace.coroutine = {} + +kernel.userspace.os = setmetatable({}, {__index = kernel._K.os}) + +kernel.userspace.os.remove = kernel.modules.vfs.remove +kernel.userspace.os.rename = kernel.modules.vfs.rename + +function kernel.userspace.os.spawn(prog, ...) + local isThread = type(prog) == "function" + local name = isThread and kernel.modules.threading.currentThread.name or "unknown" + if type(prog) == "string" then + name = kernel.modules.vfs.resolve(prog) + prog, reason = kernel._G.loadfile(prog, nil, kernel._G) + if not prog then + error(reason) + end + end + local thread = kernel.modules.threading.spawn(prog, 0, name, isThread, _, ...) + thread.io_output = kernel.modules.threading.currentThread.io_output + thread.io_input = kernel.modules.threading.currentThread.io_input + return thread.pid +end + +function kernel.userspace.os.spawnp(prog, stdin, stdout, stderr, ...) + local isThread = type(prog) == "function" + local name = isThread and kernel.modules.threading.currentThread.name or "unknown" + if type(prog) == "string" then + name = kernel.modules.vfs.resolve(prog) + prog, reason = kernel._G.loadfile(prog, nil, kernel._G) + if not prog then + error(reason) + end + end + local thread = kernel.modules.threading.spawn(prog, 0, name, isThread, _, ...) + thread.env["_"] = name + thread.io_output = stdout --todo: check types! + thread.io_error = stderr --todo: check types! + thread.io_input = stdin + return thread.pid +end + +function kernel.userspace.os.kill(pid, signal) + return kernel.modules.threadUtil.userKill(pid, signal or "terminate") +end + +function kernel.userspace.os.exit() + kernel.modules.threading.kill(kernel.modules.threading.currentThread.pid) +end + +function kernel.userspace.os.sleep(time) + coroutine.yield("yield", computer.uptime() + (time or 0)) +end + +function kernel.userspace.os.getenv(name) + return kernel.modules.threading.currentThread.env[name] +end + +function kernel.userspace.os.setenv(name, value) + kernel.modules.threading.currentThread.env[name] = value +end + +function kernel.userspace.dofile(filename, env) + local program, reason = kernel.userspace.loadfile(filename, nil, env or kernel._G) + if not program then + return error(reason, 0) + end + return program() +end + +function kernel.userspace.loadfile(filename, mode, env) + local file, reason = kernel.modules.io.io.open(filename) + if not file then + return nil, reason + end + local source, reason = file:read("*a") + file:close() + if not source then + return nil, reason + end + if string.sub(source, 1, 1) == "#" then + local endline = string.find(source, "\n", 2, true) + if endline then + source = string.sub(source, endline + 1) + else + source = "" + end + end + return load(source, "=" .. filename, mode, env or kernel._G) +end + +function kernel.userspace.load(ld, source, mode, env) + return load(ld, source, mode, env or kernel._G) +end + +function kernel.userspace.print(...) + local args = table.pack(...) + kernel.modules.io.io.stdout:setvbuf("line") + for i = 1, args.n do + local arg = tostring(args[i]) + if i > 1 then + arg = "\t" .. arg + end + kernel.modules.io.io.stdout:write(arg) + end + kernel.modules.io.io.stdout:write("\n") + kernel.modules.io.io.stdout:setvbuf("no") + kernel.modules.io.io.stdout:flush() +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_buffer.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_buffer.lua new file mode 100644 index 0000000000..24e0792352 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_buffer.lua @@ -0,0 +1,498 @@ +local computer = computer +local unicode = unicode + +local buffer = {} + +function buffer.new(mode, stream) + local result = { + mode = {}, + stream = stream, + bufferRead = "", + bufferWrite = "", + bufferSize = math.max(512, math.min(8 * 1024, computer.freeMemory() / 8)), + bufferMode = "full", + readTimeout = math.huge + } + mode = mode or "r" + for i = 1, unicode.len(mode) do + result.mode[unicode.sub(mode, i, i)] = true + end + local metatable = { + __index = buffer, + __metatable = "file" + } + return setmetatable(result, metatable) +end + + +local function badFileDescriptor() + return nil, "bad file descriptor" +end + +function buffer.pipe() + local inStream = {} + local outStream = {} + + local isOpen = true + + local buf = "" + + function inStream:close() + buf = nil + return true + end + + function outStream:close() + isOpen = false + return true + end + + + function outStream:write(str) + local notify = #buf == 0 + buf = buf .. str + if notify then + local sig = {"pipe", inStream} + kernel.modules.threading.eachThread(function(thread) + if thread.currentHandler == "pipe" then + thread.eventQueue[#thread.eventQueue + 1] = sig + end + end) + end + return self + end + + function inStream:read(n, dobreak) + if #buf == 0 then + while isOpen and coroutine.yield("pipe") ~= inStream and #buf < 1 do end + if #buf == 0 and not isOpen then + buf = nil + end + end + --kernel.io.println("Pipe insert: "..tostring(buf)) + local result = buf + buf = buf and "" + return result + end + + inStream.seek = badFileDescriptor + inStream.write = badFileDescriptor + outStream.read = badFileDescriptor + outStream.seek = badFileDescriptor + + local _in = buffer.new("r", inStream) + local out = buffer.new("w", outStream) + + _in.remaining = function() + return buf and #buf or -1 + end + + out:setvbuf("no") + + return _in, out +end + +function buffer:close() + if self.mode.w or self.mode.a then + self:flush() + end + self.closed = true + return self.stream:close() +end + +function buffer:flush() + local result, reason = self.stream:write(self.bufferWrite) + if result then + self.bufferWrite = "" + else + if reason then + return nil, reason + else + return nil, "bad file descriptor" + end + end + + return self +end + +function buffer:lines(...) + local args = table.pack(...) + return function() + local result = table.pack(self:read(table.unpack(args, 1, args.n))) + if not result[1] and result[2] then + error(result[2]) + end + return table.unpack(result, 1, result.n) + end +end + +function buffer:read(...) + local timeout = computer.uptime() + self.readTimeout + + local function readChunk() + if computer.uptime() > timeout then + error("timeout") + end + local result, reason = self.stream:read(self.bufferSize) + if result then + self.bufferRead = self.bufferRead .. result + return self + else -- error or eof + return nil, reason + end + end + + local function readBytesOrChars(n) + n = math.max(n, 0) + local len, sub + if self.mode.b then + len = rawlen + sub = string.sub + else + len = unicode.len + sub = unicode.sub + end + local buffer = "" + repeat + if len(self.bufferRead) == 0 then + local result, reason = readChunk() + if not result then + if reason then + return nil, reason + else -- eof + return #buffer > 0 and buffer or nil + end + end + end + local left = n - len(buffer) + buffer = buffer .. sub(self.bufferRead, 1, left) + self.bufferRead = sub(self.bufferRead, left + 1) + until len(buffer) == n + + --kernel.io.println("buffer read: "..tostring(buffer)) + return buffer + end + + local function readNumber() + local len, sub + if self.mode.b then + len = rawlen + sub = string.sub + else + len = unicode.len + sub = unicode.sub + end + local buffer = "" + local first = true + local decimal = false + local last = false + local hex = false + local pat = "^[0-9]+" + local minbuf = 3 -- "+0x" (sign + hexadecimal tag) + -- this function is used to read trailing numbers (1e2, 0x1p2, etc) + local function readnum(checksign) + local _buffer = "" + local sign = "" + while true do + if len(self.bufferRead) == 0 then + local result, reason = readChunk() + if not result then + if reason then + return nil, reason + else -- eof + return #_buffer > 0 and (sign .. _buffer) or nil + end + end + end + if checksign then + local _sign = sub(self.bufferRead, 1, 1) + if _sign == "+" or _sign == "-" then + -- "eat" the sign (Rio Lua behaviour) + sign = sub(self.bufferRead, 1, 1) + self.bufferRead = sub(self.bufferRead, 2) + end + checksign = false + else + local x,y = string.find(self.bufferRead, pat) + if not x then + break + else + _buffer = _buffer .. sub(self.bufferRead, 1, y) + self.bufferRead = sub(self.bufferRead, y + 1) + end + end + end + return #_buffer > 0 and (sign .. _buffer) or nil + end + while true do + if len(self.bufferRead) == 0 or len(self.bufferRead) < minbuf then + local result, reason = readChunk() + if not result then + if reason then + return nil, reason + else -- eof + return #buffer > 0 and tonumber(buffer) or nil + end + end + end + -- these ifs are here so we run the buffer check above + if first then + local sign = sub(self.bufferRead, 1, 1) + if sign == "+" or sign == "-" then + -- "eat" the sign (Rio Lua behaviour) + buffer = buffer .. sub(self.bufferRead, 1, 1) + self.bufferRead = sub(self.bufferRead, 2) + end + local hextag = sub(self.bufferRead, 1, 2) + if hextag == "0x" or hextag == "0X" then + pat = "^[0-9A-Fa-f]+" + -- "eat" the 0x, see https://gist.github.com/SoniEx2/570a363d81b743353151 + buffer = buffer .. sub(self.bufferRead, 1, 2) + self.bufferRead = sub(self.bufferRead, 3) + hex = true + end + minbuf = 0 + first = false + elseif decimal then + local sep = sub(self.bufferRead, 1, 1) + if sep == "." then + buffer = buffer .. sep + self.bufferRead = sub(self.bufferRead, 2) + local temp = readnum(false) -- no sign + if temp then + buffer = buffer .. temp + end + end + if not tonumber(buffer) then break end + decimal = false + last = true + minbuf = 1 + elseif last then + local tag = sub(self.bufferRead, 1, 1) + if hex and (tag == "p" or tag == "P") then + local temp = sub(self.bufferRead, 1, 1) + self.bufferRead = sub(self.bufferRead, 2) + local temp2 = readnum(true) -- this eats the next sign if any + if temp2 then + buffer = buffer .. temp .. temp2 + end + elseif tag == "e" or tag == "E" then + local temp = sub(self.bufferRead, 1, 1) + self.bufferRead = sub(self.bufferRead, 2) + local temp2 = readnum(true) -- this eats the next sign if any + if temp2 then + buffer = buffer .. temp .. temp2 + end + end + break + else + local x,y = string.find(self.bufferRead, pat) + if not x then + minbuf = 1 + decimal = true + else + buffer = buffer .. sub(self.bufferRead, 1, y) + self.bufferRead = sub(self.bufferRead, y + 1) + end + end + end + return tonumber(buffer) + end + + local function readLine(chop) + local start = 1 + while true do + local l = self.bufferRead:find("\n", start, true) + if l then + local result = self.bufferRead:sub(1, l + (chop and -1 or 0)) + self.bufferRead = self.bufferRead:sub(l + 1) + return result + else + start = #self.bufferRead + local result, reason = readChunk() + if not result then + if reason then + return nil, reason + else -- eof + local result = #self.bufferRead > 0 and self.bufferRead or nil + self.bufferRead = "" + return result + end + end + end + end + end + + local function readAll() + repeat + local result, reason = readChunk() + if not result and reason then + return nil, reason + end + until not result -- eof + local result = self.bufferRead + self.bufferRead = "" + return result + end + + local function read(n, format) + if type(format) == "number" then + return readBytesOrChars(format) + else + if type(format) ~= "string" or unicode.sub(format, 1, 1) ~= "*" then + error("bad argument #" .. n .. " (invalid option)") + end + format = unicode.sub(format, 2, 2) + if format == "n" then + return readNumber() + elseif format == "l" then + return readLine(true) + elseif format == "L" then + return readLine(false) + elseif format == "a" then + return readAll() + else + error("bad argument #" .. n .. " (invalid format)") + end + end + end + + if self.mode.w or self.mode.a then + self:flush() + end + + local results = {} + local formats = table.pack(...) + if formats.n == 0 then + return readLine(true) + end + for i = 1, formats.n do + local result, reason = read(i, formats[i]) + if result then + results[i] = result + elseif reason then + return nil, reason + end + end + return table.unpack(results, 1, formats.n) +end + +function buffer:seek(whence, offset) + whence = tostring(whence or "cur") + assert(whence == "set" or whence == "cur" or whence == "end", + "bad argument #1 (set, cur or end expected, got " .. whence .. ")") + offset = offset or 0 + checkArg(2, offset, "number") + assert(math.floor(offset) == offset, "bad argument #2 (not an integer)") + + if self.mode.w or self.mode.a then + self:flush() + elseif whence == "cur" then + offset = offset - #self.bufferRead + end + local result, reason = self.stream:seek(whence, offset) + if result then + self.bufferRead = "" + return result + else + return nil, reason + end +end + +function buffer:setvbuf(mode, size) + mode = mode or self.bufferMode + size = size or self.bufferSize + + assert(mode == "no" or mode == "full" or mode == "line", + "bad argument #1 (no, full or line expected, got " .. tostring(mode) .. ")") + assert(mode == "no" or type(size) == "number", + "bad argument #2 (number expected, got " .. type(size) .. ")") + + self.bufferMode = mode + self.bufferSize = size + + return self.bufferMode, self.bufferSize +end + +function buffer:getTimeout() + return self.readTimeout +end + +function buffer:setTimeout(value) + self.readTimeout = tonumber(value) +end + +function buffer:write(...) + if self.closed then + return nil, "bad file descriptor" + end + local args = table.pack(...) + for i = 1, args.n do + if type(args[i]) == "number" then + args[i] = tostring(args[i]) + end + checkArg(i, args[i], "string") + end + + for i = 1, args.n do + local arg = args[i] + local result, reason + + if self.bufferMode == "full" then + if self.bufferSize - #self.bufferWrite < #arg then + result, reason = self:flush() + if not result then + return nil, reason + end + end + if #arg > self.bufferSize then + result, reason = self.stream:write(arg) + else + self.bufferWrite = self.bufferWrite .. arg + result = self + end + + elseif self.bufferMode == "line" then + local l + repeat + local idx = arg:find("\n", (l or 0) + 1, true) + if idx then + l = idx + end + until not idx + if l or #arg > self.bufferSize then + result, reason = self:flush() + if not result then + return nil, reason + end + end + if l then + result, reason = self.stream:write(arg:sub(1, l)) + if not result then + return nil, reason + end + arg = arg:sub(l + 1) + end + if #arg > self.bufferSize then + result, reason = self.stream:write(arg) + else + self.bufferWrite = self.bufferWrite .. arg + result = self + end + + else -- self.bufferMode == "no" + result, reason = self.stream:write(arg) + end + + if not result then + return nil, reason + end + end + + return self +end + +for k,v in pairs(buffer) do + _G[k] = v +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_component.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_component.lua new file mode 100644 index 0000000000..00705f27a0 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_component.lua @@ -0,0 +1,141 @@ +local component = kernel._K.component + +local adding = {} +local removing = {} +local primaries = {} + +------------------------------------------------------------------------------- + +-- This allows writing component.modem.open(123) instead of writing +-- component.getPrimary("modem").open(123), which may be nicer to read. +setmetatable(component, { + __index = function(_, key) + return component.getPrimary(key) + end, + __pairs = function(self) + local parent = false + return function(_, key) + if parent then + return next(primaries, key) + else + local k, v = next(self, key) + if not k then + parent = true + return next(primaries) + else + return k, v + end + end + end + end +}) + +function component.get(address, componentType) + checkArg(1, address, "string") + checkArg(2, componentType, "string", "nil") + for c in component.list(componentType, true) do + if c:sub(1, address:len()) == address then + return c + end + end + return nil, "no such component" +end + +function component.isAvailable(componentType) + checkArg(1, componentType, "string") + if not primaries[componentType] and not adding[componentType] then + -- This is mostly to avoid out of memory errors preventing proxy + -- creation cause confusion by trying to create the proxy again, + -- causing the oom error to be thrown again. + component.setPrimary(componentType, component.list(componentType, true)()) + end + return primaries[componentType] ~= nil +end + +function component.isPrimary(address) + local componentType = component.type(address) + if componentType then + if component.isAvailable(componentType) then + return primaries[componentType].address == address + end + end + return false +end + +function component.getPrimary(componentType) + checkArg(1, componentType, "string") + assert(component.isAvailable(componentType), + "no primary '" .. componentType .. "' available") + return primaries[componentType] +end + +function component.setPrimary(componentType, address) + checkArg(1, componentType, "string") + checkArg(2, address, "string", "nil") + if address ~= nil then + address = component.get(address, componentType) + assert(address, "no such component") + end + + local wasAvailable = primaries[componentType] + if wasAvailable and address == wasAvailable.address then + return + end + local wasAdding = adding[componentType] + if wasAdding and address == wasAdding.address then + return + end + if wasAdding then + kernel.modules.timer.remove(wasAdding.timer) + end + primaries[componentType] = nil + adding[componentType] = nil + + local primary = address and component.proxy(address) or nil + if wasAvailable then + computer.pushSignal("component_unavailable", componentType) + end + if primary then + if wasAvailable or wasAdding then + adding[componentType] = { + address=address, + timer=kernel.modules.timer.add(function() + adding[componentType] = nil + primaries[componentType] = primary + --computer.pushSignal("component_available", componentType) + end, 0.1) + } + else + primaries[componentType] = primary + computer.pushSignal("component_available", componentType) + end + end +end + +------------------------------------------------------------------------------- +function start() + + for address in component.list('screen') do + if #component.invoke(address,'getKeyboards') > 0 then + component.setPrimary('screen',address) + end + end + +end + +local function onComponentAdded(_, address, componentType) + if not (primaries[componentType] or adding[componentType]) then + component.setPrimary(componentType, address) + end +end + +local function onComponentRemoved(_, address, componentType) + if primaries[componentType] and primaries[componentType].address == address or + adding[componentType] and adding[componentType].address == address + then + component.setPrimary(componentType, component.list(componentType, true)()) + end +end + +kernel.modules.keventd.listen("component_added", onComponentAdded) +kernel.modules.keventd.listen("component_removed", onComponentRemoved) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua new file mode 100644 index 0000000000..94ae27a373 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua @@ -0,0 +1,69 @@ + +kernel.userspace.package = {} + +kernel.userspace.package.loaded = {} +kernel.userspace.package.preload = {} +kernel.userspace.package.loading = {} +kernel.userspace.package.searchers = {} + +local function preloadSearcher(module) + return kernel.userspace.package.preload[module] +end + +local function pathSearcher(module) + for dir in string.gmatch(kernel.userspace.os.getenv("LIBPATH"), "[^:$]+") do + if dir:sub(1,1) ~= "/" then + local dir = kernel.modules.vfs.concat(kernel.userspace.os.getenv("PWD") or "/", dir) + end + local file = kernel.modules.vfs.concat(dir, module .. ".lua") + if kernel.modules.vfs.exists(file) then + local loader, reason = kernel.userspace.loadfile(file, "bt", setmetatable({},{__index = kernel.userspace})) + if loader then + local state, mod = pcall(loader) + if state then + return mod + else + kernel.io.println("Module '" .. tostring(module) .. "' loading failed: " .. tostring(mod)) + end + end + end + end +end + +kernel.userspace.package.searchers[#kernel.userspace.package.searchers + 1] = preloadSearcher +kernel.userspace.package.searchers[#kernel.userspace.package.searchers + 1] = pathSearcher + +--TODO: possibly wrap result into metatable +kernel.userspace.require = function(module) + if kernel.userspace.package.loaded[module] then + return kernel.userspace.package.loaded[module] + else + if kernel.userspace.package.loading[module] then + error("Already loading "..tostring(module)) + else + kernel.userspace.package.loading[module] = true + for _, searcher in ipairs(kernel.userspace.package.searchers) do + local res, mod = pcall(searcher, module) + if res and mod then + kernel.userspace.package.loading[module] = nil + kernel.userspace.package.loaded[module] = mod + return mod + elseif not res then + kernel.io.println("Searcher for '" .. tostring(module) .. "' loading failed: " .. tostring(mod)) + end + end + kernel.userspace.package.loading[module] = nil + error("Could not load module " .. tostring(module)) + end + end +end + +function start() + kernel.userspace.package.preload.filesystem = setmetatable({}, {__index = kernel.modules.vfs}) + kernel.userspace.package.preload.buffer = setmetatable({}, {__index = kernel.modules.buffer}) + kernel.userspace.package.preload.bit32 = setmetatable({}, {__index = kernel.userspace.bit32}) + kernel.userspace.package.preload.component = setmetatable({}, {__index = kernel.userspace.component}) + kernel.userspace.package.preload.computer = setmetatable({}, {__index = kernel.userspace.computer}) + kernel.userspace.package.preload.io = setmetatable({}, {__index = kernel.modules.io.io}) + kernel.userspace.package.preload.unicode = setmetatable({}, {__index = kernel.userspace.unicode}) +end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_io.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_io.lua new file mode 100644 index 0000000000..99cce1709a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_io.lua @@ -0,0 +1,177 @@ +local io = {} + +------------------------------------------------------------------------------- + +function io.close(file) + return (file or io.output()):close() +end + +function io.flush() + return io.output():flush() +end + +function io.input(file) + if file then + if type(file) == "string" then + local result, reason = io.open(file) + if not result then + error(reason, 2) + end + file = result + elseif not io.type(file) then + error("bad argument #1 (string or file expected, got " .. type(file) .. ")", 2) + end + kernel.modules.threading.currentThread.io_input = file + end + return kernel.modules.threading.currentThread.io_input +end + +function io.lines(filename, ...) + if filename then + local file, reason = io.open(filename) + if not file then + error(reason, 2) + end + local args = table.pack(...) + return function() + local result = table.pack(file:read(table.unpack(args, 1, args.n))) + if not result[1] then + if result[2] then + error(result[2], 2) + else -- eof + file:close() + return nil + end + end + return table.unpack(result, 1, result.n) + end + else + return io.input():lines() + end +end + +function io.open(path, mode) + -- These requires are not on top because this is a bootstrapped file. + local stream, result = kernel.modules.vfs.open(path, mode) + if stream then + return kernel.modules.buffer.new(mode, stream) + else + return nil, result + end +end + +function io.popen(prog, mode, ...) + local name = "unknown" + if type(prog) == "string" then + name = prog + prog, reason = kernel._G.loadfile(prog, nil, kernel._G) + if not prog then + error(reason) + end + end + + if mode == "w" then + local newin, sink = kernel.modules.buffer.pipe() + local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow + + thread.io_output = kernel.modules.threading.currentThread.io_output + thread.io_error = kernel.modules.threading.currentThread.io_error + thread.io_input = newin + + sink.thread = thread.pid + return sink + elseif mode == "r" or mode == nil then + local out, newout = kernel.modules.buffer.pipe() + + local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow + thread.io_output = newout + thread.io_error = kernel.modules.threading.currentThread.io_error + thread.io_input = kernel.modules.threading.currentThread.io_input + + out.thread = thread.pid + return out + elseif mode == "rw" or mode == "wr" then + local newin, sink = kernel.modules.buffer.pipe() + local out, newout = kernel.modules.buffer.pipe() + + local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow + thread.io_output = newout + thread.io_error = kernel.modules.threading.currentThread.io_error + thread.io_input = newin + + sink.thread = thread.pid + out.thread = thread.pid + + return sink, out + elseif mode == "" then + local thread = kernel.modules.threading.spawn(prog, 0, name, _, _, ...) --TODO: child mode somehow + + thread.io_output = kernel.modules.threading.currentThread.io_output + thread.io_error = kernel.modules.threading.currentThread.io_error + thread.io_input = kernel.modules.threading.currentThread.io_input + + return thread.pid + end + return nil, "Unallowed mode" +end + +function io.pipe() + return kernel.modules.buffer.pipe() +end + +function io.output(file) + if file then + if type(file) == "string" then + local result, reason = io.open(file, "w") + if not result then + error(reason, 2) + end + file = result + elseif not io.type(file) then + error("bad argument #1 (string or file expected, got " .. type(file) .. ")", 2) + end + kernel.modules.threading.currentThread.io_output = file + end + return kernel.modules.threading.currentThread.io_output +end + +function io.read(...) + return io.input():read(...) +end + +function io.tmpfile() + local name = os.tmpname() + if name then + return io.open(name, "a") + end +end + +function io.type(object) + if type(object) == "table" then + if getmetatable(object) == "file" then + if object.stream.handle then + return "file" + else + return "closed file" + end + end + end + return nil +end + +function io.write(...) + return io.output():write(...) +end + +------------------------------------------------------------------------------- + +kernel.userspace.io = io + +setmetatable(io, {__index = function(_, k) + if k == "stdout" then return io.output() + elseif k == "stdout" then return io.output() + elseif k == "stderr" then return kernel.modules.threading.currentThread.io_error + end +end}) + +_G.io = io diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_keyboard.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_keyboard.lua new file mode 100644 index 0000000000..d07d9ed903 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_keyboard.lua @@ -0,0 +1,200 @@ +local keyboard = {pressedChars = {}, pressedCodes = {}} + +keyboard.keys = { + ["1"] = 0x02, + ["2"] = 0x03, + ["3"] = 0x04, + ["4"] = 0x05, + ["5"] = 0x06, + ["6"] = 0x07, + ["7"] = 0x08, + ["8"] = 0x09, + ["9"] = 0x0A, + ["0"] = 0x0B, + a = 0x1E, + b = 0x30, + c = 0x2E, + d = 0x20, + e = 0x12, + f = 0x21, + g = 0x22, + h = 0x23, + i = 0x17, + j = 0x24, + k = 0x25, + l = 0x26, + m = 0x32, + n = 0x31, + o = 0x18, + p = 0x19, + q = 0x10, + r = 0x13, + s = 0x1F, + t = 0x14, + u = 0x16, + v = 0x2F, + w = 0x11, + x = 0x2D, + y = 0x15, + z = 0x2C, + + apostrophe = 0x28, + at = 0x91, + back = 0x0E, -- backspace + backslash = 0x2B, + colon = 0x92, + comma = 0x33, + enter = 0x1C, + equals = 0x0D, + grave = 0x29, -- accent grave + lbracket = 0x1A, + lcontrol = 0x1D, + lmenu = 0x38, -- left Alt + lshift = 0x2A, + minus = 0x0C, + numlock = 0x45, + pause = 0xC5, + period = 0x34, + rbracket = 0x1B, + rcontrol = 0x9D, + rmenu = 0xB8, -- right Alt + rshift = 0x36, + scroll = 0x46, -- Scroll Lock + semicolon = 0x27, + slash = 0x35, -- / on main keyboard + space = 0x39, + stop = 0x95, + tab = 0x0F, + underline = 0x93, + + -- Keypad (and numpad with numlock off) + up = 0xC8, + down = 0xD0, + left = 0xCB, + right = 0xCD, + home = 0xC7, + ["end"] = 0xCF, + pageUp = 0xC9, + pageDown = 0xD1, + insert = 0xD2, + delete = 0xD3, + + -- Function keys + f1 = 0x3B, + f2 = 0x3C, + f3 = 0x3D, + f4 = 0x3E, + f5 = 0x3F, + f6 = 0x40, + f7 = 0x41, + f8 = 0x42, + f9 = 0x43, + f10 = 0x44, + f11 = 0x57, + f12 = 0x58, + f13 = 0x64, + f14 = 0x65, + f15 = 0x66, + f16 = 0x67, + f17 = 0x68, + f18 = 0x69, + f19 = 0x71, + + -- Japanese keyboards + kana = 0x70, + kanji = 0x94, + convert = 0x79, + noconvert = 0x7B, + yen = 0x7D, + circumflex = 0x90, + ax = 0x96, + + -- Numpad + numpad0 = 0x52, + numpad1 = 0x4F, + numpad2 = 0x50, + numpad3 = 0x51, + numpad4 = 0x4B, + numpad5 = 0x4C, + numpad6 = 0x4D, + numpad7 = 0x47, + numpad8 = 0x48, + numpad9 = 0x49, + numpadmul = 0x37, + numpaddiv = 0xB5, + numpadsub = 0x4A, + numpadadd = 0x4E, + numpaddecimal = 0x53, + numpadcomma = 0xB3, + numpadenter = 0x9C, + numpadequals = 0x8D, +} + +-- Create inverse mapping for name lookup. +do + local keys = {} + for k in pairs(keyboard.keys) do + table.insert(keys, k) + end + for _, k in pairs(keys) do + keyboard.keys[keyboard.keys[k]] = k + end +end + +------------------------------------------------------------------------------- + +function keyboard.isAltDown() + return (keyboard.pressedCodes[keyboard.keys.lmenu] or keyboard.pressedCodes[keyboard.keys.rmenu]) ~= nil +end + +function keyboard.isControl(char) + return type(char) == "number" and (char < 0x20 or (char >= 0x7F and char <= 0x9F)) +end + +function keyboard.isControlDown() + return (keyboard.pressedCodes[keyboard.keys.lcontrol] or keyboard.pressedCodes[keyboard.keys.rcontrol]) ~= nil +end + +function keyboard.isKeyDown(charOrCode) + checkArg(1, charOrCode, "string", "number") + if type(charOrCode) == "string" then + return keyboard.pressedChars[charOrCode] + elseif type(charOrCode) == "number" then + return keyboard.pressedCodes[charOrCode] + end +end + +function keyboard.isShiftDown() + return (keyboard.pressedCodes[keyboard.keys.lshift] or keyboard.pressedCodes[keyboard.keys.rshift]) ~= nil +end + +------------------------------------------------------------------------------- + +kernel.userspace.package.preload.keyboard = keyboard + +function start() + local function onKeyDown(_, address, char, code) + if component.isPrimary(address) then + keyboard.pressedChars[char] = true + keyboard.pressedCodes[code] = true + end + end + + local function onKeyUp(_, address, char, code) + if component.isPrimary(address) then + keyboard.pressedChars[char] = nil + keyboard.pressedCodes[code] = nil + end + end + + local function onComponentUnavailable(_, componentType) + if componentType == "keyboard" then + keyboard.pressedChars = {} + keyboard.pressedCodes = {} + end + end + + kernel.modules.keventd.listen("key_down", onKeyDown) + kernel.modules.keventd.listen("key_up", onKeyUp) + kernel.modules.keventd.listen("component_unavailable", onComponentUnavailable) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua new file mode 100644 index 0000000000..33ad4d5ff0 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_network.lua @@ -0,0 +1,381 @@ +--local network = require "network" + +local _rawSend +local isAccessible +local getNodes +--local getInterfaceInfo +local startNetwork + +local dataHandler --Layer 2 data handler + +--local accessibleHosts +--local nodes + +------------------------ +--Layer 1 + +local initated = false + +function start() + if initated then return end + initated = true + + --local filesystem = require "filesystem" + + accessibleHosts = {} + nodes = {} + + local drivers = {} + + for file in kernel.modules.vfs.list("/lib/modules/network") do + + --print("Loading driver:", file) + local ld, reason = kernel.userspace.loadfile("/lib/modules/network/"..file, nil, _G) + if not ld then + kernel.io.println("Network driver loading failed: " .. tostring(reason)) + end + drivers[file] = {driver = ld()} + + local eventHandler = {}--EVENT HANDLERS FOR DRIVER EVENTS + --eventHandler.debug = print + eventHandler.debug = function()end + + function eventHandler.newHost(node, address)--New interface in net node + --print("New host: ",node, address) + accessibleHosts[address] = {driver = drivers[file], node = node} + nodes[node].hosts[address] = address--mark host in node + end + + function eventHandler.newInterface(interface, selfAddr, linkName)--New node + --print("New interface: ",interface, selfaddr) + nodes[interface] = {hosts={}, driver = drivers[file], selfAddr = selfAddr, linkName = linkName} + end + + function eventHandler.recvData(data, node, origin) + dataHandler(data, node, origin) + end + + function eventHandler.setListener(evt, listener) + return kernel.modules.keventd.listen(evt, function(...) + local args = {...} + local res = {pcall(function()listener(table.unpack(args))end)} + if not res[1] then + kernel.io.println("ERROR IN NET EVENTHANDLER["..file.."]:"..tostring(res[2])) + end + return table.unpack(res,2) + end) + end + + drivers[file].handle = drivers[file].driver.start(eventHandler) + end + + _rawSend = function(addr, node, data) + --print("TrySend:",node,addr,":",data) + if accessibleHosts[addr] then + accessibleHosts[addr].driver.driver.send(accessibleHosts[addr].driver.handle, node, addr, data) + end + end + + isAccessible = function(addr) + if not accessibleHosts[addr] then return end + return accessibleHosts[addr].node, accessibleHosts[addr].driver + end + + getNodes = function() + return nodes + end + + getInterfaceInfo = function(interface) + if nodes[interface] then + return nodes[interface].driver.driver.info(interface) + end + end + + kernel.io.println("Link Control initated") + startNetwork() + kernel.io.println("Network initated") + --computer.pushSignal("network_ready") +end + +------------------------ +--Layer 2 + +startNetwork = function() + + local ttl = 32 + local rawSend + --local send + + local routeRequests = {} -- Table by dest addressed of tables {type = T[, data=..]}, types: D(own waiting data), R(route request for someone), E(routed data we should be able to route..) + local routes = {} --Table of pairs -> [this or route] / {thisHost=true} / {router = [addr]} + + routes[computer.address()] = {thisHost=true} + + -----Utils + local function sizeToString(size) + return string.char((size)%256) .. string.char(math.floor(size/256)%256) .. string.char(math.floor(size/65536)%256) + end + + local function readSizeStr(str, pos) + local len = str:sub(pos,pos):byte() + return str:sub(pos+1, pos+len), len+1 + end + + local toByte = string.char + -----Data out + + local function onRecv(origin, data) + --computer.pushSignal("network_message", origin, data) + --kernel.io.println("netMSG/"..origin.." "..data) + kernel.modules.libnetwork.handleData(origin, data) + end + + -----Sending + + local function sendDirectData(addr, data)--D[ttl-byte][data] + return rawSend(addr, "D"..toByte(ttl)..data) + end + + local function sendRoutedData(addr, data)--E[ttl-byte][hostlen-byte][dest host][hostlen-byte][origin host]message + local nodes = getNodes() + local msg = "E"..toByte(ttl)..toByte(addr:len())..addr..toByte(nodes[routes[addr].node].selfAddr:len())..nodes[routes[addr].node].selfAddr..data + _rawSend(routes[addr].router, routes[addr].node, msg) + end + + local function sendRoutedDataAs(addr, origin, data, ottl)--E[ttl-byte][hostlen-byte][dest host][hostlen-byte][origin host]message + local msg = "E"..toByte(ottl-1)..toByte(addr:len())..addr..toByte(origin:len())..origin..data + _rawSend(routes[addr].router, routes[addr].node, msg) + end + + local function sendRouteRequest(addr)--R[ttl-byte][Addr len][Requested addr][Route hosts-byte][ [addrLen-byte][addr] ] + local base = "R"..toByte(ttl)..toByte(addr:len())..addr..toByte(1) + local nodes = getNodes() + local sent = {} + for node, n in pairs(nodes) do + for host in pairs(n.hosts)do + if not sent[host]then + sent[host] = true + _rawSend(host, node, base..toByte(n.selfAddr:len())..n.selfAddr) + end + end + end + sent = nil + end + + local function resendRouteRequest(orig, node, host, nttl)--R[ttl-byte][Addr len][Requested addr][Route hosts-byte][ [addrLen-byte][addr] ] + local nodes = getNodes() + local hlen = orig:sub(3,3):byte() + + --local msg = "R" .. toByte(nttl) .. toByte(hlen+1) .. orig:sub(pos+4) .. toByte(nodes[node].selfAddr) .. nodes[node].selfAddr --broken, TODO repair + local msg = "R" .. toByte(nttl) .. orig:sub(3) --workaround + _rawSend(host, node, msg) + end + + local function sendHostFound(dest, addr)--H[ttl-byte][Found host] + return rawSend(dest, "H"..toByte(ttl)..addr) + end + + rawSend = function(addr, data) + local node, driver = isAccessible(addr) + if node then + _rawSend(addr, node, data) + return true + end + return false + end + + send = function(addr, data) + if type(addr) ~= "string" then error("Address must be string!!") end + if not sendDirectData(addr, data) then--Try send directly + if routes[addr] then + if routes[addr].thisHost then + onRecv("localhost", data)--it's this host, use loopback + else + sendRoutedData(addr, data)--We know route, try to send it that way + end + else + --route is unknown, we have to request it if we haven't done so already + if not routeRequests[addr] then + routeRequests[addr] = {} + routeRequests[addr][#routeRequests[addr]+1] = {type = "D", data = data} + sendRouteRequest(addr) + else + routeRequests[addr][#routeRequests[addr]+1] = {type = "D", data = data} + end + end + end + end + + local function processRouteRequests(host) + if routeRequests[host] then + for _, request in pairs(routeRequests[host]) do + if request.type == "D" then + sendRoutedData(host, request.data) + elseif request.type == "E" then + if request.ttl-1 > 1 then + sendRoutedDataAs(host, request.origin, request.data, request.ttl) + end + elseif request.type == "R" then + sendHostFound(request.host, host) + end + end + routeRequests[host] = nil + end + end + + local function checkRouteDest(dest, origin, node, data) + local nodes = getNodes() + if dest == nodes[node].selfAddr then + return true + elseif routes[dest] and routes[dest].thisHost then + return true + end + return false + end + + bindAddr = function(addr) + routes[addr] = {thisHost=true} + processRouteRequests(addr) + end + + kernel.modules.keventd.listen("hostname", function(_, name)bindAddr(name)end) + + dataHandler = function(data, node, origin) + --print("DATA:", data, node, origin) + + if data:sub(1,1) == "D" then --Direct data + onRecv(origin, data:sub(3)) + elseif data:sub(1,1) == "E" then --Routed data + local ttl = data:byte(2) + local dest, destlen = readSizeStr(data, 3) + local orig, origlen = readSizeStr(data, 3+destlen) + local dat = data:sub(3+destlen+origlen) + if checkRouteDest(dest, orig, node, dat) then + onRecv(orig, dat) + else + if routes[dest] then + if ttl-1 > 0 then + sendRoutedDataAs(dest, orig, dat, ttl) + end + else + local _node, driver = isAccessible(dest) + if _node then + routes[dest] = {router = dest, node = _node} + if ttl-1 > 0 then + sendRoutedDataAs(dest, orig, dat, ttl) + end + else + if not routeRequests[dest] then routeRequests[dest] = {} end + routeRequests[dest][#routeRequests[dest]+1] = {type = "E", origin = orig, ttl = ttl, data = dat} + sendRouteRequest(dest) + end + end + end + elseif data:sub(1,1) == "R" then --Route request + local dest, l = readSizeStr(data, 3) + if not routeRequests[dest] then + + --check if accessible interface + local nodes = getNodes() + for _node, n in pairs(nodes) do + if _node ~= node then --requested host won't ever be in same node + for host in pairs(n.hosts)do + if host == dest then + --Found it! + sendHostFound(origin, dest) + return + end + end + end + end + + --check if route known + if routes[dest] then + if routes[dest].thisHost then + --sendHostFound(origin, nodes[node].selfAddr) + sendHostFound(origin, dest) + elseif routes[dest].router ~= origin then--Routen might have rebooted and is asking about route + --sendHostFound(origin, routes[dest].router) + sendHostFound(origin, dest) + end + return + end + + routeRequests[dest] = {} + routeRequests[dest][#routeRequests[dest]+1] = {type = "R", host = origin} + + local nttl = data:byte(2)-1 + if nttl > 1 then + local sent = {} + --Bcast request + for _node, n in pairs(nodes) do + if _node ~= node then --We mustn't send it to origin node + for host in pairs(n.hosts)do + if not sent[host] then + sent[host] = true + resendRouteRequest(data, _node, host, nttl) + end + end + end + end + end + sent = nil + else + --we've already requested this addr so if we get the route + --we'll respond + routeRequests[dest][#routeRequests[dest]+1] = {type = "R", host = origin} + end + elseif data:sub(1,1) == "H" then --Host found + local nttl = data:byte(2)-1 + local host = data:sub(3) + + if not routes[host] then + routes[host] = {router = origin, node = node} + processRouteRequests(host) + end + end + end + + --network.core.setCallback("send", send) + --network.core.setCallback("bind", bindAddr) + + --------------- + --Network stats&info + + function getInfo() + local res = {} + + res.interfaces = {} + for k, node in pairs(getNodes())do + res.interfaces[k] = {selfAddr = node.selfAddr, linkName = node.linkName} + end + return res + end + + function getRoutingTable() + local res = {} + + for k,v in pairs (routes) do + if v.router then + res[k] = {router = v.router, interface = v.node} + end + end + return res + end + + function getArpTable(interface) + local res = {} + for k in pairs(nodes[interface].hosts)do + table.insert(res, k) + end + return res + end + + --network.core.setCallback("netstat", getInfo) + --network.core.setCallback("intstat", getInterfaceInfo) + --network.core.setCallback("routetab", getRoutingTable) + --network.core.setCallback("arptab", getArpTable) + + --network.core.lockCore() +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua new file mode 100644 index 0000000000..faaf7efe9a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_tape.lua @@ -0,0 +1,65 @@ +tapes = {} + +local function buildDevfs() + for file in pairs(kernel.modules.devfs.data) do + if file:match("^tape") then + kernel.modules.devfs.data[file] = nil + end + end + for k, tape in ipairs(tapes) do + kernel.modules.devfs.data["tape" .. k] = { + __type = "f", + open = function(hnd) + if not component.invoke(tape, "isReady") then + error("Tape drive is not ready") + end + component.invoke(tape, "seek", -math.huge) + hnd.tape = tape + end, + size = function() + return component.invoke(tape, "getSize") + end, + write = function(h, data) + component.invoke(tape, "write", data) + return not component.invoke(tape, "isEnd", data) + --TODO: do this correctly + end, + read = function(h, len) + if component.invoke(tape, "isEnd", data) then + return + end + return component.invoke(tape, "read", len) + end + } + end +end + +local function onComponentAdded(_, address, componentType) + if componentType == "tape_drive" then + tapes[#tapes + 1] = address + buildDevfs() + end +end + +local function onComponentRemoved(_, address, componentType) + if componentType == "tape_drive" then + local t + for i, tape in ipairs(tapes) do + if tape == address then + t = i + break + end + end + table.remove(tapes, t) + buildDevfs() + end +end + +function start() + for tape, t in component.list("tape_drive") do + onComponentAdded(_, tape, t) + end +end + +kernel.modules.keventd.listen("component_added", onComponentAdded) +kernel.modules.keventd.listen("component_removed", onComponentRemoved) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_pty.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_pty.lua new file mode 100644 index 0000000000..6e65c71d24 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_pty.lua @@ -0,0 +1,47 @@ +allocator, list = kernel.modules.util.getAllocator() + +function new() + local pty = allocator:get() + pty.mi, pty.so = kernel.modules.buffer.pipe() + pty.si, pty.mo = kernel.modules.buffer.pipe() + + function pty:read(...) + return self.si:read(...) + end + + function pty:write(...) + return self.so:write(...) + end + + return pty.id, pty.mi, pty.mo, pty.si, pty.so +end + +function nextPty(at) + local pty = at and tonumber(at) + 1 or 1 + if not list[pty] then + return + elseif list[pty].next then + return nextPty(pty) + else + return pty, list[pty] + end +end + +function start() + setmetatable(kernel.modules.devfs.data.pts, { + __newindex = function()error("Access denied")end, + __index = function(_,k) + if list[tonumber(k)] and list[tonumber(k)].id then + return list[tonumber(k)] + end + end, + __pairs = function() + local lastIndex = nil + return function() + local k, v = nextPty(lastIndex) + lastIndex = k + return k and tostring(k), k and tostring(v) + end + end + }) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua new file mode 100644 index 0000000000..1b81f17447 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua @@ -0,0 +1,31 @@ +kernel.userspace.package.preload.pipes = {} + +kernel.userspace.package.preload.pipes.setKernelOutput = function(sink) + kernel.io.println = function(str) + sink:setvbuf("line") + sink:write("[dmesg] ") + sink:write(str) + sink:write("\n") + sink:setvbuf("no") + sink:flush() + end +end + +function start() + kernel.userspace.package.preload.pipes.joinThread = kernel.modules.threadUtil.joinThread + kernel.userspace.package.preload.pipes.getThreadInfo = kernel.modules.threadUtil.getThreadInfo + kernel.userspace.package.preload.pipes.setKillHandler = kernel.modules.threadUtil.setKillHandler + + kernel.userspace.package.preload.pipes.shouldYield = kernel.modules.threading.checkTimeout + kernel.userspace.package.preload.pipes.setTimer = kernel.modules.timer.add + kernel.userspace.package.preload.pipes.removeTimer = kernel.modules.timer.remove + kernel.userspace.package.preload.pipes.setThreadName = function(name) + kernel.modules.threading.currentThread.name = name + end + kernel.userspace.package.preload.pipes.log = function(msg) + kernel.io.println(msg) + end + + kernel.userspace.package.preload.pipes.openPty = kernel.modules.pty.new + +end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_libnetwork.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_libnetwork.lua new file mode 100644 index 0000000000..6bedeb3881 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_libnetwork.lua @@ -0,0 +1,216 @@ +--local event = require "event" + +local driver + +network = {} +internal = {} + + +------------ +--ICMP + +network.icmp = {} +internal.icmp = {} + +local pingid = 0 +function network.icmp.ping(addr, payload) + pingid = pingid + 1 + driver.send(addr, "IP"..computer.address()..":"..tostring(pingid)..":"..payload) + return pingid +end + +function internal.icmp.handle(origin, data) + if data:sub(2,2) == "P" then + local matcher = data:sub(3):gmatch("[^:]+") + local compid = matcher() + local id = tonumber(matcher()) + local payload = matcher() + if compid == computer.address() then + computer.pushSignal("ping_reply", origin, tonumber(id), payload) + else + driver.send(origin, data) + end + end +end + +------------ +--Datagrams - UDP like protocol + +network.udp = {} +internal.udp = {ports = {}} + +function internal.udp.checkPortRange(port) + if port < 0 or port > 65535 then error("Wrong port!")end +end + +function network.udp.open(port) + internal.udp.checkPortRange(port) + internal.udp.ports[port] = true +end + +function network.udp.close(port) + internal.udp.checkPortRange(port) + internal.udp.ports[port] = nil +end + +function network.udp.send(addr, port, data) + internal.udp.checkPortRange(port) + driver.send(addr, "D".. string.char(math.floor(port/256))..string.char(port%256)..data) +end + +function internal.udp.handle(origin, data) + local port = data:byte(2)*256 + data:byte(3) + if internal.udp.ports[port] then + computer.pushSignal("datagram", origin, port, data:sub(4)) + end +end + +----------- +--TCP - TCP like protocol + +--O[port,2B][openers channel,2B] --Try open connection +--A[opened channel,2B][openers channel,2B] --Accept connection +--R[openers channel,2B] --Reject connection i.e. closed port +--C[remote channel,2B] --Close connection(user request or adta at closed/wrong channel) +--D[remote channel,2B][data] --Data + +network.tcp = {} +internal.tcp = {ports = {}, channels = {}, freeCh = 1} + +function network.tcp.listen(port) + internal.udp.checkPortRange(port) + internal.tcp.ports[port] = true +end + +function network.tcp.unlisten(port) + internal.udp.checkPortRange(port) + internal.tcp.ports[port] = nil +end + +function network.tcp.open(addr, port) + internal.udp.checkPortRange(port) + local ch = internal.tcp.freeCh + if internal.tcp.channels[ch] and internal.tcp.channels[ch].next then + internal.tcp.freeCh = internal.tcp.channels[ch].next + else + internal.tcp.freeCh = #internal.tcp.channels+2 + end + --kernel.io.println("TCP: use ch " .. ch) + internal.tcp.channels[ch] = {open = false, waiting = true, addr = addr, port = port}--mark openning + + driver.send(addr, "TO".. string.char(math.floor(port/256))..string.char(port%256).. string.char(math.floor(ch/256))..string.char(ch%256)) + return ch +end + +function network.tcp.close(channel) + if internal.tcp.channels[channel] then + if internal.tcp.channels[channel].open or internal.tcp.channels[channel].waiting then + driver.send(internal.tcp.channels[channel].addr, "TC".. string.char(math.floor(internal.tcp.channels[channel].remote/256))..string.char(internal.tcp.channels[channel].remote%256)) + end + internal.tcp.channels[channel] = {next = internal.tcp.freeCh} + internal.tcp.freeCh = channel + --computer.pushSignal("tcp_close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end +end + +function network.tcp.send(channel, data) + if internal.tcp.channels[channel] and internal.tcp.channels[channel].open then + driver.send(internal.tcp.channels[channel].addr, "TD".. string.char(math.floor(internal.tcp.channels[channel].remote/256))..string.char(internal.tcp.channels[channel].remote%256)..data) + return true + end + return false +end + +function internal.tcp.handle(origin, data) + if data:sub(2,2) == "O" then + local port = data:byte(3)*256 + data:byte(4) + local rchan = data:byte(5)*256 + data:byte(6) + + if internal.tcp.ports[port] then + local ch = internal.tcp.freeCh + if internal.tcp.channels[ch] and internal.tcp.channels[ch].next then + internal.tcp.freeCh = internal.tcp.channels[ch].next + else + internal.tcp.freeCh = #internal.tcp.channels+2 + end + --kernel.io.println("TCP: use ch " .. ch) + internal.tcp.channels[ch] = {open = true, remote = rchan, addr = origin, port = port} + driver.send(origin, "TA".. string.char(math.floor(ch/256))..string.char(ch%256) .. string.char(math.floor(rchan/256)) .. string.char(rchan%256)) + computer.pushSignal("tcp", "connection", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port, "incoming") + else + driver.send(origin, "TR".. string.char(math.floor(rchan/256))..string.char(rchan%256)) + end + elseif data:sub(2,2) == "R" then + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].waiting then + computer.pushSignal("tcp" ,"close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + internal.tcp.channels[ch] = {next = internal.tcp.freeCh} + internal.tcp.freeCh = ch + end + elseif data:sub(2,2) == "A" then + local remote = data:byte(3)*256 + data:byte(4) + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].waiting then + internal.tcp.channels[ch].waiting = nil + internal.tcp.channels[ch].open = true + internal.tcp.channels[ch].remote = remote + computer.pushSignal("tcp", "connection", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end + elseif data:sub(2,2) == "C" then + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].open then + internal.tcp.channels[ch] = {next = internal.tcp.freeCh} + internal.tcp.freeCh = ch + computer.pushSignal("tcp" ,"close", ch, internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end + elseif data:sub(2,2) == "D" then --TODO: check source + local ch = data:byte(3)*256 + data:byte(4) + if internal.tcp.channels[ch] and internal.tcp.channels[ch].open then + computer.pushSignal("tcp", "message", ch, data:sub(5), internal.tcp.channels[ch].addr, internal.tcp.channels[ch].port) + end + end +end + +----------- +--IP + +network.ip = {} + +function network.ip.bind(addr) + driver.bind(addr) +end + +------------ +--Data processing + +function handleData(origin, data) + if data:sub(1,1) == "I" then internal.icmp.handle(origin, data) + elseif data:sub(1,1) == "T" then internal.tcp.handle(origin, data) + elseif data:sub(1,1) == "D" then internal.udp.handle(origin, data) end + +end + + +------------ +--Info + +network.info = {} +network.info.getInfo = function(...)return driver.netstat(...) end +network.info.getInterfaceInfo = function(...)return driver.intstat(...) end +network.info.getRoutes = function(...)return driver.routetab(...) end +network.info.getArpTable = function(...)return driver.arptab(...) end + +------------ + +kernel.userspace.package.preload.network = network + +function start() + driver = { + send = kernel.modules.network.send, + bind = kernel.modules.network.bindAddr, + netstat = kernel.modules.network.getInfo, + intstat = kernel.modules.network.getInterfaceInfo, + routetab = kernel.modules.network.getRoutingTable, + arptab = kernel.modules.network.getArpTable, + } +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_manageg.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_manageg.lua new file mode 100644 index 0000000000..eccf1bcf7a --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/19_manageg.lua @@ -0,0 +1,58 @@ +local metatable = {__index = kernel._K, __newindex=function()end} + +function start() + metatable.__index = kernel.userspace + setmetatable(kernel._G, metatable) +end + +function newsandbox() + local sandbox = {} + sandbox._G = sandbox + sandbox.__STRICT = false + + local mt = {} + mt.__declared = {} + + mt.__index = function(t, n) + local res = kernel.userspace[n] + if res then + return res + end + if sandbox.__STRICT and (not mt.__declared[n]) then + error("variable '"..n.."' is not declared", 2) + end + return rawget(t, n) + end + + mt.__newindex = function(t, n, v) + if sandbox.__STRICT and (not mt.__declared[n]) then + error("assign to undeclared variable '"..n.."'", 2) + end + rawset(t, n, v) + end + + return setmetatable(sandbox, mt) +end + +--User function. Defines globals for strict mode. +function global(sandbox, ...) + for _, v in ipairs{...} do getmetatable(mt).__declared[v] = true end +end + + +local protectionStack = {{newindex = function()end, index = kernel.userspace}} + +function protect(sandbox) + table.insert(protectionStack, { + newindex = metatable.__newindex, + index = metatable.__index + }) + metatable.__newindex = sandbox + metatable.__index = sandbox +end + +function unprotect() + local prot = table.remove(protectionStack, protectionStack.n) + metatable.__newindex = prot.newindex + metatable.__index = prot.index +end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua new file mode 100644 index 0000000000..3a30a1de00 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/20_threading.lua @@ -0,0 +1,204 @@ +threads = {} +currentThread = nil + +local threadMode = { + top = 0, + child = 1 +} + +local function getPendingThreads() + local res = {} + for _, thread in ipairs(threads) do + if thread.coro and #thread.eventQueue > 0 then + res[#res + 1] = thread + end + end + return res +end + +local function getResumableThreads(threads_) + local res = {} + for _,thread in ipairs(threads_) do + for n,event in ipairs(thread.eventQueue) do + if event[1] == thread.currentHandler then + table.remove(thread.eventQueue, n) + thread.currentEvent = event + res[#res + 1] = thread + break + end + end + end + return res +end + +local firstFree = 1 +local nextUid = 1 + +function kill(pid) + threads[pid] = {next = firstFree} + firstFree = pid + eachThread(function(thread) + if thread.coro then + if thread.currentHandler == "kill" then + thread.eventQueue[#thread.eventQueue + 1] = {"kill", pid} + end + thread.eventQueue[#thread.eventQueue + 1] = sig + end + end) + --TODO: remove thread timers +end + +function spawn(exec, child, name, isthread, _, ...) + local thread + thread = { + child = child, + coro = coroutine.create(function(...) + local arg = {...} + local r = {xpcall(function() + exec(table.unpack(arg)) + end, function(e) + pcall(kernel.io.println, "ERROR IN THREAD " .. thread.pid .. ": " .. tostring(thread.name)) + pcall(kernel.io.println, e) + pcall(kernel.io.println, debug.traceback()) + end)} + return table.unpack(r, 2) + end), + sandbox = isthread and currentThread.sandbox or kernel.modules.manageg.newsandbox(), + currentHandler = "arg", + currentHandlerArg = nil, + eventQueue = {{"arg", ...}}, + name = name or "unnamed", + uid = nextUid + } + + nextUid = nextUid + 1 + + thread.env = currentThread and (isthread and currentThread.env or setmetatable({},{__index=currentThread.env})) or {} + + local dest = firstFree + if threads[firstFree] then + firstFree = threads[firstFree].next + else + firstFree = firstFree + 1 + end + thread.pid = dest + threads[dest] = thread + + thread.kill = { + kill = kill, + terminate = kill + } + + return thread +end + +function countThreadSignals(thread, signal) + local n = 0 + local first = 0 + for i, sig in ipairs(thread.eventQueue) do + if sig[1] == signal then + n = n + 1 + if first < 1 then + first = i + end + end + end + return n, first +end + +local deadline = math.huge +local pullSignal = computer.pullSignal +computer.pullSignal = function() + pcall(kernel._println, "Attempted to use non threaded signal pulling") + pcall(kernel._println, debug.traceback()) + kernel.panic() + --error("Attempted to use non threaded signal pulling") +end + + +local function processSignals() + deadline = math.huge + for _, thread in ipairs(threads) do + if (thread.currentHandler == "yield" or thread.currentHandler == "signal") and deadline > (thread.currentHandlerArg or math.huge) then + deadline = thread.currentHandlerArg or math.huge + end + end + + local sig = {"signal", pullSignal(deadline - computer.uptime())} + + for _, thread in ipairs(threads) do + if thread.coro then + local nsig, oldest = countThreadSignals(thread, "signal") + if nsig > 32 then --TODO: make it a bit more intelligent + table.remove(thread.eventQueue, oldest) + end + if thread.currentHandler == "yield" then + if thread.currentHandlerArg <= computer.uptime() then + thread.eventQueue[#thread.eventQueue + 1] = {"yield"} + end + end + thread.eventQueue[#thread.eventQueue + 1] = sig + end + end +end + +function eachThread(func) + for _, thread in ipairs(threads) do + if thread.coro then + func(thread) + end + end +end + +local lastYield = computer.uptime() + +function checkTimeout() + local uptime = computer.uptime() + + if uptime - lastYield > 3 then + return true + end + return false +end + +function start() + while true do + local pending = getPendingThreads() + local resumable = getResumableThreads(pending) + lastYield = computer.uptime() + while #resumable > 0 do + for _, thread in ipairs(resumable) do + --kernel.io.println("Resume " .. tostring(thread.name) .. " with " + -- .. tostring(type(thread.currentEvent) == "table" and thread.currentEvent[1] or "unknown") + -- ..(thread.currentEvent[2] and (", " .. tostring(thread.currentEvent[2])) or "")) + + kernel.modules.manageg.protect(thread.sandbox) + currentThread = thread + local state, reason, arg = coroutine.resume(thread.coro, table.unpack(thread.currentEvent, 2)) + currentThread = nil + kernel.modules.manageg.unprotect() + + if not state or coroutine.status(thread.coro) == "dead" then + kill(thread.pid) + if reason then + kernel.io.println("Thread " .. tostring(thread.name) .. "(" .. tostring(thread.pid) .. ") dead: " + .. tostring(reason or "unknown/done") .. ", after " + .. tostring(type(thread.currentEvent) == "table" and thread.currentEvent[1] or "unknown")) + end + else + thread.currentEvent = nil + thread.currentHandler = reason + thread.currentHandlerArg = arg + end + end + if checkTimeout() then + break + end + pending = getPendingThreads() + resumable = getResumableThreads(pending) + end + processSignals() + end +end + + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua new file mode 100644 index 0000000000..d755e06ad8 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_threadUtil.lua @@ -0,0 +1,54 @@ +function joinThread(pid) + while true do + local dead = coroutine.yield("kill") + if pid == dead then + break + end + end +end + +function getThreadInfo() + local info = {} + kernel.modules.threading.eachThread(function(thread) + info[thread.pid] = { + pid = thread.pid, + uid = thread.uid, + name = thread.name + } + end) + return info +end + +function userKill(pid, signal, ...) + if not kernel.modules.threading.threads[pid] + or not kernel.modules.threading.threads[pid].coro then + return nil, "Thread does not exists" + end + if not kernel.modules.threading.threads[pid].kill[signal] then + return nil, "Unknown signal" + end + local args = {...} + local thread = kernel.modules.threading.threads[pid] + kernel.modules.manageg.protect(thread.sandbox) + --TODO: probably ser threading.currentThread here + local res, reason = pcall(function() + thread.kill[signal](table.unpack(args)) + end) + kernel.modules.manageg.unprotect() + if not res then + kernel.modules.threading.kill(pid) + end + return true +end + +function setKillHandler(signal, handler) + if not kernel.modules.threading.threads[pid] + or not kernel.modules.threading.threads[pid].coro then + return nil, "Thread does not exists" + end + if signal == "kill" then + return nil, "Cannot override kill" + end + kernel.modules.threading.threads[pid].kill[signal] = handler + return true +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua new file mode 100644 index 0000000000..c8590c6e79 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua @@ -0,0 +1,69 @@ +timers = {} +local nextTimer = 1 +local deadline = math.huge +thread = nil + +function add(func, time) + local timer = { + func = func, + time = time, + thread = kernel.modules.threading.currentThread, + next = computer.uptime() + time + } + + if deadline > timer.next then + deadline = timer.next + if thread.currentHandler == "yield" then + thread.currentHandlerArg = deadline + end + end + + local n = nextTimer + if timers[n] then + nextTimer = timers[n].next + else + nextTimer = nextTimer + 1 + end + + timers[n] = timer + return n +end + +function remove(tid) + --TODO: rights check + timers[tid] = {next = nextTimer} + nextTimer = tid +end + +thread = kernel.modules.threading.spawn(function() + while true do + local now = computer.uptime() + for n, timer in ipairs(timers) do + if timer.thread then + if timer.next <= now then + kernel.modules.manageg.protect(timer.thread.sandbox) + kernel.modules.threading.currentThread = timer.thread + local res, reason = pcall(timer.func) + kernel.modules.threading.currentThread = thread + kernel.modules.manageg.unprotect() + if res then + timer.next = now + timer.time + if deadline > timer.next then + deadline = timer.next + end + else + kernel.io.println("Timer " .. n .. " died: " .. tostring(reason)) + end + else + if deadline > timer.next then + deadline = timer.next + end + end + end + end + local dl = deadline + deadline = math.huge + coroutine.yield("yield", dl) + end +end, 0, "[timer]") + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua new file mode 100644 index 0000000000..b53053fc64 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua @@ -0,0 +1,10 @@ +kernel.io.println("Installing init thread") + +kernel.modules.threading.spawn(function() + kernel.io.println("Execute init") + + kernel._G.dofile("/bin/init.lua", kernel._G) + kernel.io.println("Init is dead") + kernel.panic() +end, 0, "[init]") + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/loopback.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/loopback.lua new file mode 100644 index 0000000000..254b151f41 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/loopback.lua @@ -0,0 +1,26 @@ +local driver = {} + +local pktIn,pktOut,bytesIn,bytesOut = 0,0,0,0 + +function driver.start(eventHandler) + eventHandler.newInterface("lo", "localhost", "Local Loopback") + eventHandler.newHost("lo", "localhost") + return {send = function(data)eventHandler.recvData(data, "lo", "localhost")end} +end + +function driver.send(handle, interface, destination, data) + if interface == "lo" and destination == "localhost" then + pktIn, pktOut = pktIn+1,pktOut+1 + bytesIn,bytesOut = bytesIn + data:len(), bytesOut + data:len() + handle.send(data) + end +end + +function driver.info(interface) + if interface == "lo" then + return pktIn,pktOut,bytesIn,bytesOut + end + return 0,0,0,0 +end + +return driver diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/modem.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/modem.lua new file mode 100644 index 0000000000..0ec8d7e851 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/modem.lua @@ -0,0 +1,80 @@ +--[[ +Communication on port 1! +Node protocol: +Hello/broadcast(sent by new host in node): H (modem addersses are in event) +Hi/direct(sent by hosts to new host): I (^) +Host quitting/broadcast Q (^) +Data/direct D[data] (origin from event) +]] +local driver = {} + +local nodes = {} +local eventHnd + +function driver.start(eventHandler) + eventHnd = eventHandler + local c = 0 + + eventHandler.setListener("modem_message", function(_, interface, origin, port, _, data) + if not nodes[interface] then return end --other kind of modem(possibly tunnel) + + eventHandler.debug("modemmsg["..nodes[interface].name.."]/"..origin..":"..data) + + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + + if data:sub(1,1) == "H" then + eventHandler.newHost(nodes[interface].name, origin) + component.invoke(interface, "send", origin, 1, "I") + eventHandler.debug("REPL:",interface,origin) + elseif data:sub(1,1) == "I"then + eventHandler.newHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "Q"then + eventHandler.delHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "D"then + eventHandler.recvData(data:sub(2), nodes[interface].name, origin) + end + + end) + + for int in component.list("modem", true)do + eventHandler.newInterface("eth"..tostring(c), int, "Ethernet") + + nodes["eth"..tostring(c)] = {modem = int, name = "eth"..tostring(c), pktIn = 0, pktOut = 1, bytesIn = 0, bytesOut = 1} + nodes[int] = nodes["eth"..tostring(c)] + + component.invoke(int, "open", 1) + component.invoke(int, "broadcast", 1, "H") + + eventHandler.newHost("eth"..tostring(c), int)--register loopback + c = c + 1 + end + + --eventHandler.newHost("lo", "localhost") + return {} +end + +function driver.send(handle, interface, destination, data) + if nodes[interface] then + if nodes[interface].modem == destination then + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + data:len() + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + eventHnd.recvData(data, interface, destination) + else + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + 1 + data:len() + component.invoke(nodes[interface].modem, "send", destination, 1, "D"..data) + end + end +end + +function driver.info(interface) + if nodes[interface] then + return nodes[interface].pktIn,nodes[interface].pktOut,nodes[interface].bytesIn,nodes[interface].bytesOut + end + return 0,0,0,0 +end + +return driver \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/tunnel.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/tunnel.lua new file mode 100644 index 0000000000..7cf5368b30 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/network/tunnel.lua @@ -0,0 +1,74 @@ +--For protocol info look to at modem driver + +local driver = {} + +local nodes = {} +local eventHnd + +function driver.start(eventHandler) + eventHnd = eventHandler + local c = 0 + + eventHandler.setListener("modem_message", function(_, interface, origin, port, _, data) + if not nodes[interface] then return end --other kind of modem(possibly modem) + + eventHandler.debug("modemmsg["..nodes[interface].name.."]/"..origin..":"..data) + + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + + if data:sub(1,1) == "H" then + eventHandler.newHost(nodes[interface].name, origin) + component.invoke(interface, "send", "I") + eventHandler.debug("REPL:",interface,origin) + elseif data:sub(1,1) == "I"then + eventHandler.newHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "Q"then + eventHandler.delHost(nodes[interface].name, origin) + elseif data:sub(1,1) == "D"then + eventHandler.recvData(data:sub(2), nodes[interface].name, origin) + end + + end) + + for int in component.list("tunnel", true)do + eventHandler.newInterface("tun"..tostring(c), int, "Tunnel") + + nodes["tun"..tostring(c)] = {modem = int, name = "tun"..tostring(c), pktIn = 0, pktOut = 1, bytesIn = 0, bytesOut = 1} + nodes[int] = nodes["tun"..tostring(c)] + + component.invoke(int, "send", "H") + + eventHandler.newHost("tun"..tostring(c), int)--register loopback + c = c + 1 + end + + --eventHandler.newInterface("lo", "localhost", "Local Loopback") + --eventHandler.newHost("lo", "localhost") + return {} +end + +function driver.send(handle, interface, destination, data) + if nodes[interface] then + if nodes[interface].modem == destination then + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + data:len() + nodes[interface].pktIn = nodes[interface].pktIn + 1 + nodes[interface].bytesIn = nodes[interface].bytesIn + data:len() + eventHnd.recvData(data, interface, destination) + else + nodes[interface].pktOut = nodes[interface].pktOut + 1 + nodes[interface].bytesOut = nodes[interface].bytesOut + 1 + data:len() + component.invoke(nodes[interface].modem, "send", "D"..data) + end + end +end + +function driver.info(interface) + if nodes[interface] then + return nodes[interface].pktIn,nodes[interface].pktOut,nodes[interface].bytesIn,nodes[interface].bytesOut + end + return 0,0,0,0 +end + +return driver diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/rc.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/rc.lua new file mode 100644 index 0000000000..8f103ede0b --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/rc.lua @@ -0,0 +1,124 @@ +local fs = require('filesystem') +local serialization = require('serialization') + +-- Keeps track of loaded scripts to retain local values between invocation +-- of their command callbacks. +local loaded = {} + +local rc = {} + +local function loadConfig() + local env = {} + local result, reason = loadfile('/etc/rc.cfg', 't', env) + if result then + result, reason = xpcall(result, debug.traceback) + if result then + return env + end + end + return nil, reason +end + +local function saveConfig(conf) + local file, reason = io.open('/etc/rc.cfg', 'w') + if not file then + return nil, reason + end + for key, value in pairs(conf) do + file:write(tostring(key) .. " = " .. serialization.serialize(value) .. "\n") + end + + file:close() + return true +end + +function rc.load(name, args) + if loaded[name] then + return loaded[name] + end + local fileName = fs.concat('/etc/rc.d/', name .. '.lua') + local env = setmetatable({args = args}, {__index = _G}) + local result, reason = loadfile(fileName, 't', env) + if result then + result, reason = xpcall(result, debug.traceback) + if result then + loaded[name] = env + return env + end + end + return nil, reason +end + +function rc.unload(name) + loaded[name] = nil +end + +local function rawRunCommand(conf, name, cmd, args, ...) + local result, what = rc.load(name, args) + if result then + if not cmd then + io.output():write("Commands for service " .. name .. "\n") + for command, val in pairs(result) do + if type(val) == "function" then + io.output():write(tostring(command) .. " ") + end + end + return true + elseif type(result[cmd]) == "function" then + res, what = xpcall(result[cmd], debug.traceback, ...) + if res then + return true + end + elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then + res, what = xpcall(result["stop"], debug.traceback, ...) + if res then + res, what = xpcall(result["start"], debug.traceback, ...) + if res then + return true + end + end + elseif cmd == "enable" then + conf.enabled = conf.enabled or {} + for _, _name in ipairs(conf.enabled) do + if name == _name then + return nil, "Service already enabled" + end + end + conf.enabled[#conf.enabled + 1] = name + return saveConfig(conf) + elseif cmd == "disable" then + conf.enabled = conf.enabled or {} + for n, _name in ipairs(conf.enabled) do + if name == _name then + table.remove(conf.enabled, n) + end + end + return saveConfig(conf) + else + what = "Command '" .. cmd .. "' not found in daemon '" .. name .. "'" + end + end + return nil, what +end + +function rc.runCommand(name, cmd, ...) + local conf, reason = loadConfig() + if not conf then + return nil, reason + end + return rawRunCommand(conf, name, cmd, conf[name], ...) +end + +function rc.allRunCommand(cmd, ...) + local conf, reason = loadConfig() + if not conf then + return nil, reason + end + local results = {} + for _, name in ipairs(conf.enabled or {}) do + results[name] = table.pack(rawRunCommand(conf, name, cmd, conf[name], ...)) + end + return results +end + +return rc diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/serialization.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/serialization.lua new file mode 100644 index 0000000000..b85de6a869 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/serialization.lua @@ -0,0 +1,135 @@ +local serialization = {} + +-- Important: pretty formatting will allow presenting non-serializable values +-- but may generate output that cannot be unserialized back. +function serialization.serialize(value, pretty) + local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true, + ["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true, + ["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true, + ["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true, + ["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true, + ["until"]=true, ["while"]=true} + local id = "^[%a_][%w_]*$" + local ts = {} + local function s(v, l) + local t = type(v) + if t == "nil" then + return "nil" + elseif t == "boolean" then + return v and "true" or "false" + elseif t == "number" then + if v ~= v then + return "0/0" + elseif v == math.huge then + return "math.huge" + elseif v == -math.huge then + return "-math.huge" + else + return tostring(v) + end + elseif t == "string" then + return string.format("%q", v):gsub("\\\n","\\n") + elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then + return tostring(v) + elseif t == "table" then + if ts[v] then + if pretty then + return "recursion" + else + error("tables with cycles are not supported") + end + end + ts[v] = true + local i, r = 1, nil + local f + if pretty then + local ks, sks, oks = {}, {}, {} + for k in pairs(v) do + if type(k) == "number" then + table.insert(ks, k) + elseif type(k) == "string" then + table.insert(sks, k) + else + table.insert(oks, k) + end + end + table.sort(ks) + table.sort(sks) + for _, k in ipairs(sks) do + table.insert(ks, k) + end + for _, k in ipairs(oks) do + table.insert(ks, k) + end + local n = 0 + f = table.pack(function() + n = n + 1 + local k = ks[n] + if k ~= nil then + return k, v[k] + else + return nil + end + end) + else + f = table.pack(pairs(v)) + end + for k, v in table.unpack(f) do + if r then + r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "") + else + r = "{" + end + local tk = type(k) + if tk == "number" and k == i then + i = i + 1 + r = r .. s(v, l + 1) + else + if tk == "string" and not kw[k] and string.match(k, id) then + r = r .. k + else + r = r .. "[" .. s(k, l + 1) .. "]" + end + r = r .. "=" .. s(v, l + 1) + end + end + ts[v] = nil -- allow writing same table more than once + return (r or "{") .. "}" + else + if pretty then + return tostring(t) + else + error("unsupported type: " .. t) + end + end + end + local result = s(value, 1) + local limit = type(pretty) == "number" and pretty or 10 + if pretty then + local truncate = 0 + while limit > 0 and truncate do + truncate = string.find(result, "\n", truncate + 1, true) + limit = limit - 1 + end + if truncate then + return result:sub(1, truncate) .. "..." + end + end + return result +end + +function serialization.unserialize(data) + checkArg(1, data, "string") + local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}}) + if not result then + return nil, reason + end + local ok, output = pcall(result) + if not ok then + return nil, output + end + return output +end + +return serialization + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/shell.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/shell.lua new file mode 100644 index 0000000000..c352764855 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/shell.lua @@ -0,0 +1,204 @@ +local fs = require("filesystem") +local text = require("text") +local unicode = require("unicode") + +local shell = {} +local aliases = {} + +-- Cache loaded shells for command execution. This puts the requirement on +-- shells that they do not keep a global state, since they may be called +-- multiple times, but reduces memory usage a lot. +local shells = setmetatable({}, {__mode="v"}) + +local function getShell() + local shellPath = os.getenv("SHELL") or "/bin/rc" + local shellName, reason = shell.resolve(shellPath, "lua") + if not shellName then + return nil, "cannot resolve shell `" .. shellPath .. "': " .. reason + end + if shells[shellName] then + return shells[shellName] + end + local sh, reason = loadfile(shellName, "t", env) + if sh then + shells[shellName] = sh + end + return sh, reason +end + +local function findFile(name, ext) + checkArg(1, name, "string") + local function findIn(dir) + if dir:sub(1, 1) ~= "/" then + dir = shell.resolve(dir) + end + dir = fs.concat(fs.concat(dir, name), "..") + local name = fs.name(name) + local list = fs.list(dir) + if list then + local files = {} + for file in list do + files[file] = true + end + if ext and unicode.sub(name, -(1 + unicode.len(ext))) == "." .. ext then + -- Name already contains extension, prioritize. + if files[name] then + return true, fs.concat(dir, name) + end + elseif files[name] then + -- Check exact name. + return true, fs.concat(dir, name) + elseif ext then + -- Check name with automatially added extension. + local name = name .. "." .. ext + if files[name] then + return true, fs.concat(dir, name) + end + end + end + return false + end + if unicode.sub(name, 1, 1) == "/" then + local found, where = findIn("/") + if found then return where end + elseif unicode.sub(name, 1, 2) == "./" then + local found, where = findIn(shell.getWorkingDirectory()) + if found then return where end + else + for path in string.gmatch(shell.getPath(), "[^:]+") do + local found, where = findIn(path) + if found then return where end + end + end + return false +end + +------------------------------------------------------------------------------- + +function shell.getAlias(alias) + return aliases[alias] +end + +function shell.setAlias(alias, value) + checkArg(1, alias, "string") + checkArg(2, value, "string", "nil") + aliases[alias] = value +end + +function shell.aliases() + return pairs(aliases) +end + +function shell.resolveAlias(command, args) + checkArg(1, command, "string") + checkArg(2, args, "table", "nil") + args = args or {} + local program, lastProgram = command, nil + while true do + local tokens = text.tokenize(shell.getAlias(program) or program) + program = tokens[1] + if program == lastProgram then + break + end + lastProgram = program + for i = #tokens, 2, -1 do + table.insert(args, 1, tokens[i]) + end + end + return program, args +end + +function shell.getWorkingDirectory() + return os.getenv("PWD") +end + +function shell.setWorkingDirectory(dir) + checkArg(1, dir, "string") + dir = fs.canonical(dir) .. "/" + if dir == "//" then dir = "/" end + if fs.isDirectory(dir) then + os.setenv("PWD", dir) + return true + else + return nil, "not a directory" + end +end + +function shell.getPath() + return os.getenv("PATH") +end + +function shell.setPath(value) + os.setenv("PATH", value) +end + +function shell.resolve(path, ext) + if ext then + checkArg(2, ext, "string") + local where = findFile(path, ext) + if where then + return where + else + return nil, "file not found" + end + else + if unicode.sub(path, 1, 1) == "/" then + return fs.canonical(path) + elseif unicode.sub(path, 1, 2) == "~/" then + return fs.concat(os.getenv("HOME"), path:sub(2)) + else + return fs.concat(shell.getWorkingDirectory(), path) + end + end +end + +function shell.execute(command, env, ...) + local sh, reason = getShell() + if not sh then + return false, reason + end + local result = table.pack(pcall(sh, env, command, ...)) + if not result[1] and type(result[2]) == "table" and result[2].reason == "terminated" then + if result[2].code then + return true + else + return false, "terminated" + end + end + return table.unpack(result, 1, result.n) +end + +function shell.parse(...) + local params = table.pack(...) + local args = {} + local options = {} + local doneWithOptions = false + for i = 1, params.n do + local param = params[i] + if not doneWithOptions and type(param) == "string" then + if param == "--" then + doneWithOptions = true -- stop processing options at `--` + elseif unicode.sub(param, 1, 2) == "--" then + if param:match("%-%-(.-)=") ~= nil then + options[param:match("%-%-(.-)=")] = param:match("=(.*)") + else + options[unicode.sub(param, 3)] = true + end + elseif unicode.sub(param, 1, 1) == "-" and param ~= "-" then + for j = 2, unicode.len(param) do + options[unicode.sub(param, j, j)] = true + end + else + table.insert(args, param) + end + else + table.insert(args, param) + end + end + return args, options +end + +------------------------------------------------------------------------------- + +return shell + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua new file mode 100644 index 0000000000..f7edb0bdcc --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua @@ -0,0 +1,85 @@ +local term = {} + +function term.clear() + io.write("\x1b[2J") +end + +function term.read(history) + history = history or {} + local x, y = 1, 1 + + local function getLine() + if not history[y] then + history[y] = "" + end + return history[y] + end + + local function setLine(text) + y = 1 + history[y] = text + end + + local function insert(char) + local pre = unicode.sub(getLine(), 1, x - 1) + local after = unicode.sub(getLine(), x) + setLine(pre .. char .. after) + x = x + 1 + io.write("\x1b[K"..char..after.."\x1b["..unicode.len(after).."D") + end + + while true do + local char = io.read(1) + if char == "\n" then + io.write("\n") + local line = getLine() + if y == 1 and line ~= "" and line ~= history[2] then + table.insert(history, 1, "") + else + history[1] = "" + end + return line + elseif char == "\t" then + elseif char == "\b" and x > 1 then + local pre = unicode.sub(getLine(), 1, x - 2) + local after = unicode.sub(getLine(), x) + setLine(pre .. after) + x = x - 1 + io.write("\x1bD\x1b[K" .. after .. "\x1b[" .. unicode.len(after) .. "D") + elseif char == "\x1b" then + local mode = io.read(1) + if mode == "[" then + local act = io.read(1) + if act == "C" then + if unicode.len(getLine()) >= x then + io.write("\x1b[C") + x = x + 1 + end + elseif act == "D" then + if x > 1 then + io.write("\x1b[D") + x = x - 1 + end + elseif act == "A" then + y = y + 1 + local line = getLine() + --x = math.min(unicode.len(line) + 1, x) + io.write("\x1b[" .. (x - 1) .. "D\x1b[K" .. line) + x = unicode.len(line) + 1 + elseif act == "B" then + if y > 1 then + y = y - 1 + local line = getLine() + --x = math.min(unicode.len(line) + 1, x) + io.write("\x1b[" .. (x - 1) .. "D\x1b[K" .. line) + x = unicode.len(line) + 1 + end + end + end + elseif char:match("[%g%s]") then + insert(char) + end + end +end + +return term diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/text.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/text.lua new file mode 100644 index 0000000000..7a8882a8bd --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/text.lua @@ -0,0 +1,113 @@ +local unicode = require("unicode") + +local text = {} + +function text.detab(value, tabWidth) + checkArg(1, value, "string") + checkArg(2, tabWidth, "number", "nil") + tabWidth = tabWidth or 8 + local function rep(match) + local spaces = tabWidth - match:len() % tabWidth + return match .. string.rep(" ", spaces) + end + local result = value:gsub("([^\n]-)\t", rep) -- truncate results + return result +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.padLeft(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 string.rep(" ", length - unicode.wlen(value)) .. value + end +end + +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 + +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, nl + return function() + if value then + line, value, nl = text.wrap(value, width, maxWidth) + return line + end + end +end + +------------------------------------------------------------------------------- + +function text.tokenize(value) + checkArg(1, value, "string") + local tokens, token = {}, "" + local escaped, quoted, start = false, false, -1 + for i = 1, unicode.len(value) do + local char = unicode.sub(value, i, i) + if escaped then -- escaped character + escaped = false + token = token .. char + elseif char == "\\" and quoted ~= "'" then -- escape character? + escaped = true + token = token .. char + elseif char == quoted then -- end of quoted string + quoted = false + token = token .. char + elseif (char == "'" or char == '"') and not quoted then + quoted = char + start = i + token = token .. char + elseif string.find(char, "%s") and not quoted then -- delimiter + if token ~= "" then + table.insert(tokens, token) + token = "" + end + else -- normal char + token = token .. char + end + end + if quoted then + return nil, "unclosed quote at index " .. start + end + if token ~= "" then + table.insert(tokens, token) + end + return tokens +end + +------------------------------------------------------------------------------- + +return text + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua new file mode 100644 index 0000000000..b572e92acc --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua @@ -0,0 +1,607 @@ +local argv = {...} +local options +local loglevel = 1 + +local ocData = { + baseDir = "/var/lib/mpt/mpt.db", + configDir = "/var/lib/mpt/config.db", + dir = "/var/lib/mpt/" +} + +local function split(text,splitter) + local rt = {} + local act = "" + local x = 1 + while x <= #text do + if text:sub(x,x+#splitter-1) == splitter then + if act ~= "" then + rt[#rt+1] = act + end + x = x + #splitter + act="" + else + act = act .. text:sub(x,x) + x = x + 1 + end + end + if act ~= "" then + rt[#rt+1] = act + end + return rt; +end + +local function slice(a,i,j) local b = {} for x = i,j do b[x-i+1] = a[x] end return b end + +local base, config, backend +local core +local frontends + +local ocBackend +ocBackend = { + + --MAIN FLOW + init = function() + ocData.io = require("io") + ocData.fs = require("filesystem") + ocData.serialization = require("serialization") + ocData.component = require("component") + ocData.filesystem = require("filesystem") + ocData.internet = require("internet") + ocData.term = require("term") + ocData.shell = require("shell") + ocData.wget = loadfile("/bin/wget.lua") + local a + a, options = ocData.shell.parse(table.unpack(argv)) + if options.root then + core.rootDir = options.root + core.log(1, "OC ","Using custom root directory: "..core.rootDir) + end + if not ocData.fs.exists(core.rootDir..ocData.dir) then ocData.fs.makeDirectory(core.rootDir..ocData.dir) end + end, + run = function() + if #argv < 1 then + error("Missing options, see 'mpt -h'") + end + local args, options = ocData.shell.parse(table.unpack(argv)) + + if options.h or options.help then + print("Usage: mpt [-hRSUuy] [packages]") + print(" -S, --sync") + print(" Synchronize packages. Packages are installed directly") + print(" from the remote repositories, including all dependencies") + print(" required to run the packages. For example, pacman -S qt") + print(" will download and install qt and all the packages it depends on.") + print(" -R, --remove") + print(" Remove package(s) from the system.") + --print(" -U, --upgrade") + --print(" Upgrade or add package(s) to the system and install the required") + --print(" dependencies from sync repositories. Either a URL or file path can be") + --print(" specified. This is a ?remove-then-add? process.") + print(" -u, --upgrades") + print(" Upgrade all packages that are out-of-date on the") + print(" local system. Only package versions are used to find outdated packages;") + print(" replacements are not checked here. This is a ?remove-then-add? process.") + print(" --root='/some/dir'") + print(" Set alternative root directory") + print(" -v") + print(" More output") + print(" -y") + print(" Don't ask any questions, answer automatically") + return + end + + if options.v then loglevel = 0 end + + if options.S or options.sync then + for _, pack in ipairs(args) do + core.install(pack) + end + end + + if options.R or options.remove then + if options.S or options.sync then + error("Illegal parameters!") + end + for _, pack in ipairs(args) do + core.remove(pack) + end + end + + if options.u or options.upgrades then + core.upgrade() + end + + if options.y then + ocBackend.prompt = function()return true end + end + core.doWork() + end, + + ----FILESYSTEM + concat = function(...)return ocData.fs.concat(...)end, + + readConfig = function() + if ocData.fs.exists(core.rootDir..ocData.configDir) then + local f = ocData.io.open(core.rootDir..ocData.configDir, "r") + local conf = ocData.serialization.unserialize(f:read("*all")) + f:close() + return conf + else + core.log(1, "OC ","No config, will generate") + return core.newConfig() + end + end, + readBase = function(root) + if ocData.fs.exists((root or core.rootDir)..ocData.baseDir) then + local f = ocData.io.open((root or core.rootDir)..ocData.baseDir, "r") + local db = ocData.serialization.unserialize(f:read("*all")) + f:close() + return db + else + core.log(1, "OC", "No database, will generate") + return core.newBase() + end + end, + saveConfig = function() + local f = ocData.io.open(core.rootDir..ocData.configDir, "w") + f:write(ocData.serialization.serialize(config)) + f:close() + end, + saveBase = function() + local f = ocData.io.open(core.rootDir..ocData.baseDir, "w") + f:write(ocData.serialization.serialize(base)) + f:close() + end, + + ensureParrentDirectory = function(dir) + if ocData.fs.exists(core.rootDir..dir) and not ocData.fs.isDirectory(core.rootDir..dir) then + error("Illegal location(already exists): "..core.rootDir..dir) + elseif not ocData.fs.exists(core.rootDir..dir) then + ocData.fs.makeDirectory(core.rootDir..dir) + ocData.fs.remove(core.rootDir..dir) + end + end, + + fileExists = function(file, root) + return ocData.filesystem.exists((root or core.rootDir)..file) + end, + + copyFile = function(from, to, fromRoot) + core.log(0, "OC", "COPY "..(fromRoot or core.rootDir)..from.." -> "..core.rootDir..to) + ocBackend.ensureParrentDirectory(to) + return ocData.filesystem.copy((fromRoot or core.rootDir)..from, core.rootDir..to) + end, + + removeFile = function(file) + core.log(0, "OC", "REMOVE "..core.rootDir..file) + ocData.fs.remove(core.rootDir..file) + end, + + ----NETWORK + getFile = function(url, location) + core.log(0, "OC", "Get "..url) + ocBackend.ensureParrentDirectory(location) + return ocData.wget("-q", url, core.rootDir..location) + end, + + getText = function(url, post) + core.log(0, "OC", "Get "..url) + local sContent = "" + local result, response = pcall(ocData.internet.request, url, post) + if not result then + return nil + end + pcall(function() + for chunk in response do + sContent = sContent..chunk + end + end) + return sContent + end, + + ----USER INTERACTION + log = print, + + prompt = function(message) + io.write(message) + local p = ocData.term.read(nil,nil,nil,nil,"^[Yyn]$") + if p:sub(1,1):upper() ~= "Y" then + error("User stopped") + end + end +} + +local mptFrontend +mptFrontend = { + + name = "MPT", + findPackage = function(name) + local data = backend.getText(config.frontend.mpt.api.."package/"..name) + if data then + local pack = load("return "..data)() + return true, pack.dependencies, pack + end + end, + + getFilesIntoCache = function(data) + for _, file in ipairs(data.files) do + if not backend.fileExists(config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file) then + backend.getFile(config.frontend.mpt.api.."file/"..data.name..file, config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file) + end + end + return data.files + end, + + installFiles = function(data) + for _, file in ipairs(data.files) do + backend.copyFile(config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file, file) + end + end, + + removePackage = function(package) + for _, file in ipairs(base.installed[package].data.files) do + backend.removeFile(file) + end + end, + + checkUpdate = function() + local toCheck = {} + for pack, data in pairs(base.installed) do + if data.frontend == mptFrontend.name then + toCheck[pack] = base.installed[pack].data.checksum + end + end + local updateResp = backend.getText(config.frontend.mpt.api.."update", toCheck) + if updateResp then + local updateList = load("return "..updateResp)() or {} + local res = {} + for _, entry in ipairs(updateList) do + res[entry.package] = {checksum = entry.checksum} + end + return res + end + end, + + isOffline = false +} + +local oppmFrontend = { + name = "OPPM", + findPackage = function(name)end, + checkUpdate = function() + --https://github.com/OpenPrograms/Magik6k-Programs.git/info/refs?service=git-upload-pack + --https://github.com/schacon/igithub/blob/master/http-protocol.txt + end, + action = function()end, + isOffline = false +} + + + +local mirrorFrontend +mirrorFrontend = { + name = "Mirror", + start = function() + if options.mirror then + mirrorFrontend.base = backend.readBase(options.mirror) + end + end, + findPackage = function(name) + if mirrorFrontend.base and mirrorFrontend.base.installed[name] then + return true, mirrorFrontend.base.installed[name].deps, mirrorFrontend.base.installed[name].data + end + end, + getFilesIntoCache = function(data) + for _, file in ipairs(data.files) do + if not backend.fileExists(config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file) then + --backend.getFile(config.frontend.mpt.api.."file/"..data.name..file, config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file) + backend.copyFile(file, config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file, options.mirror) + end + end + return data.files + end, + installFiles = function(data) + for _, file in ipairs(data.files) do + backend.copyFile(config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file, file) + end + end, + removePackage = function(package) + for _, file in ipairs(base.installed[package].data.files) do + backend.removeFile(file) + end + end, + checkUpdate = function()end, + action = function()end, + isOffline = true +} + +backend = ocBackend +frontends = {mptFrontend, oppmFrontend, mirrorFrontend} + +core = { + rootDir = "/", + + init = function() + backend.init() + base = backend.readBase() + config = backend.readConfig() + local usedFrontends = {} + for _, frontend in ipairs(frontends) do + if not options.offline or frontend.isOffline then + usedFrontends[#usedFrontends + 1] = frontend + if frontend.start then + frontend.start() + end + end + end + frontends = usedFrontends + end, + + finalize = function() + backend.saveBase(base) + backend.saveConfig(config) + end, + + newBase = function() + return {installed = {}} + end, + + newConfig = function() + return {cacheDir="/var/lib/mpt/cache/", database="/var/lib/mpt/base.db", + frontend={mpt={api="http://mpt.magik6k.net/api/"}}} + end, + + log = function(level, from, ...) + if level >= loglevel then -- 4:WTF, 3:ERROR, 2:NOTICE, 1:INFO, 0:DEBUG + backend.log("[ "..tostring(from)..((" "):sub(#tostring(from))), "] ", ...) + end + end, + + safeCall = pcall, + + ------------------------------------ + --- ACTUAL TASKS + data = { + install = false, + upgrade = false, + remove = false, + + --User requested packages + userInstall = {}, + + --List of requested packages as refs + installList = {}, + + --List of outdated packages + oldPackages = {}, + + upgradeList = {}, + + removeList = {} + }, + + --PACKAGE: + -- frontend -> frontend reference + --optional: deps -> string list + --optional: data -> data for frontend + + upgrade = function() + core.data.upgrade = true + core.data.install = true + end, + + --[[ + Adds package installation task queue + ]] + install = function(name) + if not base.installed[name] then + core.data.install = true + core.data.userInstall[#core.data.userInstall+1] = name + end + end, + + remove = function(name) + core.data.remove = true + if not base.installed[name] then + error("Package "..name.." is not installed!") + end + core.data.removeList[#core.data.removeList+1] = name + end, + + checkPackage = function(name) + for _, f in ipairs(frontends) do + local exists, deps, data_ = f.findPackage(name) + if exists then + return {frontend = f, deps = deps, data = data_} + end + end + end, + + --Get package indexes + getPackages = function() + for _, pack in pairs(core.data.userInstall)do + local package = core.checkPackage(pack) + if package then + if not core.data.installList[pack] then + core.data.installList[pack] = package + end + else + error("Package "..pack.." not found!") + end + end + end, + + --checks if packages are up-to-date + checkUpdate = function() + for _, f in ipairs(frontends) do + local updates = f.checkUpdate() + if updates then + for pack, data in pairs(updates) do + core.data.oldPackages[pack] = {frontend = f, data = data} + end + end + end + end, + + countDeps = function(package) + local toCheck = package.deps or {} + for _, check in pairs(toCheck) do + if not core.data.installList[check] and not base.installed[check] then + local dep = core.checkPackage(check) + if dep then + core.data.installList[check] = dep + core.countDeps(dep) + end + end + end + end, + + promptUser = function(msg) + if core.data.remove then + backend.log(">> Will remove:") + for _, packName in pairs(core.data.removeList) do + backend.log(packName) + end + end + if core.data.upgrade then + backend.log(">> Will upgrade:") + for packName in pairs(core.data.oldPackages) do + backend.log(packName) + end + end + if core.data.install then + backend.log(">> Will install:") + for packName in pairs(core.data.installList) do + backend.log(packName) + end + end + + backend.prompt(msg) + end, + + doWork = function() + if core.data.install then + core.log(1, "Install", "Checking requested packages") + core.getPackages() + core.log(1, "Install", "Checking dependencies") + for _, package in pairs(core.data.installList)do + core.countDeps(package) + end + end + + if core.data.upgrade then + core.log(1, "Upgrade", "Looking for old packages") + core.checkUpdate() + core.log(1, "Upgrade", "Getting update details") + for name, package in pairs(core.data.oldPackages)do + local exists, deps, data = package.frontend.findPackage(name) + if exists then + core.data.upgradeList[name] = {frontend = package.frontend, deps = deps, data = data} + end + end + core.log(1, "Upgrade", "Checking dependencies") + for _, package in pairs(core.data.upgradeList)do + core.countDeps(package) + end + end + + core.promptUser("Do you want to continue?[Y/n] ") + + if core.data.remove then + core.log(1, "Core", "Removing packages") + for _, package in pairs(core.data.removeList)do + core.log(0, "Core", "Remove "..package) + core.frontendForName(base.installed[package].frontend).removePackage(package) + base.installed[package] = nil + end + end + + if core.data.install or core.data.upgrade then + core.log(1, "Core", "Downloading files") + local oldloglevel = loglevel + loglevel = 0 + local filelist = {} + for _, package in pairs(core.data.installList)do + local got = package.frontend.getFilesIntoCache(package.data) + for _, file in ipairs(got)do + if not filelist[file] then + filelist[file] = true + else + error("File conflict detected!("..file..")") + end + end + end + + for _, package in pairs(core.data.upgradeList)do + package.files = package.frontend.getFilesIntoCache(package.data) + end + loglevel = oldloglevel + + if core.data.upgrade then + core.log(1, "Upgrade", "Removing outdated packages") + for name, package in pairs(core.data.upgradeList)do + core.log(0, "Core", "Remove "..name) + core.frontendForName(base.installed[name].frontend).removePackage(name) + base.installed[name] = nil + end + + for _, package in pairs(core.data.upgradeList)do + for _, file in ipairs(package.files)do + if not filelist[file] then + filelist[file] = true + else + error("File conflict detected!("..file..").. It is really bad now :(") + --...because files already got removed (: + end + end + end + end + + core.log(1, "Install", "Looking for file conflicts") + for file in pairs(filelist)do + if backend.fileExists(file) then + error("File conflict detected!("..file.."), If this package was upgraded, it's uninstalled now") + end + end + + core.log(1, "Core", "Installing packages") + + for name, package in pairs(core.data.upgradeList)do + package.frontend.installFiles(package.data) + base.installed[name] = {frontend = package.frontend.name, deps=package.deps, data=package.data} + end + + for name, package in pairs(core.data.installList)do + package.frontend.installFiles(package.data) + base.installed[name] = {frontend = package.frontend.name, deps=package.deps, data=package.data} + end + end + end, + + frontendForName = function(name) + for _, f in ipairs(frontends) do + if f.name == name then + return f + end + end + end + +} + +-------- + +core.log(1, "Main", "> Loading settings") +core.init() + +local state, reason = pcall(backend.run) +if not state then + io.stderr:write(reason .. "\n") +end + +core.log(1, "Main", "> Saving settings") +core.finalize() + + + + + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/nc.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/nc.lua new file mode 100644 index 0000000000..0cdf67a2c0 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/nc.lua @@ -0,0 +1,64 @@ +local network = require "network" +local event = require "event" + +local args = {...} + +local listen = false +local port = -1 +local addr + +for _,par in ipairs(args) do + if par == "-l" then + listen = true + elseif port < 1 then + local p = tonumber(par) + if p then + port = p + end + else + addr = par + end +end + +if port < 0 then error("Unspecified port")end +if not listen and not addr then error("Unspecified address")end + +local chanel +local function handleTcp() + while true do + while io.input().remaining() ~= 0 do + local data = io.read(math.min(io.input().remaining(), 7000)) + if not data then + + end + network.tcp.send(chanel, data) + end + local e = {event.pull()} + if e[1] then + if e[1] == "tcp" then + if e[2] == "connection" then + if listen and e[5] == port and e[6] == "incoming" then + network.tcp.unlisten(port) + print("connected") + elseif not listen and e[3] == chanel and e[6] ~= "incoming" then + chanel = e[3] + print("connected") + end + elseif e[2] == "close" and e[3] == chanel then + print("Connection closed") + return + elseif e[2] == "message" and e[3] == chanel then + io.write(e[4]) + end + end + end + end +end + +if listen then + network.tcp.listen(port) + handleTcp() +else + chanel = network.tcp.open(addr, port) + handleTcp() +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/man/pipes b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/man/pipes new file mode 100644 index 0000000000..6f6f1740fc --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/man/pipes @@ -0,0 +1,6 @@ +KERNELSPACE + kernel.modules - module array + kernel._K - raw global envirnoment, used by kernelspace and modulespace + kernel._G - current userspace + kernel.userspace - Shared userspace + \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db new file mode 100644 index 0000000000..3ecc57b304 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db @@ -0,0 +1 @@ +{database="/var/lib/mpt/base.db",cacheDir="/var/lib/mpt/cache/",frontend={mpt={api="http://mpt.magik6k.net/api/"}}} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db new file mode 100644 index 0000000000..0060a66fde --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db @@ -0,0 +1 @@ +{installed={["plan9k-fsutil"]={frontend="MPT",deps={"plan9k-corelibs"},data={files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua"},name="plan9k-fsutil",repo="plan9k",checksum="-1e0c3c93f8155321d24e4d0d1db31cb6",dependencies={"plan9k-corelibs"}}},["plan9k-network"]={frontend="MPT",deps={},data={files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},name="plan9k-network",repo="plan9k",checksum="336f43272f6b38051d71a5d640716df7",dependencies={}}},["plan9k-corelibs"]={frontend="MPT",deps={},data={files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},name="plan9k-corelibs",repo="plan9k",checksum="5efcf3df1abf690151c960690a75a768",dependencies={}}},["plan9k-drivers"]={frontend="MPT",deps={},data={files={"/lib/modules/base/17_tape.lua"},name="plan9k-drivers",repo="plan9k",checksum="6cbd5412ca13503484299daa0597f754",dependencies={}}},mpt={frontend="MPT",data={dependencies={},name="mpt",repo="mpt",checksum="37a61081c47a12904b8af615c938e6db",files={"/usr/bin/mpt.lua"}},deps={}},plan9k={frontend="MPT",deps={"plan9k-core","plan9k-network","plan9k-drivers"},data={files={},name="plan9k",repo="plan9k",checksum="-53f6ee1412ad0be211ff9aedfb202647",dependencies={"plan9k-core","plan9k-network","plan9k-drivers"}}},["plan9k-installer"]={frontend="MPT",data={dependencies={"plan9k","mpt"},name="plan9k-installer",repo="plan9k",checksum="52c8f82357c966ce3e19c97bf3942012",files={"/bin/install.lua"}},deps={"plan9k","mpt"}},["plan9k-coreutil"]={frontend="MPT",deps={"plan9k-corelibs","plan9k-fsutil"},data={files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua"},name="plan9k-coreutil",repo="plan9k",checksum="297a7f59f5d78bd619ddf8b576945510",dependencies={"plan9k-corelibs","plan9k-fsutil"}}},["plan9k-shell"]={frontend="MPT",deps={},data={files={"/bin/sh.lua"},name="plan9k-shell",repo="plan9k",checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={}}},pipes={frontend="MPT",deps={"openloader-init"},data={files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua"},name="pipes",repo="plan9k",checksum="-4f46f7cc7822ee140b1669d317e65d03",dependencies={"openloader-init"}}},["plan9k-core"]={frontend="MPT",deps={"pipes","plan9k-coreutil","plan9k-shell"},data={files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"},name="plan9k-core",repo="plan9k",checksum="-7efb889144de3f8ccde5260b8c43829",dependencies={"pipes","plan9k-coreutil","plan9k-shell"}}},["openloader-init"]={frontend="MPT",deps={},data={files={"/init.lua"},name="openloader-init",repo="disks",checksum="-263cb1448065d5c9988acfb1cede6eb7",dependencies={}}}}} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/loot.properties b/src/main/resources/assets/opencomputers/loot/loot.properties index b7f6a3b68b..22fc94ac44 100644 --- a/src/main/resources/assets/opencomputers/loot/loot.properties +++ b/src/main/resources/assets/opencomputers/loot/loot.properties @@ -8,6 +8,7 @@ Builder=build:1:dyeYellow MazeGen=maze:1:dyeOrange Network=network:1:dyeLime +Plan9k=plan9k:1:dyeRed OpenIRC=irc:1:dyeLightBlue OpenLoader=openloader:1:dyeMagenta OpenOS=openOS:0:dyeGreen From 6c808e75c164da80c40668adbd3d16be8a2177e3 Mon Sep 17 00:00:00 2001 From: Vexatos Date: Fri, 19 Jun 2015 18:32:31 +0200 Subject: [PATCH 2/6] Update init.lua --- .../resources/assets/opencomputers/loot/Plan9k/init.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua index 588b8d0e31..fa1c761834 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua @@ -83,6 +83,11 @@ local function boot(kernel) dofile(kernel.address, kernel.fpx .. kernel.file) end +local function labelText(fs) + local lbl = component.invoke(fs, "getLabel") + if lbl then return " ('"..lbl.."')" else return "" end +end + status(_OSVERSION) status("Select what to boot:") @@ -92,7 +97,7 @@ for fs in component.list("filesystem") do if component.invoke(fs, "isDirectory", "boot/kernel/")then for _,file in ipairs(component.invoke(fs, "list", "boot/kernel/")) do osList[#osList+1] = {fpx = "boot/kernel/", file = file, address = fs} - status(tostring(#osList).."."..file.." from "..(fs:sub(1,3))) + status(tostring(#osList).."."..file.." from "..(fs:sub(1,3))..labelText(fs)) end end if fs ~= computer.getBootAddress() and component.invoke(fs, "exists", "init.lua") then @@ -104,7 +109,7 @@ for fs in component.list("filesystem") do end) end osList[#osList+1] = {fpx = "", file = "init.lua", address = fs} - status(tostring(#osList).."."..osName.." from "..(fs:sub(1,3))) + status(tostring(#osList).."."..osName.." from "..(fs:sub(1,3))..labelText(fs)) end end status("Select os: ") From 358ebc6b3fae2d120d7ff86f6c619b66e065cf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 19 Jun 2015 22:57:51 +0200 Subject: [PATCH 3/6] Fixed some bugs, standard require MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Magiera --- .../opencomputers/loot/Plan9k/bin/init.lua | 4 +- .../opencomputers/loot/Plan9k/bin/lua.lua | 2 +- .../loot/Plan9k/boot/kernel/pipes | 14 ++--- .../assets/opencomputers/loot/Plan9k/init.lua | 9 +++- .../Plan9k/lib/modules/base/15_keventd.lua | 1 + .../Plan9k/lib/modules/base/16_require.lua | 51 ++++++++++++++----- .../Plan9k/lib/modules/base/18_syscall.lua | 2 +- .../loot/Plan9k/lib/modules/base/21_timer.lua | 14 +++-- .../loot/Plan9k/lib/modules/base/25_init.lua | 4 +- .../loot/Plan9k/var/lib/mpt/config.db | 2 +- .../loot/Plan9k/var/lib/mpt/mpt.db | 2 +- 11 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua index 86be5a6c8d..4735f7c969 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua @@ -3,7 +3,7 @@ local pipes = require("pipes") local component = require("component") local filesystem = require("filesystem") -os.setenv("LIBPATH", "/usr/local/lib:/usr/lib:/lib:.") +os.setenv("LIBPATH", "/lib/?.lua;/usr/lib/?.lua;/home/lib/?.lua;./?.lua;/lib/?/init.lua;/usr/lib/?/init.lua;/home/lib/?/init.lua;./?/init.lua") os.setenv("PATH", "/usr/local/bin:/usr/bin:/bin:.") os.setenv("PWD", "/") os.setenv("PS1", "\x1b[33m$PWD\x1b[31m#\x1b[39m ") @@ -89,6 +89,8 @@ for address, ctype in component.list() do computer.pushSignal("component_added", address, ctype) end +computer.pushSignal("init") + while true do local sig = {computer.pullSignal()} if sig[1] == "component_added" then diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua index ec96ba6115..fe946e194b 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/lua.lua @@ -12,7 +12,7 @@ end local hist = {} while true do - io.write("lua> ") + io.write(tostring(env._PROMPT or "lua> ")) local command = term.read(hist) local code, reason if string.sub(command, 1, 1) == "=" then diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes b/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes index 470abc3562..dbef2f5ab6 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes @@ -54,12 +54,14 @@ do function kernel._println(msg) if gpu and screen then - kernel._K.component.invoke(gpu, "set", 1, y, msg) - if y == h then - kernel._K.component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) - kernel._K.component.invoke(gpu, "fill", 1, h, w, 1, " ") - else - y = y + 1 + for line in string.gmatch(tostring(msg), "([^\n]+)") do + kernel._K.component.invoke(gpu, "set", 1, y, line) + if y == h then + kernel._K.component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) + kernel._K.component.invoke(gpu, "fill", 1, h, w, 1, " ") + else + y = y + 1 + end end end end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua index 588b8d0e31..fa1c761834 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua @@ -83,6 +83,11 @@ local function boot(kernel) dofile(kernel.address, kernel.fpx .. kernel.file) end +local function labelText(fs) + local lbl = component.invoke(fs, "getLabel") + if lbl then return " ('"..lbl.."')" else return "" end +end + status(_OSVERSION) status("Select what to boot:") @@ -92,7 +97,7 @@ for fs in component.list("filesystem") do if component.invoke(fs, "isDirectory", "boot/kernel/")then for _,file in ipairs(component.invoke(fs, "list", "boot/kernel/")) do osList[#osList+1] = {fpx = "boot/kernel/", file = file, address = fs} - status(tostring(#osList).."."..file.." from "..(fs:sub(1,3))) + status(tostring(#osList).."."..file.." from "..(fs:sub(1,3))..labelText(fs)) end end if fs ~= computer.getBootAddress() and component.invoke(fs, "exists", "init.lua") then @@ -104,7 +109,7 @@ for fs in component.list("filesystem") do end) end osList[#osList+1] = {fpx = "", file = "init.lua", address = fs} - status(tostring(#osList).."."..osName.." from "..(fs:sub(1,3))) + status(tostring(#osList).."."..osName.." from "..(fs:sub(1,3))..labelText(fs)) end end status("Select os: ") diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua index 74eb587dd1..8c9fc708e1 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_keventd.lua @@ -20,4 +20,5 @@ function start() end end end, 0, "[keventd]") + setmetatable(thread.env, {__index = kernel.modules.init.thread.env}) end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua index 94ae27a373..36a8f3c41f 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/16_require.lua @@ -10,23 +10,46 @@ local function preloadSearcher(module) return kernel.userspace.package.preload[module] end +function kernel.userspace.package.searchpath(name, path, sep, rep) + checkArg(1, name, "string") + checkArg(2, path, "string") + sep = sep or '.' + rep = rep or '/' + sep, rep = '%' .. sep, rep + name = string.gsub(name, sep, rep) + local fs = kernel.modules.vfs + local errorFiles = {} + for subPath in string.gmatch(path, "([^;]+)") do + subPath = string.gsub(subPath, "?", name) + if subPath:sub(1, 1) ~= "/" and os.getenv then + subPath = fs.concat(kernel.userspace.os.getenv("PWD") or "/", subPath) + end + if fs.exists(subPath) then + local file = kernel.modules.io.io.open(subPath, "r") + if file then + file:close() + return subPath + end + end + table.insert(errorFiles, "\tno file '" .. subPath .. "'") + end + return nil, table.concat(errorFiles, "\n") +end + local function pathSearcher(module) - for dir in string.gmatch(kernel.userspace.os.getenv("LIBPATH"), "[^:$]+") do - if dir:sub(1,1) ~= "/" then - local dir = kernel.modules.vfs.concat(kernel.userspace.os.getenv("PWD") or "/", dir) - end - local file = kernel.modules.vfs.concat(dir, module .. ".lua") - if kernel.modules.vfs.exists(file) then - local loader, reason = kernel.userspace.loadfile(file, "bt", setmetatable({},{__index = kernel.userspace})) - if loader then - local state, mod = pcall(loader) - if state then - return mod - else - kernel.io.println("Module '" .. tostring(module) .. "' loading failed: " .. tostring(mod)) - end + local filepath, reason = kernel.userspace.package.searchpath(module, kernel.userspace.os.getenv("LIBPATH")) + if filepath then + local loader, reason = kernel.userspace.loadfile(filepath, "bt", setmetatable({},{__index = kernel.userspace})) + if loader then + local state, mod = pcall(loader) + if state then + return mod + else + kernel.io.println("Module '" .. tostring(module) .. "' loading failed: " .. tostring(mod)) end end + else + return nil, reason end end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua index 1b81f17447..dbaed49143 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua @@ -4,7 +4,7 @@ kernel.userspace.package.preload.pipes.setKernelOutput = function(sink) kernel.io.println = function(str) sink:setvbuf("line") sink:write("[dmesg] ") - sink:write(str) + sink:write(tostring(str)) sink:write("\n") sink:setvbuf("no") sink:flush() diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua index c8590c6e79..eb550433df 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/21_timer.lua @@ -7,7 +7,7 @@ function add(func, time) local timer = { func = func, time = time, - thread = kernel.modules.threading.currentThread, + thread = kernel.modules.threading.currentThread or "kernel", next = computer.uptime() + time } @@ -41,11 +41,15 @@ thread = kernel.modules.threading.spawn(function() for n, timer in ipairs(timers) do if timer.thread then if timer.next <= now then - kernel.modules.manageg.protect(timer.thread.sandbox) - kernel.modules.threading.currentThread = timer.thread + if type(timer.thread) == "table" then + kernel.modules.manageg.protect(timer.thread.sandbox) + kernel.modules.threading.currentThread = timer.thread + end local res, reason = pcall(timer.func) - kernel.modules.threading.currentThread = thread - kernel.modules.manageg.unprotect() + if type(timer.thread) == "table" then + kernel.modules.threading.currentThread = thread + kernel.modules.manageg.unprotect() + end if res then timer.next = now + timer.time if deadline > timer.next then diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua index b53053fc64..aa5faa862b 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/25_init.lua @@ -1,6 +1,6 @@ kernel.io.println("Installing init thread") -kernel.modules.threading.spawn(function() +thread = kernel.modules.threading.spawn(function() kernel.io.println("Execute init") kernel._G.dofile("/bin/init.lua", kernel._G) @@ -8,3 +8,5 @@ kernel.modules.threading.spawn(function() kernel.panic() end, 0, "[init]") +--HAX +setmetatable(kernel.modules.timer.thread.env, {__index = thread.env}) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db index 3ecc57b304..92ac3946c4 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db @@ -1 +1 @@ -{database="/var/lib/mpt/base.db",cacheDir="/var/lib/mpt/cache/",frontend={mpt={api="http://mpt.magik6k.net/api/"}}} \ No newline at end of file +{cacheDir="/var/lib/mpt/cache/",database="/var/lib/mpt/base.db",frontend={mpt={api="http://mpt.magik6k.net/api/"}}} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db index 0060a66fde..7f9df9f411 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db @@ -1 +1 @@ -{installed={["plan9k-fsutil"]={frontend="MPT",deps={"plan9k-corelibs"},data={files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua"},name="plan9k-fsutil",repo="plan9k",checksum="-1e0c3c93f8155321d24e4d0d1db31cb6",dependencies={"plan9k-corelibs"}}},["plan9k-network"]={frontend="MPT",deps={},data={files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},name="plan9k-network",repo="plan9k",checksum="336f43272f6b38051d71a5d640716df7",dependencies={}}},["plan9k-corelibs"]={frontend="MPT",deps={},data={files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},name="plan9k-corelibs",repo="plan9k",checksum="5efcf3df1abf690151c960690a75a768",dependencies={}}},["plan9k-drivers"]={frontend="MPT",deps={},data={files={"/lib/modules/base/17_tape.lua"},name="plan9k-drivers",repo="plan9k",checksum="6cbd5412ca13503484299daa0597f754",dependencies={}}},mpt={frontend="MPT",data={dependencies={},name="mpt",repo="mpt",checksum="37a61081c47a12904b8af615c938e6db",files={"/usr/bin/mpt.lua"}},deps={}},plan9k={frontend="MPT",deps={"plan9k-core","plan9k-network","plan9k-drivers"},data={files={},name="plan9k",repo="plan9k",checksum="-53f6ee1412ad0be211ff9aedfb202647",dependencies={"plan9k-core","plan9k-network","plan9k-drivers"}}},["plan9k-installer"]={frontend="MPT",data={dependencies={"plan9k","mpt"},name="plan9k-installer",repo="plan9k",checksum="52c8f82357c966ce3e19c97bf3942012",files={"/bin/install.lua"}},deps={"plan9k","mpt"}},["plan9k-coreutil"]={frontend="MPT",deps={"plan9k-corelibs","plan9k-fsutil"},data={files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua"},name="plan9k-coreutil",repo="plan9k",checksum="297a7f59f5d78bd619ddf8b576945510",dependencies={"plan9k-corelibs","plan9k-fsutil"}}},["plan9k-shell"]={frontend="MPT",deps={},data={files={"/bin/sh.lua"},name="plan9k-shell",repo="plan9k",checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={}}},pipes={frontend="MPT",deps={"openloader-init"},data={files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua"},name="pipes",repo="plan9k",checksum="-4f46f7cc7822ee140b1669d317e65d03",dependencies={"openloader-init"}}},["plan9k-core"]={frontend="MPT",deps={"pipes","plan9k-coreutil","plan9k-shell"},data={files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"},name="plan9k-core",repo="plan9k",checksum="-7efb889144de3f8ccde5260b8c43829",dependencies={"pipes","plan9k-coreutil","plan9k-shell"}}},["openloader-init"]={frontend="MPT",deps={},data={files={"/init.lua"},name="openloader-init",repo="disks",checksum="-263cb1448065d5c9988acfb1cede6eb7",dependencies={}}}}} \ No newline at end of file +{installed={["plan9k-installer"]={deps={"plan9k","mpt"},data={checksum="52c8f82357c966ce3e19c97bf3942012",dependencies={"plan9k","mpt"},files={"/bin/install.lua"},name="plan9k-installer",repo="plan9k"},frontend="MPT"},["plan9k-drivers"]={deps={},data={checksum="6cbd5412ca13503484299daa0597f754",dependencies={},files={"/lib/modules/base/17_tape.lua"},name="plan9k-drivers",repo="plan9k"},frontend="MPT"},["plan9k-network"]={deps={},data={checksum="336f43272f6b38051d71a5d640716df7",dependencies={},files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},name="plan9k-network",repo="plan9k"},frontend="MPT"},mpt={deps={},data={checksum="37a61081c47a12904b8af615c938e6db",dependencies={},files={"/usr/bin/mpt.lua"},name="mpt",repo="mpt"},frontend="MPT"},["plan9k-core"]={deps={"pipes","plan9k-coreutil","plan9k-shell"},data={checksum="5e0b72825b5ae2e7bdd77addfafa055",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"},name="plan9k-core",repo="plan9k"},frontend="MPT"},plan9k={deps={"plan9k-core","plan9k-network","plan9k-drivers"},data={checksum="-53f6ee1412ad0be211ff9aedfb202647",dependencies={"plan9k-core","plan9k-network","plan9k-drivers"},files={},name="plan9k",repo="plan9k"},frontend="MPT"},["openloader-init"]={deps={},data={checksum="28a395353488d35f6071631a6328b048",dependencies={},files={"/init.lua"},name="openloader-init",repo="disks"},frontend="MPT"},pipes={deps={"openloader-init"},data={checksum="-b9cd57614f2f1b2ed2708e7596b8a62",dependencies={"openloader-init"},files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua"},name="pipes",repo="plan9k"},frontend="MPT"},["plan9k-fsutil"]={deps={"plan9k-corelibs"},data={checksum="-1e0c3c93f8155321d24e4d0d1db31cb6",dependencies={"plan9k-corelibs"},files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua"},name="plan9k-fsutil",repo="plan9k"},frontend="MPT"},["plan9k-shell"]={deps={},data={checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={},files={"/bin/sh.lua"},name="plan9k-shell",repo="plan9k"},frontend="MPT"},["plan9k-corelibs"]={deps={},data={checksum="5efcf3df1abf690151c960690a75a768",dependencies={},files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},name="plan9k-corelibs",repo="plan9k"},frontend="MPT"},["plan9k-coreutil"]={deps={"plan9k-corelibs","plan9k-fsutil"},data={checksum="-e7c7132468ae675bc57c29a93921f7b",dependencies={"plan9k-corelibs","plan9k-fsutil"},files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua"},name="plan9k-coreutil",repo="plan9k"},frontend="MPT"}}} \ No newline at end of file From daf81473b9726d403aacf59b029e85408156f841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Fri, 26 Jun 2015 17:28:17 +0200 Subject: [PATCH 4/6] Added edit, improved term, eeprom in devfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Magiera --- .../opencomputers/loot/Plan9k/bin/edit.lua | 189 ++++++++++++++++++ .../opencomputers/loot/Plan9k/bin/getty.lua | 69 ++++++- .../opencomputers/loot/Plan9k/bin/init.lua | 2 +- .../opencomputers/loot/Plan9k/bin/readkey.lua | 17 +- .../assets/opencomputers/loot/Plan9k/init.lua | 42 ++-- .../loot/Plan9k/lib/modules/base/10_devfs.lua | 6 +- .../Plan9k/lib/modules/base/17_eeprom.lua | 50 +++++ .../opencomputers/loot/Plan9k/lib/term.lua | 74 +++++++ .../opencomputers/loot/Plan9k/usr/bin/mpt.lua | 2 + .../loot/Plan9k/var/lib/mpt/config.db | 2 +- .../loot/Plan9k/var/lib/mpt/mpt.db | 2 +- 11 files changed, 426 insertions(+), 29 deletions(-) create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_eeprom.lua diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua new file mode 100644 index 0000000000..e728c8c97f --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua @@ -0,0 +1,189 @@ +local shell = require("shell") +local term = require("term") +local fs = require("filesystem") +local unicode = require("unicode") + +local args, options = shell.parse(...) + +if not args[1] then + print("Syntax: edit [file]") + return +end + +local file = args[1] + +local function read(from, to) + local started, data + while true do + local char = io.read(1) + if not char then + error("Broken pipe") + end + if not started and char == from then + started = true + data = char + elseif started then + if char == to then + return data .. char + else + data = data .. char + end + end + end +end + +--Cute, isn't it? +io.write("\x1b[999;999H\x1b6n\x1b2J\x1b[30m\x1b[47m\x1bKEdit: " .. file .. "| F1 - save&quit | F3 - just quit\n\x1b[39m\x1b[49m") +local code = read("\x1b", "R") +local h, w = code:match("\x1b%[(%d+);(%d+)R") + +local edith = h - 1 +local editw = w +local x, y = 1, 1 +local atline = 1 + +local lines = {} + +if fs.exists(file) then + for line in io.lines(file) do + lines[#lines + 1] = line + end +end + +function setcur() + io.write("\x1b[" .. (y - atline + 2) .. ";" .. (x) .. "H") +end + +local function render(startline, nlines) + --io.write("\x1b["..(startline - atline + 1)..";1H") + for n = 1, nlines do + io.write("\x1b["..(startline - atline + n + 1)..";1H\x1b[K" .. unicode.sub(lines[n + startline - 1] or "", 1, editw)) + end + setcur() +end + +render(1, edith) +setcur() + +local run = true +local baseHandler, codeHandler +local charHandler + +local code = "" +codeHandler = function(char) + if char == "[" then code = code .. char + elseif char == "0" then code = code .. char + elseif code == "[" and char == "A" then + charHandler = baseHandler + if y - 1 < 1 then return end + y = y - 1 + if unicode.len(lines[y]) < x then + x = unicode.len(lines[y]) + 1 + end + if y < atline then + atline = y + render(y, edith) + end + setcur() + elseif code == "[" and char == "B" then + charHandler = baseHandler + y = y + 1 + lines[y] = lines[y] or "" + if unicode.len(lines[y]) < x then + x = unicode.len(lines[y]) + 1 + end + if y > atline + edith - 1 then + atline = y - edith + 1 + render(y - edith + 1, edith) + end + setcur() + elseif code == "[" and char == "C" then + charHandler = baseHandler + if unicode.len(lines[y]) < x then + y = y + 1 + x = 1 + lines[y] = lines[y] or "" + if y > atline + edith - 1 then + atline = y - edith + 1 + render(y - edith + 1, edith) + end + setcur() + return + end + x = x + 1 + setcur() + elseif code == "[" and char == "D" then + charHandler = baseHandler + if x - 1 < 1 then + if y - 1 < 1 then return end + y = y - 1 + if y < atline then + atline = y + render(y, edith) + end + x = unicode.len(lines[y]) + 1 + setcur() + return + end + x = x - 1 + setcur() + elseif code == "[0" and char == "P" or char == "R" then + run = false + io.write("\x1b[2J") + if char == "P" then + local out = io.open(file, "w") + local text = "" + for _, line in ipairs(lines) do + text = text .. line .. "\n" + end + out:write(text) + out:close() + end + else + charHandler = baseHandler + end +end + +baseHandler = function(char) + if char == "\x1b" then + code = "" + charHandler = codeHandler + elseif char == "\n" then + line = lines[y] + lines[y] = unicode.sub(line or "", 1, x - 1) + table.insert(lines, y + 1, unicode.sub(line or "", x)) + x = 1 + render(y, atline + edith - y - 1) + y = y + 1 + if y > atline + edith - 1 then + atline = y - edith + 1 + render(y - edith + 1, edith) + end + setcur() + elseif char == "\b" then + if x > 1 then + lines[y] = unicode.sub(lines[y] or "", 1, x-2)..unicode.sub(lines[y] or "", x) + x = x - 1 + render(y, 1) + elseif y > 1 then + x = unicode.len(lines[y - 1]) + 1 + lines[y - 1] = lines[y - 1] .. lines[y] + table.remove(lines, y) + y = y - 1 + render(y, atline + edith - y - 1) + end + else + lines[y] = unicode.sub(lines[y] or "", 1, x-1)..char..unicode.sub(lines[y] or "", x) + render(y, 1) + x = x + 1 + setcur() + end +end + +charHandler = baseHandler + +while run do + local char = io.read(1) + charHandler(char) +end + diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua index 4d5489d8ae..f5b8dd0988 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua @@ -29,12 +29,34 @@ local function checkCoord() if y > h then y = h end end +local preblinkbg = 0x000000 +local preblinkfg = 0x000000 + local function unblink() if blinkState then blinkState = not blinkState - component.invoke(gpu, "setBackground", blinkState and 0xFFFFFF or 0x000000) - component.invoke(gpu, "set", x, y, component.invoke(gpu, "get", x, y) or " ") - component.invoke(gpu, "setBackground", 0x000000) + local char, fg, bg = component.invoke(gpu, "get", x, y) + preblinkbg = blinkState and bg or preblinkbg + preblinkfg = blinkState and fg or preblinkfg + local oribg, obpal = component.invoke(gpu, "setBackground", blinkState and 0xFFFFFF or preblinkbg) + local orifg, ofpal = component.invoke(gpu, "setForeground", blinkState and 0x000000 or preblinkfg) + component.invoke(gpu, "set", x, y, char or " ") + component.invoke(gpu, "setBackground", oribg) + component.invoke(gpu, "setForeground", orifg) + end +end + +local function reblink() + if not blinkState then + blinkState = not blinkState + local char, fg, bg = component.invoke(gpu, "get", x, y) + preblinkbg = blinkState and bg or preblinkbg + preblinkfg = blinkState and fg or preblinkfg + local oribg, obpal = component.invoke(gpu, "setBackground", blinkState and 0xFFFFFF or preblinkbg) + local orifg, ofpal = component.invoke(gpu, "setForeground", blinkState and 0x000000 or preblinkfg) + component.invoke(gpu, "set", x, y, char or " ") + component.invoke(gpu, "setBackground", oribg) + component.invoke(gpu, "setForeground", orifg) end end @@ -51,6 +73,7 @@ local function scroll() else y = y + 1 end + reblink() end local printBuf = "" @@ -77,6 +100,7 @@ local function backDelChar() x = x - 1 unblink() component.invoke(gpu, "set", x, y, " ") + reblink() end end @@ -92,6 +116,7 @@ function charHandlers.base(char) unblink() printBuffer() x = 1 + reblink() elseif char == "\t" then printBuf = printBuf .. " " elseif char == "\b" then @@ -159,11 +184,16 @@ local lcommands = {} lcommands["4"] = function()end --Reset to replacement mode +local ncommands = {} + +ncommands["6"] = function()io.write("\x1b[" .. y .. ";" .. x .. "R")end local commandMode = "" local commandBuf = "" local commandList = {} +--TODO \1b[C -- reset term to initial state + function charHandlers.control(char) if char == "\x1b" then commandList = {} @@ -174,6 +204,7 @@ function charHandlers.control(char) elseif char == "[" then if commandMode ~= "" or commandBuf ~= "" then charHandlers.active = charHandlers.base + reblink() return end commandMode = "[" @@ -181,6 +212,7 @@ function charHandlers.control(char) elseif char == "(" then if commandMode ~= "" or commandBuf ~= "" then charHandlers.active = charHandlers.base + reblink() return end commandMode = "(" @@ -213,6 +245,18 @@ function charHandlers.control(char) end lcommands[command]() end + elseif char == "n" then + commandList[#commandList + 1] = commandBuf + if not commandList[1] or commandList[1] == "" then + commandList[1] = "0" + end + for _, command in ipairs(commandList) do + if not ncommands[command] then + pipes.log("Unknown escape code: " .. tostring(command)) + break + end + ncommands[command]() + end elseif char == "d" then commandList[#commandList + 1] = commandBuf local n = tonumber(commandList[1]) or 1 @@ -225,9 +269,9 @@ function charHandlers.control(char) scrBot = nb elseif char == "H" or char == "f" then --set pos commandList[#commandList + 1] = commandBuf - local nx, ny = tonumber(commandList[1]), tonumber(commandList[2]) - x = nx or 1 - y = ny or 1 + local ny, nx = tonumber(commandList[1]), tonumber(commandList[2]) + x = math.min(nx or 1, w) + y = math.min(ny or 1, h) checkCoord() elseif char == "A" then --move up commandList[#commandList + 1] = commandBuf @@ -237,6 +281,7 @@ function charHandlers.control(char) elseif char == "B" then --move down if commandMode == "(" then charHandlers.active = charHandlers.base + reblink() return end commandList[#commandList + 1] = commandBuf @@ -274,6 +319,7 @@ function charHandlers.control(char) return end charHandlers.active = charHandlers.base + reblink() commandList = {} commandBuf = "" commandMode = "" @@ -296,9 +342,14 @@ end pipes.setTimer(function() blinkState = not blinkState - component.invoke(gpu, "setBackground", blinkState and 0xFFFFFF or 0x000000) - component.invoke(gpu, "set", x, y, component.invoke(gpu, "get", x, y) or " ") - component.invoke(gpu, "setBackground", 0x000000) + local char, fg, bg = component.invoke(gpu, "get", x, y) + preblinkbg = blinkState and bg or preblinkbg + preblinkfg = blinkState and fg or preblinkfg + local oribg, obpal = component.invoke(gpu, "setBackground", blinkState and 0xFFFFFF or preblinkbg) + local orifg, ofpal = component.invoke(gpu, "setForeground", blinkState and 0x000000 or preblinkfg) + component.invoke(gpu, "set", x, y, char or " ") + component.invoke(gpu, "setBackground", oribg) + component.invoke(gpu, "setForeground", orifg) end, 0.5) while true do diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua index 4735f7c969..e5354ddb82 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/init.lua @@ -37,7 +37,7 @@ for gpu in component.list("gpu") do print("SIGINT!!") end - os.spawnp("/bin/getty.lua", mi, nil, nil, gpu) + os.spawnp("/bin/getty.lua", mi, mo, nil, gpu) os.spawnp("/bin/readkey.lua", nil, mo, mo, screen, interruptHandler) if not sout then diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua index 1dab8eb1e6..8a2b40dfde 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/readkey.lua @@ -162,13 +162,27 @@ function on.key_down(_, source, ascii, keycode, user) keyboard.pressedCodes[keycode] = true if ascii == 13 then ascii = 10 end - if ascii ~= 0 then + if ascii ~= 0 and ascii ~= 127 then io.stdout:write(unicode.char(ascii)) else if keycode == 200 then io.stdout:write("\x1b[A") elseif keycode == 208 then io.stdout:write("\x1b[B") elseif keycode == 205 then io.stdout:write("\x1b[C") elseif keycode == 203 then io.stdout:write("\x1b[D") + + elseif keycode == keyboard.keys.f1 then io.stdout:write("\x1b[0P") + elseif keycode == keyboard.keys.f2 then io.stdout:write("\x1b[0Q") + elseif keycode == keyboard.keys.f3 then io.stdout:write("\x1b[0R") + elseif keycode == keyboard.keys.f4 then io.stdout:write("\x1b[0S") + + elseif keycode == keyboard.keys.delete then io.stdout:write("\x1b[3~") + elseif keycode == keyboard.keys.insert then io.stdout:write("\x1b[2~") + elseif keycode == keyboard.keys.pageUp then io.stdout:write("\x1b[5~") + elseif keycode == keyboard.keys.pageDown then io.stdout:write("\x1b[6~") + elseif keycode == keyboard.keys.home then io.stdout:write("\x1b0H") + elseif keycode == keyboard.keys["end"] then io.stdout:write("\x1b0F") + elseif keycode == keyboard.keys.tab then io.stdout:write("\t") + --TODO: rest fX keys end end end @@ -180,6 +194,7 @@ function on.key_up(_, source, ascii, keycode, user) end function on.clipboard(_, source, data, user) + if not keyboards[source] then return end io.stdout:write(data) end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua index fa1c761834..049b36b656 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/init.lua @@ -1,4 +1,4 @@ -_G._OSVERSION = "OpenLoader 0.2" +_G._OSVERSION = "OpenLoader 0.3" local component = component or require('component') local computer = computer or require('computer') local unicode = unicode or require('unicode') @@ -15,29 +15,45 @@ local gpu = component.list("gpu")() local w, h local screen = component.list('screen')() -for address in component.list('screen') do - if #component.invoke(address, 'getKeyboards') > 0 then - screen = address + +local function gpucast(op, arg, ...) + local res = {} + local n = 1 + for address in component.list('screen') do + component.invoke(gpu, "bind", address) + if type(arg) == "table" then + res[#res + 1] = {component.invoke(gpu, op, table.unpack(arg[n]))} + else + res[#res + 1] = {component.invoke(gpu, op, arg, ...)} + end + n = n + 1 end + return res end local cls = function()end if gpu and screen then - component.invoke(gpu, "bind", screen) + --component.invoke(gpu, "bind", screen) w, h = component.invoke(gpu, "getResolution") - component.invoke(gpu, "setResolution", w, h) - component.invoke(gpu, "setBackground", 0x000000) - component.invoke(gpu, "setForeground", 0xFFFFFF) - component.invoke(gpu, "fill", 1, 1, w, h, " ") - cls = function()component.invoke(gpu,"fill", 1, 1, w, h, " ")end + local res = gpucast("getResolution") + gpucast("setResolution", res) + gpucast("setBackground", 0x000000) + gpucast("setForeground", 0xFFFFFF) + for _, e in ipairs(res)do + table.insert(e, 1, 1) + table.insert(e, 1, 1) + e[#e+1] = " " + end + gpucast("fill", res) + cls = function()gpucast("fill", res)end end local y = 1 local function status(msg) if gpu and screen then - component.invoke(gpu, "set", 1, y, msg) + gpucast("set", 1, y, msg) if y == h then - component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) - component.invoke(gpu, "fill", 1, h, w, 1, " ") + gpucast("copy", 1, 2, w, h - 1, 0, -1) + gpucast("fill", 1, h, w, 1, " ") else y = y + 1 end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua index 5f2f1abff4..b79880a7d0 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/10_devfs.lua @@ -32,18 +32,18 @@ proxy.open = function(path) local hnd = allocator:get() hnd.file = file if hnd.file.open then - hnd.file:open() + hnd.file.open(hnd) end return hnd.id end proxy.read = function(h, ...) - return handles[h].file:read(...) + return handles[h].file.read(handles[h], ...) end proxy.close = function(h) allocator:unset(handles[h]) end proxy.write = function(h, ...) - return handles[h].file:write(...) + return handles[h].file.write(handles[h], ...) end proxy.isDirectory = function(path) local seg = kernel.modules.vfs.segments(path) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_eeprom.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_eeprom.lua new file mode 100644 index 0000000000..725198fe12 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/17_eeprom.lua @@ -0,0 +1,50 @@ +function start() + kernel.modules.devfs.data.eeprom = { + __type = "f", + open = function(hnd) + if not component.list("eeprom")() then + error("No eeprom installed") + end + hnd.pos = 1 + end, + size = function() + return component.invoke(component.list("eeprom")() or "", "getSize") + end, + write = function(h, data) + if h.pos > 1 then + data = component.invoke(component.list("eeprom")() or "", "get"):sub(1,h.pos) .. data + end + component.invoke(component.list("eeprom")() or "", "set", data) + end, + read = function(h, len) + local res = component.invoke(component.list("eeprom")() or "", "get") + res = res:sub(h.pos, len) + h.pos = h.pos + len + return res ~= "" and res + end + } + kernel.modules.devfs.data["eeprom-data"] = { + __type = "f", + open = function(hnd) + if not component.list("eeprom")() then + error("No eeprom installed") + end + hnd.pos = 1 + end, + size = function() + return 256 --TODO: is this correct? + end, + write = function(h, data) + if h.pos > 1 then + data = component.invoke(component.list("eeprom")() or "", "getData"):sub(1,h.pos) .. data + end + component.invoke(component.list("eeprom")() or "", "setData", data) + end, + read = function(h, len) + local res = component.invoke(component.list("eeprom")() or "", "getData") + res = res:sub(h.pos, len) + h.pos = h.pos + len + return res ~= "" and res + end + } +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua index f7edb0bdcc..cc9ff9b3e5 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/term.lua @@ -1,9 +1,63 @@ local term = {} +local function read(from, to) + local started, data + while true do + local char = io.read(1) + if not char then + error("Broken pipe") + end + if not started and char == from then + started = true + data = char + elseif started then + if char == to then + return data .. char + else + data = data .. char + end + end + end +end + function term.clear() io.write("\x1b[2J") end +function term.clearLine() + --io.write("\x1b[K") +end + +function term.getCursor() + io.write("\x1b[6n") + local code = read("\x1b", "R") + local y, x = code:match("\x1b%[(%d+);(%d+)R") + + return tonumber(x), tonumber(y) +end + +function term.getResolution() + --io.write("\x1b[6n") + --local code = read("\x1b", "R") + --local y, x = code:match("\x1b%[(%d+);(%d+)R") + + --return tonumber(x), tonumber(y) +end + +function term.setCursor(col, row) + checkArg(1, col, "number") + checkArg(2, row, "number") + io.write("\x1b[" .. row .. ";" .. col .. "H") +end + +function term.isAvailable() + return true +end + +function term.setCursorBlink(enabled) + +end + function term.read(history) history = history or {} local x, y = 1, 1 @@ -74,6 +128,22 @@ function term.read(history) io.write("\x1b[" .. (x - 1) .. "D\x1b[K" .. line) x = unicode.len(line) + 1 end + elseif act == "3" and io.read(1) == "~" then + local pre = unicode.sub(getLine(), 1, x - 1) + local after = unicode.sub(getLine(), x + 1) + setLine(pre .. after) + --x = x + io.write("\x1b[K" .. after .. "\x1b[" .. unicode.len(after) .. "D") + end + elseif mode == "0" then + local act = io.read(1) + if act == "H" then + io.write("\x1b["..(x - 1).."D") + x = 1 + elseif act == "F" then + local line = getLine() + io.write("\x1b[" .. (x - 1) .. "D\x1b[" .. (unicode.len(line)) .. "C") + x = unicode.len(line) + 1 end end elseif char:match("[%g%s]") then @@ -82,4 +152,8 @@ function term.read(history) end end +function term.write(value, wrap) + io.write(value) +end + return term diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua index b572e92acc..17657e1ef8 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua @@ -236,6 +236,7 @@ mptFrontend = { for _, file in ipairs(data.files) do backend.copyFile(config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file, file) end + backend.removeFile(config.cacheDir.."mpt/"..data.name.."/".. data.checksum) end, removePackage = function(package) @@ -304,6 +305,7 @@ mirrorFrontend = { for _, file in ipairs(data.files) do backend.copyFile(config.cacheDir.."mpt/"..data.name.."/".. data.checksum ..file, file) end + backend.removeFile(config.cacheDir.."mpt/"..data.name.."/".. data.checksum) end, removePackage = function(package) for _, file in ipairs(base.installed[package].data.files) do diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db index 92ac3946c4..15bd8410da 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db @@ -1 +1 @@ -{cacheDir="/var/lib/mpt/cache/",database="/var/lib/mpt/base.db",frontend={mpt={api="http://mpt.magik6k.net/api/"}}} \ No newline at end of file +{cacheDir="/var/lib/mpt/cache/",frontend={mpt={api="http://mpt.magik6k.net/api/"}},database="/var/lib/mpt/base.db"} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db index 7f9df9f411..b49515f0cc 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db @@ -1 +1 @@ -{installed={["plan9k-installer"]={deps={"plan9k","mpt"},data={checksum="52c8f82357c966ce3e19c97bf3942012",dependencies={"plan9k","mpt"},files={"/bin/install.lua"},name="plan9k-installer",repo="plan9k"},frontend="MPT"},["plan9k-drivers"]={deps={},data={checksum="6cbd5412ca13503484299daa0597f754",dependencies={},files={"/lib/modules/base/17_tape.lua"},name="plan9k-drivers",repo="plan9k"},frontend="MPT"},["plan9k-network"]={deps={},data={checksum="336f43272f6b38051d71a5d640716df7",dependencies={},files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},name="plan9k-network",repo="plan9k"},frontend="MPT"},mpt={deps={},data={checksum="37a61081c47a12904b8af615c938e6db",dependencies={},files={"/usr/bin/mpt.lua"},name="mpt",repo="mpt"},frontend="MPT"},["plan9k-core"]={deps={"pipes","plan9k-coreutil","plan9k-shell"},data={checksum="5e0b72825b5ae2e7bdd77addfafa055",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"},name="plan9k-core",repo="plan9k"},frontend="MPT"},plan9k={deps={"plan9k-core","plan9k-network","plan9k-drivers"},data={checksum="-53f6ee1412ad0be211ff9aedfb202647",dependencies={"plan9k-core","plan9k-network","plan9k-drivers"},files={},name="plan9k",repo="plan9k"},frontend="MPT"},["openloader-init"]={deps={},data={checksum="28a395353488d35f6071631a6328b048",dependencies={},files={"/init.lua"},name="openloader-init",repo="disks"},frontend="MPT"},pipes={deps={"openloader-init"},data={checksum="-b9cd57614f2f1b2ed2708e7596b8a62",dependencies={"openloader-init"},files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua"},name="pipes",repo="plan9k"},frontend="MPT"},["plan9k-fsutil"]={deps={"plan9k-corelibs"},data={checksum="-1e0c3c93f8155321d24e4d0d1db31cb6",dependencies={"plan9k-corelibs"},files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua"},name="plan9k-fsutil",repo="plan9k"},frontend="MPT"},["plan9k-shell"]={deps={},data={checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={},files={"/bin/sh.lua"},name="plan9k-shell",repo="plan9k"},frontend="MPT"},["plan9k-corelibs"]={deps={},data={checksum="5efcf3df1abf690151c960690a75a768",dependencies={},files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},name="plan9k-corelibs",repo="plan9k"},frontend="MPT"},["plan9k-coreutil"]={deps={"plan9k-corelibs","plan9k-fsutil"},data={checksum="-e7c7132468ae675bc57c29a93921f7b",dependencies={"plan9k-corelibs","plan9k-fsutil"},files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua"},name="plan9k-coreutil",repo="plan9k"},frontend="MPT"}}} \ No newline at end of file +{installed={["plan9k-shell"]={frontend="MPT",data={name="plan9k-shell",checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={},repo="plan9k",files={"/bin/sh.lua"}},deps={}},["plan9k-installer"]={frontend="MPT",data={name="plan9k-installer",checksum="52c8f82357c966ce3e19c97bf3942012",dependencies={"plan9k","mpt"},repo="plan9k",files={"/bin/install.lua"}},deps={"plan9k","mpt"}},["openloader-init"]={frontend="MPT",data={name="openloader-init",checksum="-45e6d7b1e41468c1d335952ee3b89e13",dependencies={},repo="disks",files={"/init.lua"}},deps={}},mpt={frontend="MPT",data={name="mpt",checksum="-4dd2104fef407efcf47ca2908f680340",dependencies={},repo="mpt",files={"/usr/bin/mpt.lua"}},deps={}},["plan9k-core"]={frontend="MPT",data={name="plan9k-core",checksum="-6b8c4bbb33c0c20a900a2b5e1e9476f7",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},repo="plan9k",files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"}},deps={"pipes","plan9k-coreutil","plan9k-shell"}},["plan9k-network"]={frontend="MPT",data={name="plan9k-network",checksum="336f43272f6b38051d71a5d640716df7",dependencies={},repo="plan9k",files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"}},deps={}},["plan9k-corelibs"]={frontend="MPT",data={name="plan9k-corelibs",checksum="7d4b91ee265f364a6deafd9ca2881efa",dependencies={},repo="plan9k",files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"}},deps={}},["plan9k-fsutil"]={frontend="MPT",data={name="plan9k-fsutil",checksum="-1e0c3c93f8155321d24e4d0d1db31cb6",dependencies={"plan9k-corelibs"},repo="plan9k",files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua"}},deps={"plan9k-corelibs"}},["plan9k-edit"]={frontend="MPT",data={name="plan9k-edit",checksum="-5a8af91ab93ec0362f5bf1080eb94718",dependencies={},repo="plan9k",files={"/bin/edit.lua"}},deps={}},pipes={frontend="MPT",data={name="pipes",checksum="-51cf98ed4df4c9a20832a4c57086a57c",dependencies={"openloader-init"},repo="plan9k",files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua"}},deps={"openloader-init"}},plan9k={frontend="MPT",data={name="plan9k",checksum="-c16ccdf21a1ff13be8f6258d1a17d89",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"},repo="plan9k",files={}},deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"}},["plan9k-drivers"]={frontend="MPT",data={name="plan9k-drivers",checksum="-2a9819da9410d2803c1946d76bbd4250",dependencies={},repo="plan9k",files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua"}},deps={}},["plan9k-coreutil"]={frontend="MPT",data={name="plan9k-coreutil",checksum="-e7c7132468ae675bc57c29a93921f7b",dependencies={"plan9k-corelibs","plan9k-fsutil"},repo="plan9k",files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua"}},deps={"plan9k-corelibs","plan9k-fsutil"}}}} \ No newline at end of file From 4ca825d234cababff99132b3d39c4a6f777d0fef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 29 Jun 2015 01:56:48 +0200 Subject: [PATCH 5/6] More final fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Magiera --- .../opencomputers/loot/Plan9k/bin/edit.lua | 2 +- .../opencomputers/loot/Plan9k/bin/getty.lua | 1 - .../opencomputers/loot/Plan9k/bin/mkdir.lua | 23 ++ .../loot/Plan9k/bin/mount.cow.lua | 37 ++++ .../opencomputers/loot/Plan9k/bin/mount.lua | 28 +++ .../loot/Plan9k/bin/shutdown.lua | 5 + .../opencomputers/loot/Plan9k/bin/touch.lua | 13 ++ .../loot/Plan9k/lib/modules/base/05_vfs.lua | 13 +- .../loot/Plan9k/lib/modules/base/06_cowfs.lua | 200 ++++++++++++++++++ .../Plan9k/lib/modules/base/09_rootfs.lua | 9 + .../Plan9k/lib/modules/base/15_userspace.lua | 1 + .../Plan9k/lib/modules/base/18_syscall.lua | 2 +- .../opencomputers/loot/Plan9k/usr/bin/mpt.lua | 2 +- .../loot/Plan9k/var/lib/mpt/config.db | 2 +- .../loot/Plan9k/var/lib/mpt/mpt.db | 2 +- 15 files changed, 325 insertions(+), 15 deletions(-) create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/mkdir.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.cow.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/shutdown.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/bin/touch.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/06_cowfs.lua create mode 100644 src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/09_rootfs.lua diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua index e728c8c97f..f178f7eecb 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/edit.lua @@ -10,7 +10,7 @@ if not args[1] then return end -local file = args[1] +local file = args[1] or "" local function read(from, to) local started, data diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua index f5b8dd0988..9e8e568402 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/getty.lua @@ -158,7 +158,6 @@ mcommands["3"] = function()end --Italic mcommands["4"] = function()end --Underscore mcommands["10"] = function()end --Select primary font (LA100) - mcommands["30"] = function()component.invoke(gpu, "setForeground", 0x000000)end mcommands["31"] = function()component.invoke(gpu, "setForeground", 0xFF0000)end mcommands["32"] = function()component.invoke(gpu, "setForeground", 0x00FF00)end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mkdir.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mkdir.lua new file mode 100644 index 0000000000..2b3c077c3f --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mkdir.lua @@ -0,0 +1,23 @@ +local fs = require("filesystem") +local shell = require("shell") + +local args = shell.parse(...) +if #args == 0 then + io.write("Usage: mkdir [ [...]]") + return +end + +for i = 1, #args do + local path = shell.resolve(args[i]) + local result, reason = fs.makeDirectory(path) + if not result then + if not reason then + if fs.exists(path) then + reason = "file or folder with that name already exists" + else + reason = "unknown reason" + end + end + io.stderr:write(path .. ": " .. reason .. "\n") + end +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.cow.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.cow.lua new file mode 100644 index 0000000000..5594abf33b --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.cow.lua @@ -0,0 +1,37 @@ +local fs = require("filesystem") +local shell = require("shell") +local pipes = require("pipes") + +local args, options = shell.parse(...) +if #args == 0 then + for proxy, path in fs.mounts() do + local label = proxy.getLabel() or proxy.address + local mode = proxy.isReadOnly() and "ro" or "rw" + print(string.format("%s on %s (%s)", label, path, mode)) + end + return +end +if #args < 2 then + print("Usage: mount label|address label|address path]") + print("Note that the addresses may be abbreviated.") + return +end + +local readproxy, reason = fs.proxy(args[1]) +if not readproxy then + print(reason) + return +end + +local writeproxy, reason = fs.proxy(args[2]) +if not writeproxy then + print(reason) + return +end + +local proxy = pipes.cowProxy(readproxy, writeproxy) + +local result, reason = fs.mount(proxy, args[3]) +if not result then + print(reason) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.lua new file mode 100644 index 0000000000..6f7c1f870d --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/mount.lua @@ -0,0 +1,28 @@ +local fs = require("filesystem") +local shell = require("shell") + +local args, options = shell.parse(...) +if #args == 0 then + for proxy, path in fs.mounts() do + local label = proxy.getLabel() or proxy.address + local mode = proxy.isReadOnly() and "ro" or "rw" + io.write(string.format("%s on %s (%s)\n", label, path, mode)) + end + return +end +if #args < 2 then + io.write("Usage: mount [label|address path]\n") + io.write("Note that the address may be abbreviated.") + return +end + +local proxy, reason = fs.proxy(args[1]) +if not proxy then + io.stderr:write(reason) + return +end + +local result, reason = fs.mount(table.unpack(args)) +if not result then + io.stderr:write(reason) +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/shutdown.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/shutdown.lua new file mode 100644 index 0000000000..5ee5c3ce5f --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/shutdown.lua @@ -0,0 +1,5 @@ +local computer = require("computer") +local term = require("term") + +term.clear() +computer.shutdown() diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/bin/touch.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/touch.lua new file mode 100644 index 0000000000..11a376091e --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/bin/touch.lua @@ -0,0 +1,13 @@ +local fs = require("filesystem") + +local args = {...} +local file = args[1] + +if not file then + print("Usage: touch [file]") + return +end + +if fs.exists(file) then return end + +fs.open(file, "w"):close() \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua index 1a45be7626..b4a19de7c5 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/05_vfs.lua @@ -457,23 +457,23 @@ function filesystem.copy(fromPath, toPath) end local input, reason = kernel.modules.io.io.open(fromPath, "rb") if not input then - return nil, reason + return nil, reason, "open input" end local output, reason = kernel.modules.io.io.open(toPath, "wb") if not output then input:close() - return nil, reason + return nil, reason, "open output" end repeat local buffer, reason = input:read(1024) if not buffer and reason then - return nil, reason + return nil, reason, "read input" elseif buffer then local result, reason = output:write(buffer) if not result then input:close() output:close() - return nil, reason + return nil, reason, "write to output" end end until not buffer @@ -545,8 +545,3 @@ for k,v in pairs(filesystem) do _G[k] = v end - -function start() - mount(computer.getBootAddress(), "/") -end - diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/06_cowfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/06_cowfs.lua new file mode 100644 index 0000000000..755587c7d3 --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/06_cowfs.lua @@ -0,0 +1,200 @@ +local fscount = 0 + +function new(readfs, writefs) + if type(readfs) == "string" then + readfs = component.proxy(component.get(readfs)) + end + if type(writefs) == "string" then + writefs = component.proxy(component.get(writefs)) + end + + local function getFileFS(path) + if writefs.exists(path) then + return writefs + end + if not writefs.exists(kernel.modules.vfs.path(path)..".cfsdel."..(kernel.modules.vfs.name(path) or "")) then + if readfs.exists(path) then + return readfs + end + end + end + + local proxy = {} + + proxy.address = "MOOOoooo" + proxy.spaceUsed = function() return writefs.spaceUsed() end + proxy.spaceTotal = function() return writefs.spaceTotal() end + proxy.makeDirectory = function(...) return writefs.makeDirectory(...) end + proxy.isReadOnly = function() return writefs.isReadOnly() end + proxy.getLabel = function() return readfs.getLabel() and readfs.getLabel().."-cow" or "cowfs" end + proxy.setLabel = function() --[[TODO!]] end + proxy.size = function(path) + local fs = getFileFS(path) + if fs then + return fs.size(path) + end + return 0 + end + proxy.exists = function(path) + if not writefs.exists(kernel.modules.vfs.path(path)..".cfsdel."..kernel.modules.vfs.name(path)) then + if readfs.exists(path) then + return true + end + end + return writefs.exists(path) + end + proxy.isDirectory = function(path) + local fs = getFileFS(path) + if fs then + return fs.isDirectory(path) + end + return 0 + end + proxy.rename = function(from, to) + if getFileFS(to) then + return false + end + local fromfs = getFileFS(from) + if not fromfs then return false end + if fromfs == writefs then + return writefs.rename(from, to) + elseif fromfs == readfs then + local rfd = readfs.open(from, "rb") + local wfd = writefs.open(to, "wb") + repeat + local buf = readfs.read(rfd, 1024) + if buf then + if not writefs.write(wfd, buf) then break end + end + until not buf + readfs.close(rfd) + writefs.close(wfd) + return true --TODO: handle fails + end + end + proxy.remove = function(path) --TODO: fix directory deletion + local fs = getFileFS(path) + if fs == writefs then + return writefs.remove(path) + elseif fs == readfs then + writefs.makeDirectory(kernel.modules.vfs.path(path)) + writefs.close(writefs.open(kernel.modules.vfs.path(path)..".cfsdel."..kernel.modules.vfs.name(path), "w")) + return true + end + end + proxy.list = function(path) + local result = {} + if readfs.isDirectory(path) and not writefs.exists(kernel.modules.vfs.path(path)..".cfsdel."..(kernel.modules.vfs.name(path) or "")) then + result = readfs.list(path) + end + if writefs.isDirectory(path) then + local wlist = writefs.list(path) + for _, file in ipairs(wlist) do + if file:sub(1, 8) == ".cfsdel." then + if not writefs.exists(file) then + local fn = file:sub(9) + for i, f in ipairs(result) do + if f:sub(#f) == "/" then f = f:sub(1, #f - 1) end + if f == fn then + table.remove(result, i) + break + end + end + end + else + result[#result + 1] = file + end + end + end + return result + end + proxy.open = function(path, mode) --hnd = orig * 2 [+ 1] + if mode:sub(1, 1) == "w" then + if readfs.exists(path) and not writefs.exists(kernel.modules.vfs.path(path)..".cfsdel."..kernel.modules.vfs.name(path)) then + if readfs.isDirectory(path) then + return nil, "Cannot open a directory" + elseif writefs.isDirectory(kernel.modules.vfs.path(path)) then + writefs.close(writefs.open(kernel.modules.vfs.path(path)..".cfsdel."..kernel.modules.vfs.name(path), "w")) + else + return nil, "file not found" + end + end + if not writefs.isDirectory(kernel.modules.vfs.path(path)) then + return nil, "file not found" + end + if writefs.isDirectory(path) then + return nil, "Cannot open a directory" + end + local hnd = writefs.open(path, mode) + return hnd * 2 + elseif mode:sub(1, 1) == "a" then + if readfs.exists(path) and not writefs.exists(kernel.modules.vfs.path(path)..".cfsdel."..kernel.modules.vfs.name(path)) then + if readfs.isDirectory(path) then + return nil, "Cannot open a directory" + else + if not writefs.isDirectory(kernel.modules.vfs.path(path)) then + writefs.makeDirectory(kernel.modules.vfs.path(path)) + end + writefs.close(writefs.open(kernel.modules.vfs.path(path)..".cfsdel."..kernel.modules.vfs.name(path), "w")) + local rfd = readfs.open(path, "rb") + local wfd = writefs.open(path, "wb") + repeat + local buf = readfs.read(rfd, 1024) + if buf then + if not writefs.write(wfd, buf) then break end + end + until not buf + readfs.close(rfd) + writefs.close(wfd) + end + end + if not writefs.isDirectory(kernel.modules.vfs.path(path)) then + return nil, "file not found" + end + if writefs.isDirectory(path) then + return nil, "Cannot open a directory" + end + local hnd = writefs.open(path, mode) + return hnd * 2 + elseif mode:sub(1, 1) == "r" then + local fs = getFileFS(path) + if not fs then return nil, "file not found" end + if fs.isDirectory(path) then + return nil, "Cannot open a directory" + end + local hnd = fs.open(path, mode) + hnd = hnd * 2 + if fs == readfs then hnd = hnd + 1 end + return hnd + end + end + proxy.seek = function(h, ...) + if h % 2 == 0 then + return writefs.seek(h / 2, ...) + else + return readfs.seek((h - 1) / 2, ...) + end + end + proxy.read = function(h, ...) + if h % 2 == 0 then + return writefs.read(h / 2, ...) + else + return readfs.read((h - 1) / 2, ...) + end + end + proxy.close = function(h, ...) + if h % 2 == 0 then + return writefs.close(h / 2, ...) + else + return readfs.close((h - 1) / 2, ...) + end + end + proxy.write = function(h, ...) + if h % 2 == 0 then + return writefs.write(h / 2, ...) + else + return readfs.write((h - 1) / 2, ...) + end + end + return proxy +end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/09_rootfs.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/09_rootfs.lua new file mode 100644 index 0000000000..8713f4f98e --- /dev/null +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/09_rootfs.lua @@ -0,0 +1,9 @@ +function start() + if component.invoke(computer.getBootAddress(), "isReadOnly") then + local cow = kernel.modules.cowfs.new(computer.getBootAddress(), computer.tmpAddress()) + kernel.modules.vfs.mount(cow, "/") + else + kernel.modules.vfs.mount(computer.getBootAddress(), "/") + end + kernel.modules.vfs.mount(computer.tmpAddress(), "/tmp") +end diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua index 4b56bfa9db..0b0a3ba320 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/15_userspace.lua @@ -15,6 +15,7 @@ kernel.userspace.computer.addUser = kernel._K.computer.addUser kernel.userspace.computer.removeUser = kernel._K.computer.removeUser kernel.userspace.computer.pushSignal = kernel._K.computer.pushSignal kernel.userspace.computer.uptime = kernel._K.computer.uptime +kernel.userspace.computer.getBootAddress = kernel._K.computer.getBootAddress kernel.userspace.computer.pullSignal = function(timeout) return coroutine.yield("signal", timeout) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua index dbaed49143..66dd6a8714 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/lib/modules/base/18_syscall.lua @@ -27,5 +27,5 @@ function start() end kernel.userspace.package.preload.pipes.openPty = kernel.modules.pty.new - + kernel.userspace.package.preload.pipes.cowProxy = kernel.modules.cowfs.new end \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua index 17657e1ef8..5f9ef04ded 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/usr/bin/mpt.lua @@ -507,7 +507,7 @@ core = { end end - core.promptUser("Do you want to continue?[Y/n] ") + core.promptUser("Do you want to continue?[y/N] ") if core.data.remove then core.log(1, "Core", "Removing packages") diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db index 15bd8410da..1297bda91e 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db @@ -1 +1 @@ -{cacheDir="/var/lib/mpt/cache/",frontend={mpt={api="http://mpt.magik6k.net/api/"}},database="/var/lib/mpt/base.db"} \ No newline at end of file +{database="/var/lib/mpt/base.db",frontend={mpt={api="http://mpt.magik6k.net/api/"}},cacheDir="/var/lib/mpt/cache/"} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db index b49515f0cc..e35bbf2c42 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db @@ -1 +1 @@ -{installed={["plan9k-shell"]={frontend="MPT",data={name="plan9k-shell",checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={},repo="plan9k",files={"/bin/sh.lua"}},deps={}},["plan9k-installer"]={frontend="MPT",data={name="plan9k-installer",checksum="52c8f82357c966ce3e19c97bf3942012",dependencies={"plan9k","mpt"},repo="plan9k",files={"/bin/install.lua"}},deps={"plan9k","mpt"}},["openloader-init"]={frontend="MPT",data={name="openloader-init",checksum="-45e6d7b1e41468c1d335952ee3b89e13",dependencies={},repo="disks",files={"/init.lua"}},deps={}},mpt={frontend="MPT",data={name="mpt",checksum="-4dd2104fef407efcf47ca2908f680340",dependencies={},repo="mpt",files={"/usr/bin/mpt.lua"}},deps={}},["plan9k-core"]={frontend="MPT",data={name="plan9k-core",checksum="-6b8c4bbb33c0c20a900a2b5e1e9476f7",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},repo="plan9k",files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"}},deps={"pipes","plan9k-coreutil","plan9k-shell"}},["plan9k-network"]={frontend="MPT",data={name="plan9k-network",checksum="336f43272f6b38051d71a5d640716df7",dependencies={},repo="plan9k",files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"}},deps={}},["plan9k-corelibs"]={frontend="MPT",data={name="plan9k-corelibs",checksum="7d4b91ee265f364a6deafd9ca2881efa",dependencies={},repo="plan9k",files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"}},deps={}},["plan9k-fsutil"]={frontend="MPT",data={name="plan9k-fsutil",checksum="-1e0c3c93f8155321d24e4d0d1db31cb6",dependencies={"plan9k-corelibs"},repo="plan9k",files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua"}},deps={"plan9k-corelibs"}},["plan9k-edit"]={frontend="MPT",data={name="plan9k-edit",checksum="-5a8af91ab93ec0362f5bf1080eb94718",dependencies={},repo="plan9k",files={"/bin/edit.lua"}},deps={}},pipes={frontend="MPT",data={name="pipes",checksum="-51cf98ed4df4c9a20832a4c57086a57c",dependencies={"openloader-init"},repo="plan9k",files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua"}},deps={"openloader-init"}},plan9k={frontend="MPT",data={name="plan9k",checksum="-c16ccdf21a1ff13be8f6258d1a17d89",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"},repo="plan9k",files={}},deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"}},["plan9k-drivers"]={frontend="MPT",data={name="plan9k-drivers",checksum="-2a9819da9410d2803c1946d76bbd4250",dependencies={},repo="plan9k",files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua"}},deps={}},["plan9k-coreutil"]={frontend="MPT",data={name="plan9k-coreutil",checksum="-e7c7132468ae675bc57c29a93921f7b",dependencies={"plan9k-corelibs","plan9k-fsutil"},repo="plan9k",files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua"}},deps={"plan9k-corelibs","plan9k-fsutil"}}}} \ No newline at end of file +{installed={pipes={frontend="MPT",data={files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua","/lib/modules/base/06_cowfs.lua","/lib/modules/base/09_rootfs.lua"},checksum="-409e583a9ec6fcc87e70326c1ef4c85d",dependencies={"openloader-init"},repo="plan9k",name="pipes"},deps={"openloader-init"}},["plan9k-coreutil"]={frontend="MPT",data={files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua","/bin/shutdown.lua"},checksum="6ac1f869cdc43d4f19520a64f01fe3af",dependencies={"plan9k-corelibs","plan9k-fsutil"},repo="plan9k",name="plan9k-coreutil"},deps={"plan9k-corelibs","plan9k-fsutil"}},["plan9k-fsutil"]={frontend="MPT",data={files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua","/bin/touch.lua","/bin/mount.lua","/bin/mount.cow.lua","/bin/mkdir.lua"},checksum="1cb19ef3e4dfdc41faadf0cd6df02003",dependencies={"plan9k-corelibs"},repo="plan9k",name="plan9k-fsutil"},deps={"plan9k-corelibs"}},["plan9k-core"]={frontend="MPT",data={files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"},checksum="-3d53e6f082cfe5a61d78c78e7293660b",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},repo="plan9k",name="plan9k-core"},deps={"pipes","plan9k-coreutil","plan9k-shell"}},["plan9k-edit"]={frontend="MPT",data={files={"/bin/edit.lua"},checksum="fed5f4ee1212297b07247afa1cfe3a2",dependencies={},repo="plan9k",name="plan9k-edit"},deps={}},["openloader-init"]={frontend="MPT",data={files={"/init.lua"},checksum="-45e6d7b1e41468c1d335952ee3b89e13",dependencies={},repo="disks",name="openloader-init"},deps={}},plan9k={frontend="MPT",data={files={},checksum="-c16ccdf21a1ff13be8f6258d1a17d89",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"},repo="plan9k",name="plan9k"},deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"}},mpt={frontend="MPT",data={files={"/usr/bin/mpt.lua"},checksum="f9d7744571e5c46c658f405043c656",dependencies={},repo="mpt",name="mpt"},deps={}},["plan9k-corelibs"]={frontend="MPT",data={files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},checksum="7d4b91ee265f364a6deafd9ca2881efa",dependencies={},repo="plan9k",name="plan9k-corelibs"},deps={}},["plan9k-installer"]={frontend="MPT",data={files={"/bin/install.lua"},checksum="52c8f82357c966ce3e19c97bf3942012",dependencies={"plan9k","mpt"},repo="plan9k",name="plan9k-installer"},deps={"plan9k","mpt"}},["plan9k-drivers"]={frontend="MPT",data={files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua"},checksum="-2a9819da9410d2803c1946d76bbd4250",dependencies={},repo="plan9k",name="plan9k-drivers"},deps={}},["plan9k-shell"]={frontend="MPT",data={files={"/bin/sh.lua"},checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={},repo="plan9k",name="plan9k-shell"},deps={}},["plan9k-network"]={frontend="MPT",data={files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},checksum="336f43272f6b38051d71a5d640716df7",dependencies={},repo="plan9k",name="plan9k-network"},deps={}}}} \ No newline at end of file From 120b4911b11f7fabb98da9d0b32453e56589f347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 29 Jun 2015 18:53:14 +0200 Subject: [PATCH 6/6] Added loading bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Łukasz Magiera --- .../loot/Plan9k/boot/kernel/pipes | 27 +++++++++++++++---- .../loot/Plan9k/var/lib/mpt/config.db | 2 +- .../loot/Plan9k/var/lib/mpt/mpt.db | 2 +- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes b/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes index dbef2f5ab6..34a7b696d0 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/boot/kernel/pipes @@ -46,25 +46,41 @@ do component.invoke(gpu, "bind", screen) w, h = component.invoke(gpu, "getResolution") component.invoke(gpu, "setResolution", w, h) + h = h - 3 + component.invoke(gpu, "setBackground", 0x555555) + component.invoke(gpu, "fill", 1, 1, w, 3, " ") + component.invoke(gpu, "setBackground", 0xAAAAAA) + component.invoke(gpu, "fill", 4, 2, w-7, 1, "-") component.invoke(gpu, "setBackground", 0x000000) component.invoke(gpu, "setForeground", 0xFFFFFF) - component.invoke(gpu, "fill", 1, 1, w, h, " ") + component.invoke(gpu, "fill", 1, 4, w, h, " ") end local y = 1 function kernel._println(msg) if gpu and screen then for line in string.gmatch(tostring(msg), "([^\n]+)") do - kernel._K.component.invoke(gpu, "set", 1, y, line) + kernel._K.component.invoke(gpu, "set", 1, y + 3, line) if y == h then - kernel._K.component.invoke(gpu, "copy", 1, 2, w, h - 1, 0, -1) - kernel._K.component.invoke(gpu, "fill", 1, h, w, 1, " ") + kernel._K.component.invoke(gpu, "copy", 1, 2+3, w, h - 1, 0, -1) + kernel._K.component.invoke(gpu, "fill", 1, h+3, w, 1, " ") else y = y + 1 end end end end + + function kernel._status(done) + if gpu and screen then + component.invoke(gpu, "setBackground", 0xFF0000) + component.invoke(gpu, "setForeground", 0x000000) + component.invoke(gpu, "fill", 4, 2, (w-7) * done, 1, "X") + component.invoke(gpu, "setBackground", 0x000000) + component.invoke(gpu, "setForeground", 0xFFFFFF) + end + end + kernel.io.println = kernel._println end @@ -151,7 +167,7 @@ for i = 1, #modules do kernel.io.println(result[2]) kernel.panic() end - + kernel._status((0.5 / (#modules)) * i) else kernel.io.println(reason) kernel.panic() @@ -169,6 +185,7 @@ for i = 1, #modules do kernel.panic() end end + kernel._status(0.5 + (0.5 / (#modules)) * i) end kernel.panic() diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db index 1297bda91e..92ac3946c4 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/config.db @@ -1 +1 @@ -{database="/var/lib/mpt/base.db",frontend={mpt={api="http://mpt.magik6k.net/api/"}},cacheDir="/var/lib/mpt/cache/"} \ No newline at end of file +{cacheDir="/var/lib/mpt/cache/",database="/var/lib/mpt/base.db",frontend={mpt={api="http://mpt.magik6k.net/api/"}}} \ No newline at end of file diff --git a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db index e35bbf2c42..ae0bc16c2b 100644 --- a/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db +++ b/src/main/resources/assets/opencomputers/loot/Plan9k/var/lib/mpt/mpt.db @@ -1 +1 @@ -{installed={pipes={frontend="MPT",data={files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua","/lib/modules/base/06_cowfs.lua","/lib/modules/base/09_rootfs.lua"},checksum="-409e583a9ec6fcc87e70326c1ef4c85d",dependencies={"openloader-init"},repo="plan9k",name="pipes"},deps={"openloader-init"}},["plan9k-coreutil"]={frontend="MPT",data={files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua","/bin/shutdown.lua"},checksum="6ac1f869cdc43d4f19520a64f01fe3af",dependencies={"plan9k-corelibs","plan9k-fsutil"},repo="plan9k",name="plan9k-coreutil"},deps={"plan9k-corelibs","plan9k-fsutil"}},["plan9k-fsutil"]={frontend="MPT",data={files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua","/bin/touch.lua","/bin/mount.lua","/bin/mount.cow.lua","/bin/mkdir.lua"},checksum="1cb19ef3e4dfdc41faadf0cd6df02003",dependencies={"plan9k-corelibs"},repo="plan9k",name="plan9k-fsutil"},deps={"plan9k-corelibs"}},["plan9k-core"]={frontend="MPT",data={files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"},checksum="-3d53e6f082cfe5a61d78c78e7293660b",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},repo="plan9k",name="plan9k-core"},deps={"pipes","plan9k-coreutil","plan9k-shell"}},["plan9k-edit"]={frontend="MPT",data={files={"/bin/edit.lua"},checksum="fed5f4ee1212297b07247afa1cfe3a2",dependencies={},repo="plan9k",name="plan9k-edit"},deps={}},["openloader-init"]={frontend="MPT",data={files={"/init.lua"},checksum="-45e6d7b1e41468c1d335952ee3b89e13",dependencies={},repo="disks",name="openloader-init"},deps={}},plan9k={frontend="MPT",data={files={},checksum="-c16ccdf21a1ff13be8f6258d1a17d89",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"},repo="plan9k",name="plan9k"},deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"}},mpt={frontend="MPT",data={files={"/usr/bin/mpt.lua"},checksum="f9d7744571e5c46c658f405043c656",dependencies={},repo="mpt",name="mpt"},deps={}},["plan9k-corelibs"]={frontend="MPT",data={files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"},checksum="7d4b91ee265f364a6deafd9ca2881efa",dependencies={},repo="plan9k",name="plan9k-corelibs"},deps={}},["plan9k-installer"]={frontend="MPT",data={files={"/bin/install.lua"},checksum="52c8f82357c966ce3e19c97bf3942012",dependencies={"plan9k","mpt"},repo="plan9k",name="plan9k-installer"},deps={"plan9k","mpt"}},["plan9k-drivers"]={frontend="MPT",data={files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua"},checksum="-2a9819da9410d2803c1946d76bbd4250",dependencies={},repo="plan9k",name="plan9k-drivers"},deps={}},["plan9k-shell"]={frontend="MPT",data={files={"/bin/sh.lua"},checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={},repo="plan9k",name="plan9k-shell"},deps={}},["plan9k-network"]={frontend="MPT",data={files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"},checksum="336f43272f6b38051d71a5d640716df7",dependencies={},repo="plan9k",name="plan9k-network"},deps={}}}} \ No newline at end of file +{installed={["plan9k-corelibs"]={data={checksum="7d4b91ee265f364a6deafd9ca2881efa",dependencies={},name="plan9k-corelibs",repo="plan9k",files={"/lib/serialization.lua","/lib/term.lua","/lib/text.lua","/lib/shell.lua","/lib/event.lua"}},frontend="MPT",deps={}},["plan9k-coreutil"]={data={checksum="6ac1f869cdc43d4f19520a64f01fe3af",dependencies={"plan9k-corelibs","plan9k-fsutil"},name="plan9k-coreutil",repo="plan9k",files={"/bin/echo.lua","/bin/wc.lua","/bin/ps.lua","/bin/lua.lua","/bin/kill.lua","/bin/reboot.lua","/bin/sleep.lua","/bin/clear.lua","/bin/components.lua","/bin/hostname.lua","/bin/dmesg.lua","/bin/shutdown.lua"}},frontend="MPT",deps={"plan9k-corelibs","plan9k-fsutil"}},["plan9k-installer"]={data={checksum="52c8f82357c966ce3e19c97bf3942012",dependencies={"plan9k","mpt"},name="plan9k-installer",repo="plan9k",files={"/bin/install.lua"}},frontend="MPT",deps={"plan9k","mpt"}},["openloader-init"]={data={checksum="-45e6d7b1e41468c1d335952ee3b89e13",dependencies={},name="openloader-init",repo="disks",files={"/init.lua"}},frontend="MPT",deps={}},mpt={data={checksum="f9d7744571e5c46c658f405043c656",dependencies={},name="mpt",repo="mpt",files={"/usr/bin/mpt.lua"}},frontend="MPT",deps={}},["plan9k-network"]={data={checksum="336f43272f6b38051d71a5d640716df7",dependencies={},name="plan9k-network",repo="plan9k",files={"/lib/internet.lua","/bin/pastebin.lua","/bin/wget.lua","/lib/modules/base/17_network.lua","/lib/modules/base/19_libnetwork.lua","/bin/arp.lua","/bin/ifconfig.lua","/bin/ping.lua","/bin/route.lua","/lib/modules/network/loopback.lua","/lib/modules/network/modem.lua","/usr/bin/nc.lua","/lib/modules/network/tunnel.lua"}},frontend="MPT",deps={}},pipes={data={checksum="-1aed07cfc9b732061797c1092fb6bb19",dependencies={"openloader-init"},name="pipes",repo="plan9k",files={"/boot/kernel/pipes","/lib/modules/base/05_vfs.lua","/lib/modules/base/20_threading.lua","/lib/modules/base/19_manageg.lua","/lib/modules/base/25_init.lua","/lib/modules/base/15_userspace.lua","/usr/man/pipes","/lib/modules/base/16_buffer.lua","/lib/modules/base/17_io.lua","/lib/modules/base/16_require.lua","/lib/modules/base/18_syscall.lua","/lib/modules/base/21_threadUtil.lua","/lib/modules/base/21_timer.lua","/lib/modules/base/16_component.lua","/lib/modules/base/15_keventd.lua","/lib/modules/base/10_procfs.lua","/lib/modules/base/01_util.lua","/lib/modules/base/10_devfs.lua","/lib/modules/base/18_pty.lua","/lib/modules/base/17_keyboard.lua","/lib/modules/base/06_cowfs.lua","/lib/modules/base/09_rootfs.lua"}},frontend="MPT",deps={"openloader-init"}},["plan9k-fsutil"]={data={checksum="1cb19ef3e4dfdc41faadf0cd6df02003",dependencies={"plan9k-corelibs"},name="plan9k-fsutil",repo="plan9k",files={"/bin/cat.lua","/bin/ln.lua","/bin/ls.lua","/bin/mv.lua","/bin/rm.lua","/bin/tee.lua","/bin/df.lua","/bin/dd.lua","/bin/cp.lua","/bin/touch.lua","/bin/mount.lua","/bin/mount.cow.lua","/bin/mkdir.lua"}},frontend="MPT",deps={"plan9k-corelibs"}},["plan9k-shell"]={data={checksum="49cb5a0a3dea62e73c409ec5072112ac",dependencies={},name="plan9k-shell",repo="plan9k",files={"/bin/sh.lua"}},frontend="MPT",deps={}},["plan9k-edit"]={data={checksum="fed5f4ee1212297b07247afa1cfe3a2",dependencies={},name="plan9k-edit",repo="plan9k",files={"/bin/edit.lua"}},frontend="MPT",deps={}},["plan9k-core"]={data={checksum="-3d53e6f082cfe5a61d78c78e7293660b",dependencies={"pipes","plan9k-coreutil","plan9k-shell"},name="plan9k-core",repo="plan9k",files={"/bin/init.lua","/bin/getty.lua","/bin/readkey.lua","/lib/rc.lua","/bin/rc.lua","/etc/rc.cfg"}},frontend="MPT",deps={"pipes","plan9k-coreutil","plan9k-shell"}},["plan9k-drivers"]={data={checksum="-2a9819da9410d2803c1946d76bbd4250",dependencies={},name="plan9k-drivers",repo="plan9k",files={"/lib/modules/base/17_tape.lua","/lib/modules/base/17_eeprom.lua"}},frontend="MPT",deps={}},plan9k={data={checksum="-c16ccdf21a1ff13be8f6258d1a17d89",dependencies={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"},name="plan9k",repo="plan9k",files={}},frontend="MPT",deps={"plan9k-core","plan9k-network","plan9k-drivers","plan9k-edit"}}}} \ No newline at end of file