From ce60ed8a0b542487dadc245e7500164056a6f834 Mon Sep 17 00:00:00 2001 From: Jase <108029829+Jased-0001@users.noreply.github.com> Date: Fri, 21 Feb 2025 00:33:21 -0500 Subject: [PATCH] 2.21.2025 (BETA) --- LICENSE | 9 ++ README.md | 3 + test/TODO | 9 ++ test/bin/openfetch.lua | 227 ++++++++++++++++++++++++++++ test/boot/scripts/10_components.lua | 49 ++++++ test/etc/motd | 12 ++ test/etc/system/boot.lua | 189 +++++++++++++++++++++++ test/etc/system/system.cfg | 3 + test/home/.bshrc | 0 test/home/filesystem_tests.lua | 24 +++ test/home/test.lua | 11 ++ test/init.lua | 35 +++++ test/lib/event.lua | 44 ++++++ test/lib/filesystem.lua | 175 +++++++++++++++++++++ test/lib/filesystem.lua.old | 223 +++++++++++++++++++++++++++ test/lib/package.lua | 40 +++++ test/sbin/bsh.lua | 204 +++++++++++++++++++++++++ test/sbin/de.lua | 133 ++++++++++++++++ test/sbin/free.lua | 4 + test/sbin/lspci.lua | 15 ++ 20 files changed, 1409 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 test/TODO create mode 100644 test/bin/openfetch.lua create mode 100644 test/boot/scripts/10_components.lua create mode 100644 test/etc/motd create mode 100644 test/etc/system/boot.lua create mode 100644 test/etc/system/system.cfg create mode 100644 test/home/.bshrc create mode 100644 test/home/filesystem_tests.lua create mode 100644 test/home/test.lua create mode 100644 test/init.lua create mode 100644 test/lib/event.lua create mode 100644 test/lib/filesystem.lua create mode 100644 test/lib/filesystem.lua.old create mode 100644 test/lib/package.lua create mode 100644 test/sbin/bsh.lua create mode 100644 test/sbin/de.lua create mode 100644 test/sbin/free.lua create mode 100644 test/sbin/lspci.lua diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..31c5805 --- /dev/null +++ b/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2025 Jase Williams + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..60eac60 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# BananaOS + +opencomputers test \ No newline at end of file diff --git a/test/TODO b/test/TODO new file mode 100644 index 0000000..7c46021 --- /dev/null +++ b/test/TODO @@ -0,0 +1,9 @@ +- Filesystem +Mounts and virtual mounts (ie /tmp, /dev) +Protected paths + +- Package +Error handling +Paths + +- I/O diff --git a/test/bin/openfetch.lua b/test/bin/openfetch.lua new file mode 100644 index 0000000..fb489b3 --- /dev/null +++ b/test/bin/openfetch.lua @@ -0,0 +1,227 @@ +-- openfetch 1.4 | by ethernalsteve & Bs0Dd + +local component = require("component") +local computer = require("computer") +local fs = require("filesystem") +local gpu = component.gpu + +local logos = { + { + " %%%%(///////(%%% ", + " %% (///%%%/(%%%%% ", + " %% (///%%%/(%%%%% ", + " %% (///////(%%%%% ", + " %%%%%%%%%%%%%%%%%%% ", + " %%%%%%%%%%%%%%%%%%% ", + " %% %% ", + " %% %% ", + " %%%%%%%%%%%%%%%%%%% ", + " %%%%%%%%%%%%%%%%% " + }, + { + " %%%%%(///////////////(%%%% ", + " %%%###(//////%%%%%%///(%%%%%%% ", + " %%%###(//////%%%%%%///(%%%%%%% ", + " %%%###(//////%%%%%%///(%%%%%%% ", + " %%%###(//////%%%%%%///(%%%%%%% ", + " %%%###(///////////////(%%%%%%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " %%%((((((((((((((((((((((((%%% ", + " %%%((((((((((((((((((((((((%%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " %%% %%% ", + " %%%////////////////////////%%% ", + " %%% %%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%% " + }, + { + " %%%%%%%%%%(///////////////////////(%%%%%%% ", + " %%%%%%#####(///////////%%%%%%%/////(%%%%%%%%% ", + " %%%%%%#####(///////////%%%%%%%/////(%%%%%%%%%% ", + " %%%%%%#####(///////////%%%%%%%/////(%%%%%%%%%% ", + " %%%%%%#####(///////////%%%%%%%/////(%%%%%%%%%% ", + " %%%%%%#####(///////////%%%%%%%/////(%%%%%%%%%% ", + " %%%%%%#####(///////////%%%%%%%/////(%%%%%%%%%% ", + " %%%%%%#####(///////////////////////(%%%%%%%%%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " %%%%%%((((((((((((((((((((((((((((((((((%%%%%% ", + " %%%%%%((((((((((((((((((((((((((((((((((%%%%%% ", + " %%%%%%((((((((((((((((((((((((((((((((((%%%%%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " %%%%%% %%%%%% ", + " %%%%%% %%%%%% ", + " %%%%%% %%%%%% ", + " %%%%%%//////////////////////////////////%%%%%% ", + " %%%%%% %%%%%% ", + " %%%%%% %%%%%% ", + " %%%%%% %%%%%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% ", + " %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% " + } +} + +local w, h = gpu.maxResolution() +local devs = component.computer.getDeviceInfo() +local gpuInfoStr + +local function getGPUTier() + local dp = gpu.maxDepth() + if dp == 8 then + return 3 + elseif dp == 4 then + return 2 + else + return 1 + end +end + +local function getModel(desc) + local name + for _, dev in pairs(devs) do + if dev.description == desc then + name = dev.product + break + end + end + return name +end + +local OS = "Unknown" +if fs.exists("OS.lua") then + OS = "MineOS" +elseif fs.exists("/lib/core") then + OS = "OpenOS" +elseif fs.exists("/root") then + OS = "Plan9k" +elseif fs.exists("/etc/system/") then + OS = "BananaOS" +end + +local function getParsedUptime() + local seconds, minutes, hours = math.floor(computer.uptime()), 0, 0 + local time = "" + if seconds >= 60 then + minutes = math.floor(seconds / 60) + seconds = seconds % 60 + end + if minutes >= 60 then + hours = math.floor(minutes / 60) + minutes = minutes % 60 + end + if getGPUTier() == 1 then + time = time .. string.format("%02d:%02d:%02d", hours, minutes, seconds) + else + if hours == 1 then time = hours .. " hour, " + elseif hours >= 2 then time = hours .. " hours, " + end + if minutes == 1 then time = time .. minutes .. " min, " + elseif minutes >= 2 then time = time .. minutes .. " mins, " + end + time = time .. seconds .. " sec" + end + return time +end + +local logo = logos[getGPUTier()] +local function addCharacteristics() + local cpu, apu = getModel("CPU"), getModel("APU") + gpuInfoStr = 8 + + logo[2] = logo[2] .. "|OS:|" .. OS + logo[3] = logo[3] .. "|Uptime:|" .. getParsedUptime() + logo[4] = logo[4] .. "|Architecture:|" .. _VERSION + logo[5] = logo[5] .. "|Resolution:|" .. math.floor(w) .. "x" .. math.floor(h) + logo[6] = logo[6] .. "|Terminal:|" .. getModel("Text buffer") + if cpu ~= nil then logo[7] = logo[7] .. "|CPU:|" .. cpu:sub(0,11) .. ' (' .. cpu:match('%d') .. ' Tier)' + elseif apu ~= nil then logo[7] = logo[7] .. "|APU:|" .. apu:sub(0,11) .. ' (' .. apu:match('%d') .. ' Tier)' end + for _, dev in pairs(devs) do + if dev.description == "Graphics controller" then + logo[gpuInfoStr] = logo[gpuInfoStr] .. "|GPU:|" .. dev.product .. ' (' .. dev.product:match('%d') .. ' Tier)' + gpuInfoStr = gpuInfoStr + 1 + end + end + logo[gpuInfoStr] = logo[gpuInfoStr] .. "|Memory:|" .. math.floor(computer.totalMemory() / 1024 - computer.freeMemory() / 1024) .. " KB / " .. math.floor(computer.totalMemory() / 1024) .. " KB" +end + +local function drawPalette() + local palette = {{0x000000, 0x333333}, {0xCC0000, 0xFF0000}, {0x00CC00, 0x00FF00}, {0xCCCC00, 0xFFFF00}, + {0x0000CC, 0x0000FF}, {0xCC00CC, 0xFF00FF}, {0x00CCCC, 0x00FFFF}, {0xCCCCCC, 0xFFFFFF}} + local cur = #logo[1] + 2 + for _, color in pairs(palette) do + gpu.setForeground(color[1]) + gpu.set(cur, gpuInfoStr + 2, "███") + gpu.setForeground(color[2]) + gpu.set(cur, gpuInfoStr + 3, "███") + cur = cur + 3 + end +end + +gpu.setResolution(w, h) +addCharacteristics() +gpu.setBackground(0x000000) +gpu.fill(1, 1, w, h, " ") + +for i = 1, #logo do + local logoLine, tmp, f = {}, {}, false + logo[i]:gsub(".", function(c) table.insert(logoLine, c) end) + for ii = 1, #logoLine do + if f then + if string.match(logoLine[ii], "|") then + f = false + else + if string.match(logoLine[ii], ":") then + gpu.setForeground(0xffffff) + elseif OS == "MineOS" then + gpu.setForeground(0x32e3de) + elseif OS == "OpenOS" then + gpu.setForeground(0x30ff80) + elseif OS == "Plan9k" then + gpu.setForeground(0xff0000) + elseif OS == "BananaOS" then + gpu.setForeground(0xffff00) + end + gpu.set(ii, i, logoLine[ii]) + end + else + if logoLine[ii] == "%" then + if OS == "MineOS" then + gpu.setForeground(0x35ffff) + elseif OS == "OpenOS" then + gpu.setForeground(0x228822) + elseif OS == "Plan9k" then + gpu.setForeground(0xff0000) + elseif OS == "BananaOS" then + gpu.setForeground(0xcccc00) + end + gpu.set(ii, i, logoLine[ii]) + elseif logoLine[ii] == "/" then + gpu.setForeground(0xfffafa) + gpu.set(ii, i, logoLine[ii]) + elseif logoLine[ii] == "#" then + gpu.setForeground(0x585858) + gpu.set(ii, i, logoLine[ii]) + elseif logoLine[ii] == "(" then + gpu.setForeground(0xc0c0c0) + gpu.set(ii, i, logoLine[ii]) + elseif string.match(logoLine[ii], "|") then + f = true + else + gpu.setForeground(0xffffff) + gpu.set(ii, i, logoLine[ii]) + end + end + end +end + +drawPalette() + +if OS == "MineOS" then + gpu.set(1, #logo + 2 > 14 and #logo + 2 or 14, 'Press any key to exit.') + local evtype + while evtype ~= 'key_down' do + evtype = computer.pullSignal() + end +else + --require("term").setCursor(1, #logo + 2 > 14 and #logo + 2 or 14) +end \ No newline at end of file diff --git a/test/boot/scripts/10_components.lua b/test/boot/scripts/10_components.lua new file mode 100644 index 0000000..6cd44c7 --- /dev/null +++ b/test/boot/scripts/10_components.lua @@ -0,0 +1,49 @@ +local _, _, status, _ = ... + +status(" *- Implementing component functions") + +function package.loaded.component.isAvailable(componentType) + if component[componentType] then + return true + end + return false +end + +function package.loaded.component.get(address, componentType) + local comp = package.loaded.component.list(componentType) + + for a,_ in comp do + if a == address then + return a + else + local s,e = string.find(a,address) + if s == 1 then + return a + end + end + end + return nil, "no such component" +end + +function package.loaded.component.getPrimary(componentType) + local prim = component[componentType] + if not prim then + error(string.format("no primary '%s' available", componentType)) + end + return prim +end + +function package.loaded.component.setPrimary(componentType, address) + if componentType == "filesystem" then + return + end + + address = package.loaded.component.get(address:sub(1,5), componentType) -- don't sub + package.loaded.component[componentType] = package.loaded.component.proxy(address) +end + +local components = package.loaded.component.list() +status(" *- Setting primary components") +for address, componentType in components do + package.loaded.component.setPrimary(componentType,address) +end \ No newline at end of file diff --git a/test/etc/motd b/test/etc/motd new file mode 100644 index 0000000..c1b1af3 --- /dev/null +++ b/test/etc/motd @@ -0,0 +1,12 @@ + + +@@@@@@@@@@@@@@@@@@@@@@@@@@@ +@ Welcome to BANANA OS. @ +@@@@@@@@@@@@@@@@@@@@@@@@@@@ + +* The world's greatest OpenComputers + operating system. No batteries included + +* It is also in beta. + +To get started, do something. diff --git a/test/etc/system/boot.lua b/test/etc/system/boot.lua new file mode 100644 index 0000000..d7f3784 --- /dev/null +++ b/test/etc/system/boot.lua @@ -0,0 +1,189 @@ +local loadfile, hcf, rom_invoke = ... + +local computer = computer +local component = component +local unicode = unicode + +local addr = computer.getBootAddress() + +Journal = {} +Journal.journal = {} +Journal.call_list = {} +function Journal.write(msg) + table.insert(Journal.journal, msg) + for i=1,#Journal.call_list do + Journal.call_list[i](msg) + end +end +function Journal.clear() + Journal.Journal = {} +end +function Journal.attach(func) + table.insert(Journal.call_list, func) +end + +local ocelot_card = component.proxy(component.list("ocelot",true)()) +Journal.attach(ocelot_card.log) + +_G._OSVERSION = "BananaOS 2.21.2025 (BETA)" + +-- first things first load up the gpu +local screen = component.list("screen", true)() +local gpu = component.list("gpu", true)() +local w,h +if gpu then + gpu = component.proxy(gpu) + if not gpu.getScreen() then + gpu.bind(screen) + end + w, h = gpu.maxResolution() + gpu.setResolution(w, h) + gpu.setBackground(0x000000) + gpu.setForeground(0xFFFFFF) + gpu.fill(1, 1, w, h, " ") +end + +gpu.cursor_y = 5 +local function banner(status_) + gpu.setBackground(0xFF0000) + gpu.setForeground(0x000000) + gpu.fill(1,1, w, 3, " ") + gpu.setBackground(0x000000) + gpu.setForeground(0xFFFFFF) + gpu.fill(2,2, w-2, 1, " ") + if not status_ then + status_ = "" + end + gpu.set(2,2, _OSVERSION .. " " .. status_) +end + +local function status(message, m2) + if gpu then + gpu.set(1,gpu.cursor_y, tostring(message)) + if gpu.cursor_y >= h then + gpu.copy(1, 2, w, h - 1, 0, -1) + else + gpu.cursor_y = gpu.cursor_y + 1 + end + end + banner(m2) +end + +gpu.cursor_x = 1 + +local function scroll(g) + g.copy(1, 2, w, h - 1, 0, -1) + g.fill(1,h,w,h," ") +end +---------------------------------------------- +function gpu.getCursor() + return {["x"]=gpu.cursor_x,["y"]=gpu.cursor_y} +end +function gpu.setCursor(x,y) + if x then + gpu.cursor_x = x + end + if y then + gpu.cursor_y = y + end + return gpu.getCursor() +end +function gpu.moveCursor(x,y) + if x then + gpu.cursor_x = gpu.cursor_x + x + end + if y then + gpu.cursor_y = gpu.cursor_y + y + end + return gpu.getCursor() +end +---------------------------------------------- +function print(...) + if gpu then + local args = table.pack(...) + for i=1,#args do + args[i] = tostring(args[i]) + for j=1, #args[i] do + local letter = args[i]:sub(j,j) + if letter == "\r" then + gpu.cursor_x = 1 + elseif letter == "\n" then + gpu.cursor_x = 1 + gpu.cursor_y = gpu.cursor_y + 1 + else + gpu.set(gpu.cursor_x,gpu.cursor_y, letter) + + gpu.cursor_x = gpu.cursor_x + 1 + + if gpu.cursor_x > w then + gpu.cursor_y = gpu.cursor_y + 1 + gpu.cursor_x = 1 + end + + if gpu.cursor_y > h then + gpu.cursor_y = gpu.cursor_y - 1 + scroll(gpu) + end + end + end + end + end +end + +status("Initializing packages", "package") +local package = loadfile("/lib/package.lua")(loadfile) + +_G.component = nil +_G.computer = nil +_G.unicode = nil +_G.package = package + +package.loaded.component = component +package.loaded.computer = computer +package.loaded.unicode = unicode +package.loaded.filesystem = loadfile("/lib/filesystem.lua")(false) + +function AlignString(String,Number,append) + if not append then + append = " " + end + String = tostring(String) + if #String > Number then + while #String > Number do + String = String:sub(1, -2) + end + String = String:sub(1, -5) .. "..." + end + if #String < Number then + while #String < Number do + String = String .. append + end + end + + return String +end + +status("Running boot scripts") + +local scripts = {} +for _, file in ipairs(rom_invoke(addr, "list", "/boot/scripts/")) do + local path = "/boot/scripts/" .. file + if not rom_invoke(addr, "isDirectory", path) then + table.insert(scripts, path) + end +end + +table.sort(scripts) + +for i = 1, #scripts do + status(" -"..scripts[i], string.format("scripts (%s/%s)",i,#scripts)) + loadfile(scripts[i])(loadfile, rom_invoke, status, banner) +end + + +status("Mounting directories", "mounting") +local fs = require("filesystem") +fs.mount(computer.getBootAddress(),"/") +if computer.tmpAddress() then + fs.mount(computer.tmpAddress(), "/tmp/") +end diff --git a/test/etc/system/system.cfg b/test/etc/system/system.cfg new file mode 100644 index 0000000..70b4e86 --- /dev/null +++ b/test/etc/system/system.cfg @@ -0,0 +1,3 @@ +{ + ["test"] = "test" +} \ No newline at end of file diff --git a/test/home/.bshrc b/test/home/.bshrc new file mode 100644 index 0000000..e69de29 diff --git a/test/home/filesystem_tests.lua b/test/home/filesystem_tests.lua new file mode 100644 index 0000000..53cd49a --- /dev/null +++ b/test/home/filesystem_tests.lua @@ -0,0 +1,24 @@ +local fs = require("filesystem") + +local tests = { + {["expected"]="/", ["value"]="/testFile"}, + {["expected"]="/tmp/",["value"]="/tmp/ggg"}, + {["expected"]="/tmp/",["value"]="/tmp/this/is/a/long/path"}, + {["expected"]="/", ["value"]="/home/tmp/tmp"}, + {["expected"]="/tmp/",["value"]="/tmp/"}, + {["expected"]=false,["value"]="mnt/"}, +} + +for i=1, #tests do + local result = fs.findNode(tests[i].value) + if result == false then + print(string.format("Failed! Expected : %s got : %s\r\n",AlignString(tests[i].expected,16),result)) + else + result = tostring(result.path) + if result == tests[i].expected then + print(string.format("Success! Expected : %s got : %s\r\n",AlignString(tests[i].expected,16),result)) + else + print(string.format("Failed! Expected : %s got : %s\r\n",AlignString(tests[i].expected,16),result)) + end + end +end \ No newline at end of file diff --git a/test/home/test.lua b/test/home/test.lua new file mode 100644 index 0000000..70877b4 --- /dev/null +++ b/test/home/test.lua @@ -0,0 +1,11 @@ +local fs = require("filesystem") + +local a = fs.open("/tmp/test.txt", "w") +fs.write(a,"error(\"awesome!\")") +fs.close(a) + +fs.makeDirectory("/tmp/awesome/") + +local a = fs.open("/tmp/awesome/abcd", "w") +fs.write(a,"error(\"what!!!\")") +fs.close(a) \ No newline at end of file diff --git a/test/init.lua b/test/init.lua new file mode 100644 index 0000000..53c7f95 --- /dev/null +++ b/test/init.lua @@ -0,0 +1,35 @@ +local addr = computer.getBootAddress() +local _component = component -- gets overridden later +local function loadfile(file) + local handle = assert(_component.invoke(addr, "open", file)) + local buffer = "" + + repeat + local data = _component.invoke(addr, "read", handle, math.maxinteger or math.huge) + buffer = buffer .. (data or "") + until not data + + _component.invoke(addr, "close", handle) + return load(buffer, "=" .. file, "bt", _G) +end + +local function hcf() + local ts = os.time() + while 1 do + if os.time() - ts >= 0.5 then + coroutine.yield() + end + end +end + + +local function rom_invoke(a, m, ...) + return _component.invoke(a, m, ...) +end + +loadfile("/etc/system/boot.lua")(loadfile, hcf, rom_invoke) +--if loadfile("/sbin/de.lua")() == 0 then -- success +-- computer.shutdown() +--else +loadfile("/sbin/bsh.lua")(loadfile) +--end \ No newline at end of file diff --git a/test/lib/event.lua b/test/lib/event.lua new file mode 100644 index 0000000..ab1489d --- /dev/null +++ b/test/lib/event.lua @@ -0,0 +1,44 @@ +_G.event = {} +local computer = require("computer") + +function event.pull(name, timeout) + local rawSignal = {} + local signals_recieved_in_mean_time = {} + local timeouts = 0 + if not timeout then + timeout = 99999999999 + end + + while rawSignal[1] ~= name do + -- pull any signal + rawSignal = table.pack(computer.pullSignal(0.1)) + + -- if no signal then add to time timeout + if rawSignal[1] == nil then + timeouts = timeouts + 0.1 + else + if rawSignal[1] ~= name then + -- not the signal, push to the signals we got in the mean time + table.insert(signals_recieved_in_mean_time,rawSignal) + else + -- got the signal + break + end + end + if timeouts >= timeout then + -- we never got the signal :( + return nil + end + end + + -- rethrow all signals... they weren't what we wanted + if #signals_recieved_in_mean_time > 0 then + for i in ipairs(signals_recieved_in_mean_time) do + computer.pushSignal(table.unpack(signals_recieved_in_mean_time[i])) + end + end + + return table.unpack(rawSignal) +end + +return event \ No newline at end of file diff --git a/test/lib/filesystem.lua b/test/lib/filesystem.lua new file mode 100644 index 0000000..c0eb38c --- /dev/null +++ b/test/lib/filesystem.lua @@ -0,0 +1,175 @@ +local debug = ... + +local filesystem = {} +local component = require("component") +local computer = require("computer") +local mounts = {} + +--false if file, true if dir +local function isFileOrDir(path) + return (string.sub(path, -1) == "/") +end +local function removeFilename(path) -- this sucks + local t = {} + local s = "" + + for i in string.gmatch(path, "[^/]+") do + table.insert(t,i) + end + + table.remove(t,#t) + + if #t == 0 then + return "/" + end + for i=1,#t do + s = s .. t[i] .. "/" + end + if s:sub(1,1) ~= "/" then + s = "/" .. s + end + + return s +end +local function log(msg,d) + if d then + if debug then + Journal.write(msg) + end + elseif not d then + Journal.write(msg) + end +end + + +-- Mount/node handling +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function filesystem.findNode(path,skipFileDirCheck) + --go through mounts + -- abc - / 1st iteration, does match + -- def - /mnt 2nd iteration, matches better + if not skipFileDirCheck then + if not isFileOrDir(path) then + path = removeFilename(path) + end + end + + local candidate = nil + local lastNum = 0 + for i=1, #mounts do + local s,e = string.find(path,mounts[i].path) + log(string.format(" [FILESYSTEM.findNode] s:%s e:%s path:%s candidate:%s lastNum:%s - tagainst %s, is id %s",s,e,path,candidate,lastNum,mounts[i].path,mounts[i].disk.address or "n/a"), true) + if e == nil then + e = 0 + end + if e > lastNum and s == 1 then + candidate = mounts[i] + lastNum = e + end + end + + if lastNum == 0 then + log(" [FILESYSTEM.findNode] failed", true) + return false, "did not find any matching" + else + log(" [FILESYSTEM.findNode] candidate has addr "..candidate.disk.address, true) + return candidate, nil, lastNum + end +end + +function filesystem.mount(disk, path) + log(string.format(" [FILESYSTEM] mounting %sas %s", AlignString(disk,7),path),false) + -- check if disk exists + + if component.get(disk, "filesystem") then + table.insert(mounts, { + ["disk"]=component.proxy(disk), + ["path"]=path + }) + return mounts[#mounts] + else + log(string.format(" [FILESYSTEM] drive does not exist", false)) + return nil, "no filesystem at address" + end +end + +-- File operations +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function filesystem.open(path, mode) + local mount,reason,lastNum = filesystem.findNode(path) + assert(mount,reason) + local id = mount.disk.open(path:sub(lastNum),mode) + assert(id, string.format("id is '%s', not a handle, when opening '%s' (actually '%s') as '%s'",id,path,path:sub(lastNum),mode)) + return {["id"]=id,["disk"]=mount} +end + +function filesystem.write(handle, data) + handle.disk.disk.write(handle.id,data) +end + +function filesystem.read(handle,count) + if count == nil then + count = math.maxinteger or math.huge + end + local buffer = "" + + repeat + local data = handle.disk.disk.read(handle.id, count) + buffer = buffer .. (data or "") + until not data + + return buffer +end + +function filesystem.close(handle) + return handle.disk.disk.close(handle.id) +end + +-- Directory operations +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function filesystem.makeDirectory(path) + local addr,reason,lastNum = filesystem.findNode(path,true) + assert(addr, reason) + + return addr.disk.makeDirectory(path:sub(lastNum)) +end + +-- Misc +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function filesystem.exists(path) + local addr,reason,lastNum = filesystem.findNode(path) + assert(addr, reason) + + if addr.disk.isDirectory(path:sub(lastNum)) or + addr.disk.exists(path:sub(lastNum)) then + return true + end + return false +end + +function filesystem.loadfile(path, workspace) + local handle = filesystem.open(path, "r") + local data = filesystem.read(handle) + filesystem.close(handle) + if workspace == nil then + return load(data, "=" .. path, "bt", _G) + else + return load(data, "=" .. path, "bt", workspace) + end +end + +function filesystem.getlisting(path) -- handle.disk.files[path] + local mount,reason,lastNum = filesystem.findNode(path) + assert(mount, reason) + return mount.disk.list(path:sub(lastNum)) +end + +return filesystem \ No newline at end of file diff --git a/test/lib/filesystem.lua.old b/test/lib/filesystem.lua.old new file mode 100644 index 0000000..22c731a --- /dev/null +++ b/test/lib/filesystem.lua.old @@ -0,0 +1,223 @@ +local debug = ... + +local filesystem = {} +local component = require("component") +local computer = require("computer") +local mounts = {} + +--false if file, true if dir +local function isFileOrDir(path) + return (string.sub(path, -1) == "/") +end +local function removeFilename(path) -- this sucks + local t = {} + local s = "" + + for i in string.gmatch(path, "[^/]+") do + table.insert(t,i) + end + + table.remove(t,#t) + + if #t == 0 then + return "/" + end + for i=1,#t do + s = s .. t[i] .. "/" + end + if s:sub(1,1) ~= "/" then + s = "/" .. s + end + + return s +end +local function log(msg,d) + if d then + if debug then + Journal.write(msg) + end + elseif not d then + Journal.write(msg) + end +end + + +-- Mount/node handling +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function filesystem.findNode(path) + --go through mounts + -- abc - / 1st iteration, does match + -- def - /mnt 2nd iteration, matches better + + if not isFileOrDir(path) then + path = removeFilename(path) + end + + local candidate = nil + local lastNum = 0 + for i=1, #mounts do + local s,e = string.find(path,mounts[i].path) + log(string.format(" [FILESYSTEM.findNode] s:%s e:%s path:%s candidate:%s lastNum:%s - tagainst %s, is id %s",s,e,path,candidate,lastNum,mounts[i].path,mounts[i].disk.address or "n/a"), true) + if e == nil then + e = 0 + end + if e > lastNum and s == 1 then + candidate = mounts[i] + lastNum = e + end + end + + if lastNum == 0 then + log(" [FILESYSTEM.findNode] failed", true) + return false, "did not find any matching" + else + log(" [FILESYSTEM.findNode] candidate has addr "..candidate.disk.address, true) + return candidate + end +end + +function filesystem.mount(disk, path) + log(string.format(" [FILESYSTEM] mounting %sas %s", AlignString(disk,7),path),false) + + if disk == nil then + table.insert(mounts, { + ["disk"]={["address"]="n/a"}, + ["path"]=path, + ["isVirtual"]=true, + ["files"]={}, -- {[path] = data} + ["handles"]={} + }) + log(" [FILESYSTEM] drive is virtual") + return mounts + end + + -- check if disk exists + + if component.get(disk, "filesystem") then + table.insert(mounts, { + ["disk"]=component.proxy(disk), + ["path"]=path, + ["isVirtual"]=false + }) + return mounts[#mounts] + else + log(string.format(" [FILESYSTEM] drive does not exist", false)) + return nil, "no filesystem at address" + end +end + +-- File operations +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function filesystem.open(path, mode) + local mount, reason = filesystem.findNode(path) + assert(mount,reason) + if mount.isVirtual then + table.insert(mount.handles,{ + ["path"] = path, + ["mode"] = mode, + ["ptr"] = 1 + }) + return {["id"]=#mount.handles,["disk"]=mount} + else --untested + local id = mount.disk.open(path,mode) + assert(id, string.format("id is '%s', not a handle, when opening '%s' as '%s'",id,path,mode)) + return {["id"]=id,["disk"]=mount} + end +end +function filesystem.write(handle, data) + if handle.disk.isVirtual then + local mode = handle.disk.handles[handle.id].mode + local path = handle.disk.handles[handle.id].path + if mode == "a" or mode == "ab" then + error("not implemented") + elseif mode == "w" or mode == "wb" then + handle.disk.files[path] = data + return + else + error(string.format("mode '%s' not understood",mode)) + end + else + handle.disk.disk.write(handle.id,data) + end +end + +function filesystem.read(handle,count) + if count == nil then + count = math.maxinteger or math.huge + end + if handle.disk.isVirtual then + local d = handle.disk + local a = d.files[d.handles[handle.id].path] + :sub(d.handles[handle.id].ptr,d.handles[handle.id].ptr+count-1) + d.handles[handle.id].ptr = d.handles[handle.id].ptr +count + return a + else --untested + local buffer = "" + + repeat + local data = handle.disk.disk.read(handle.id, count) + buffer = buffer .. (data or "") + until not data + + return buffer + end +end + +function filesystem.close(handle) + if handle.disk.isVirtual then + handle.disk.handles[handle.id] = nil + return + else --untested + return handle.disk.disk.close(handle.id) + end +end + + +-- Misc +---------------------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function filesystem.exists(path) -- weird, doesnt w + local addr = filesystem.findNode(path) + if addr == nil then + return false + end + if addr.disk.isDirectory(path) or + addr.disk.exists(path) then + return true + end + return false +end + +function filesystem.loadfile(path, workspace) + local handle = filesystem.open(path, "r") + local data = filesystem.read(handle) + filesystem.close(handle) + if workspace == nil then + return load(data, "=" .. path, "bt", _G) + else + return load(data, "=" .. path, "bt", workspace) + end +end + +function filesystem.getlisting(path) -- handle.disk.files[path] + local mount, reason = filesystem.findNode(path) + assert(mount, reason) + if mount.isVirtual then + local a = {} + print(#mount.files) + for b=1, #mount.files do + error(b) + table.insert(a, mount.files[b]:gmatch("[^/]+")) + end + print(a[1]) + else + return mount.disk.list(path) + end +end + +return filesystem \ No newline at end of file diff --git a/test/lib/package.lua b/test/lib/package.lua new file mode 100644 index 0000000..ffdefa6 --- /dev/null +++ b/test/lib/package.lua @@ -0,0 +1,40 @@ +local loadfile = ... +local package = {} + +local loaded = { + ["_G"] = _G, + ["bit32"] = bit32, + ["coroutine"] = coroutine, + ["math"] = math, + ["os"] = os, + ["package"] = package, + ["string"] = string, + ["table"] = table, +} +local pkg_path = "/lib/?.lua" +package.loaded = loaded + +function package.search(file,path) + for path in path:gmatch("[^;]+") do + local possible = path:gsub("%?", file) + local s, _ = pcall(loadfile, possible) + if s then + return possible + end + end + return false +end + +function require(module) + if package.loaded[module] then + return package.loaded[module] + else + local path = package.search(module,pkg_path) + assert(path, "module not found") + local mod = loadfile(path)() + package.loaded[module] = mod + return mod + end +end + +return package \ No newline at end of file diff --git a/test/sbin/bsh.lua b/test/sbin/bsh.lua new file mode 100644 index 0000000..1c8dc6a --- /dev/null +++ b/test/sbin/bsh.lua @@ -0,0 +1,204 @@ +local bsh = {} +local component = require("component") +local computer = require("computer") +local event = require("event") +local fs = require("filesystem") +local package = require("package") +_G.ExecPath = "/sbin/?.lua;/bin/?.lua" + +if fs.exists("/etc/motd") then + local motd_h = fs.open("/etc/motd", "r") + print(fs.read(motd_h)) + fs.close(motd_h) +end + +print("\n") + +-- BUG: when in /bin directory, running something tries to run /binhello.lua +-- FEAT: autofill slashes, and .lua +-- FEAT: handle .. + +bsh.running = true +local cwd = "/home/" + +local function keyin(timeout) + while true do + local _,_,ascii,code,_ = event.pull("key_down",timeout) + return ascii,code + end +end + +function bsh.split(str) + local words = {} + for word in str:gmatch("%S+") do + table.insert(words, word) + end + return words + end + +function bsh.is_in_arr(arr,elem) + for i in ipairs(arr) do + if tostring(arr[i]) == tostring(elem) then + return true + end + end + + return false +end + +function bsh.command(buffer) + if #buffer == 0 then + return + end + local command = bsh.split(buffer) + if command[1] == "ls" then + local cwdls + if command[2] == nil then + cwdls = fs.getlisting(cwd) + else + cwdls = fs.getlisting(command[2]) + end + if cwdls ~= nil then + for i = 1, #cwdls do + print(cwdls[i] .. " ") + end + else + print("(Nothing here!)") + end + print("\r\n") + elseif command[1] == "cd" then + if command[2] == nil then + error("no argument") + else + cwd = command[2] + end + elseif command[1] == "clear" then + local w,h=component.gpu.getResolution() + component.gpu.fill(1,1,w,h," ") + component.gpu.setCursor(2,1) + elseif command[1] == "exit" then + bsh.running = false + else -- execute file, this sucks tho + local location = command[1] + local s,_ = pcall(fs.loadfile,location) + -- is in cwd? + if not s then + if bsh.is_in_arr(fs.getlisting(cwd),command[1]) then + location = cwd .. command[1] + else + -- no, check path + local a = package.search(command[1],ExecPath) + if a ~= false then + location = a + else + error("command not found") + end + end + end + + local a = fs.loadfile(location) + a(command) + end +end + +local function prompt() + print(cwd .." #> ") +end + +print("bsh v3\n") + +local buffer = "" +local gpu = component.gpu + +prompt() + +local cursor = "█" +local cursorOnOff = 1 +local history = {} +local history_index = 1 + +while bsh.running do + local nextkeycode, code = keyin() + local charAtCurLocation = " " + local cur = gpu.getCursor() + --print(nextkeycode,"=",code," ") + + if nextkeycode == nil and code == nil then + -- timeout reached, invert cursor + cursorOnOff = cursorOnOff * -1 + if cursorOnOff == 1 then + charAtCurLocation = gpu.get(cur.x,cur.y) + gpu.set(cur.x,cur.y,cursor) + else + gpu.set(cur.x,cur.y,charAtCurLocation) + end + end + + if nextkeycode == 0 or nextkeycode == nil then + local preBuffer=buffer + if code == 42 then -- shift + -- ignore + elseif code == 200 then -- up + if history[history_index] then + buffer = history[history_index] + history_index = history_index - 1 + gpu.setCursor(1) + prompt() + for i=1,#preBuffer do print(" ") end + gpu.setCursor(1) + prompt() + print(buffer) + end + elseif code == 208 then -- down + if history[history_index+1] then + history_index = history_index + 2 + buffer = history[history_index] + gpu.setCursor(1) + prompt() + for i=1,#preBuffer do print(" ") end + gpu.setCursor(1) + prompt() + print(buffer) + end + end + else + gpu.set(cur.x,cur.y,charAtCurLocation) + local nextkey = string.char(nextkeycode) + + if nextkeycode == 13 then --enter + table.insert(history, buffer) + history_index = #history + if cursorOnOff ~= 1 then + gpu.set(cur.x,cur.y,charAtCurLocation) + end + print("\n") + local success, reason = xpcall(bsh.command, function(a) + return {a, debug.traceback()} + end, buffer) + + if not success then + if reason then + print(reason[1].."\n\n") + component.gpu.setForeground(0xff3333) + print(reason[2]) + component.gpu.setForeground(0xffffff) + end + end + + buffer = "" + nextkeycode = nil + print("\n") + prompt() + elseif nextkeycode == 8 then --backsp + if #buffer ~= 0 then + buffer = buffer:sub(1, -2) + gpu.moveCursor(-1) + print(" ") + gpu.moveCursor(-1) + end + else + buffer = buffer .. nextkey + print(nextkey) + end + end +end \ No newline at end of file diff --git a/test/sbin/de.lua b/test/sbin/de.lua new file mode 100644 index 0000000..e2333c9 --- /dev/null +++ b/test/sbin/de.lua @@ -0,0 +1,133 @@ +local component = require("component") +local computer = require("computer") +local fs = require("filesystem") +local event = require("event") + +-- get GPU +local gpu = component.proxy(component.list("gpu", true)()) + +if gpu == nil then + return -1 +end + + +local resolution = {} +resolution.w, resolution.h = gpu.maxResolution() +local bg_color = 0xa2cffe +local touch_listeners = {} -- {x start,y start,x end,y end,function to call, with args} +local windows = {} -- {WINDOW_NAME,X,Y,WIDTH,HEIGHT} +-- local + +gpu.setResolution(resolution.w, resolution.h) + +local function add_touch_listener(xs,ys,xe,ye,call,args) + table.insert(touch_listeners, {xs,ys,xe,ye,call,args}) + return #touch_listeners +end + + +local function draw_nav() + gpu.setBackground(0xffffff) + gpu.setForeground(0xffffff) + + gpu.fill(1, 1, resolution.w, 1, " ") + + gpu.setForeground(0x000000) + gpu.setBackground(0xffffff) + + gpu.set(2,1, "@") + local totalmem = computer.totalMemory() + local freemem = computer.freeMemory() + gpu.set(6,1, tostring(totalmem-freemem) .. " b / " .. tostring(totalmem) .. " b") + + gpu.setBackground(bg_color) +end + +local function draw_bg() + gpu.setBackground(bg_color) + gpu.fill(1, 2, resolution.w, resolution.h, " ") +end + +local function draw_windows() + for i = 1, #windows do + gpu.setBackground(0x000000) + gpu.setForeground(0xffffff) + gpu.fill( + windows[i][2], + windows[i][3]-1, + windows[i][2]+windows[i][4], + windows[i][3]-1, + " " + ) + gpu.set(windows[i][2], windows[i][3]-1, "X | " .. windows[i][1]) + gpu.setBackground(0xaaaaaa) + gpu.fill( + windows[i][2], + windows[i][3], + windows[i][2]+windows[i][4], + windows[i][3]+windows[i][5], + " " + ) + end +end + +local function keyin() + return event.pull("key_down") +end + + +local function open_exec_menu(args) + gpu.setBackground(0xdadada) + gpu.fill(1,3,resolution.w, 1, " ") + gpu.setBackground(bg_color) + local grab = true + local buffer = "" + local curr_loc_x = 1 + while grab do + local _,_,next_key = keyin() + if next_key == 13 then + local result = table.pack(pcall(fs.dofile, buffer)) + if not result[1] then + print(result[2] .. "\n") + end + buffer = "" + curr_loc_x = 1 + grab = false + elseif next_key == 8 then + if #buffer ~= 0 then + buffer = buffer:sub(1, -2) + gpu.set(curr_loc_x-1, 2, " ") + curr_loc_x = curr_loc_x - 1 + end + else + buffer = buffer .. string.char(next_key) + gpu.set(curr_loc_x, 2, string.char(next_key)) + curr_loc_x = curr_loc_x + 1 + end + end + gpu.setBackground(bg_color) + gpu.fill(1,2,resolution.w, 3, " ") +end + + +draw_bg() +draw_nav() + + +add_touch_listener(2, 1, 2, 1, open_exec_menu, {}) + +while true do + local event,uuid,x,y = computer.pullSignal(0.3) + if event == "touch" then + for i = 1, #touch_listeners do + if touch_listeners[i][1] <= x and x <= touch_listeners[i][3] then + if touch_listeners[i][2] <= y and y <= touch_listeners[i][4] then + touch_listeners[i][5](touch_listeners[i][6]) + end + end + end + end + + draw_nav() + draw_windows() +end \ No newline at end of file diff --git a/test/sbin/free.lua b/test/sbin/free.lua new file mode 100644 index 0000000..91157cc --- /dev/null +++ b/test/sbin/free.lua @@ -0,0 +1,4 @@ +local computer = require("computer") +local free = computer.freeMemory() +local total = computer.totalMemory() +print(string.format("Total%12d\nUsed%13d\nFree%13d\n", total, total - free, free)) diff --git a/test/sbin/lspci.lua b/test/sbin/lspci.lua new file mode 100644 index 0000000..e079ae4 --- /dev/null +++ b/test/sbin/lspci.lua @@ -0,0 +1,15 @@ +local computer = require("computer") + + +local function getModel(desc) + local name + for _, dev in pairs(computer.getDeviceInfo()) do + if dev.description == desc then + name = dev.product + break + end + end + return name +end + +print(getModel("APU")) \ No newline at end of file