From 109c0beffb4ffa576615b34dd7b5bfe347a51cf2 Mon Sep 17 00:00:00 2001 From: TheWahlolly Date: Sun, 27 Apr 2025 10:13:12 +0300 Subject: [PATCH] v0.7.0 - Added basic CLI tools: ls, cd, cp, mv, rm, lua --- halyde/apps/cat.lua | 17 ++++++ halyde/apps/cd.lua | 26 +++++++++ halyde/apps/clear.lua | 2 + halyde/apps/cp.lua | 25 +++++++++ halyde/apps/fetch.lua | 1 + halyde/apps/helpdb/cat.txt | 1 + halyde/apps/ls.lua | 70 +++++++++++++++++++++++ halyde/apps/lua.lua | 19 +++++++ halyde/apps/mv.lua | 25 +++++++++ halyde/apps/rm.lua | 12 ++++ halyde/config/shell.cfg | 12 +++- halyde/core/boot.lua | 2 +- halyde/core/cormgr.lua | 2 +- halyde/core/datatools.lua | 25 ++++++++- halyde/core/shell.lua | 17 +++--- halyde/{lib => core}/termlib.lua | 1 + halyde/lib/component.lua | 9 ++- halyde/lib/filesystem.lua | 95 +++++++++++++++++++++++++++++++- init.lua | 2 +- 19 files changed, 347 insertions(+), 16 deletions(-) create mode 100644 halyde/apps/cat.lua create mode 100644 halyde/apps/cd.lua create mode 100644 halyde/apps/clear.lua create mode 100644 halyde/apps/cp.lua create mode 100644 halyde/apps/fetch.lua create mode 100644 halyde/apps/helpdb/cat.txt create mode 100644 halyde/apps/ls.lua create mode 100644 halyde/apps/lua.lua create mode 100644 halyde/apps/mv.lua create mode 100644 halyde/apps/rm.lua rename halyde/{lib => core}/termlib.lua (99%) diff --git a/halyde/apps/cat.lua b/halyde/apps/cat.lua new file mode 100644 index 0000000..3caffd3 --- /dev/null +++ b/halyde/apps/cat.lua @@ -0,0 +1,17 @@ +local args = {...} +local file = args[1] +args = nil +local fs = import("filesystem") + +if file:sub(1, 1) ~= "/" then + file = shell.workingDirectory .. file +end +if not fs.exists(file) then + print("\27[91mFile does not exist.") +end +local handle = fs.open(file, "r") +local data +repeat + data = handle:read(math.huge or math.maxinteger) + print(data, false) +until not data diff --git a/halyde/apps/cd.lua b/halyde/apps/cd.lua new file mode 100644 index 0000000..a4c08b3 --- /dev/null +++ b/halyde/apps/cd.lua @@ -0,0 +1,26 @@ +local args = {...} +local directory = args[1] +args = nil +local fs = import("filesystem") + +if directory == ".." then + local backDirectory = shell.workingDirectory:match("(.+)/.-/") + if backDirectory then + backDirectory = backDirectory .. "/" + else + backDirectory = "/" + end + shell.workingDirectory = backDirectory +else + if directory:sub(-1, -1) ~= "/" then + directory = directory .. "/" + end + if directory:sub(1, 1) ~= "/" then + directory = shell.workingDirectory .. directory + end + if fs.exists(directory) and fs.isDirectory(directory) or fs.exists(shell.workingDirectory .. directory) and fs.isDirectory(shell.workingDirectory .. directory) then + shell.workingDirectory = directory + else + print("error: no such directory") + end +end diff --git a/halyde/apps/clear.lua b/halyde/apps/clear.lua new file mode 100644 index 0000000..12b9b4e --- /dev/null +++ b/halyde/apps/clear.lua @@ -0,0 +1,2 @@ +clear() +-- truly so much going on here diff --git a/halyde/apps/cp.lua b/halyde/apps/cp.lua new file mode 100644 index 0000000..f9e1ac4 --- /dev/null +++ b/halyde/apps/cp.lua @@ -0,0 +1,25 @@ +local args = {...} +local fromFile, toFile = args[1], args[2] +args = nil +local fs = import("filesystem") + +if fromFile:sub(1, 1) ~= "/" then + fromFile = shell.workingDirectory .. fromFile +end +if toFile:sub(1, 1) ~= "/" then + toFile = shell.workingDirectory .. toFile +end +if fromFile == toFile then + print("\27[91mSource and destination are the same.") +end +if not fs.exists(fromFile) then + print("\27[91mSource file does not exist.") +end +if fs.exists(toFile) then + print("Destination file already exists. Overwrite it? [Y/n] ", false) + if read():lower() == "n" then + print("Aborted.") + return + end +end +fs.copy(fromFile, toFile) diff --git a/halyde/apps/fetch.lua b/halyde/apps/fetch.lua new file mode 100644 index 0000000..b2279bb --- /dev/null +++ b/halyde/apps/fetch.lua @@ -0,0 +1 @@ +print("\27[40m \27[41m \27[42m \27[43m \27[44m \27[45m \27[46m \27[47m \n\27[100m \27[101m \27[102m \27[103m \27[104m \27[105m \27[106m \27[107m ") diff --git a/halyde/apps/helpdb/cat.txt b/halyde/apps/helpdb/cat.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/halyde/apps/helpdb/cat.txt @@ -0,0 +1 @@ + diff --git a/halyde/apps/ls.lua b/halyde/apps/ls.lua new file mode 100644 index 0000000..97fd62d --- /dev/null +++ b/halyde/apps/ls.lua @@ -0,0 +1,70 @@ +local args = {...} +local target = args[1] +args = nil +local fs = import("filesystem") +local maxLength = 0 +local margin = 2 -- minimum space between filename and size +local dirTable = {} +local fileTable = {} + +if target then + if target:sub(1, 1) ~= "/" then + target = shell.workingDirectory .. target + end + if target:sub(-1, -1) ~= "/" then + target = target .. "/" + end +else + target = shell.workingDirectory +end + +local files = fs.list(target) + +for _, file in pairs(files) do + if file:sub(-1, -1) == "/" then + table.insert(dirTable, file) + file = file:sub(1, -2) + else + table.insert(fileTable, file) + end + if unicode.wlen(file) > maxLength then + maxLength = unicode.wlen(file) + end +end +table.sort(dirTable) +table.sort(fileTable) +files = {} +for _, v in ipairs(dirTable) do + table.insert(files, v) +end +for _, v in ipairs(fileTable) do + table.insert(files, v) +end +dirTable, fileTable = nil, nil +for _, file in ipairs(files) do + local dir = false + local filetext + if file:sub(-1, -1) == "/" then -- i think this is a more efficient way to check if it's a directory + dir = true + filetext = "\27[93m"..file:sub(1, -2) + elseif file:find(".") and file:match("[^.]+$") == "lua" then + filetext = "\27[92m"..file + end + filetext = (filetext or file)..string.rep(" ", maxLength - unicode.wlen(file) + margin) + if dir then + print(filetext.." \27[0m[DIR]") + else + local size = fs.size(target .. file) + local sizeString + if convert(size, "B", "GiB") >= 1 then + sizeString = tostring(math.floor(convert(size, "B", "GiB") * 100 + 0.5) / 100).." GiB" + elseif convert(size, "B", "MiB") >= 1 then + sizeString = tostring(math.floor(convert(size, "B", "MiB") * 100 + 0.5) / 100).." MiB" + elseif convert(size, "B", "KiB") >= 1 then + sizeString = tostring(math.floor(convert(size, "B", "KiB") * 100 + 0.5) / 100).." KiB" + else + sizeString = tostring(size).." B" + end + print(filetext.."\27[0m"..sizeString) + end +end diff --git a/halyde/apps/lua.lua b/halyde/apps/lua.lua new file mode 100644 index 0000000..d503be3 --- /dev/null +++ b/halyde/apps/lua.lua @@ -0,0 +1,19 @@ +print("\27[44m".._VERSION.."\27[0m shell") +print('Type "exit" to exit.') +while true do + print("\27[44mlua>\27[0m ", false) + local command = read() + if command == "exit" then + return + else + local function runCommand() + assert(load(command))() + end + local result, reason = xpcall(runCommand, function(errMsg) + return errMsg .. "\n\n" .. debug.traceback() + end) + if not result then + print("\27[91m" .. reason) + end + end +end diff --git a/halyde/apps/mv.lua b/halyde/apps/mv.lua new file mode 100644 index 0000000..ba8b51e --- /dev/null +++ b/halyde/apps/mv.lua @@ -0,0 +1,25 @@ +local args = {...} +local fromFile, toFile = args[1], args[2] +args = nil +local fs = import("filesystem") + +if fromFile:sub(1, 1) ~= "/" then + fromFile = shell.workingDirectory .. fromFile +end +if toFile:sub(1, 1) ~= "/" then + toFile = shell.workingDirectory .. toFile +end +if fromFile == toFile then + print("\27[91mSource and destination are the same.") +end +if not fs.exists(fromFile) then + print("\27[91mSource file does not exist.") +end +if fs.exists(toFile) then + print("Destination file already exists. Overwrite it? [Y/n] ", false) + if read():lower() == "n" then + print("Aborted.") + return + end +end +fs.rename(fromFile, toFile) diff --git a/halyde/apps/rm.lua b/halyde/apps/rm.lua new file mode 100644 index 0000000..a484664 --- /dev/null +++ b/halyde/apps/rm.lua @@ -0,0 +1,12 @@ +local args = {...} +local file = args[1] +args = nil +local fs = import("filesystem") + +if file:sub(1, 1) ~= "/" then + file = shell.workingDirectory .. file +end +if not fs.exists(file) then + print("\27[91mFile does not exist.") +end +fs.remove(file) diff --git a/halyde/config/shell.cfg b/halyde/config/shell.cfg index e62ce6b..6eebc8b 100644 --- a/halyde/config/shell.cfg +++ b/halyde/config/shell.cfg @@ -2,7 +2,17 @@ local shellcfg = { ["startupMessage"] = "\n │\n │ ".._OSVERSION.." running on ".._VERSION..'\n │ Welcome! Type "help" to get started.\n │\n ', -- message shown on startup ["prompt"] = "\x1b[92m%s > \x1b[0m", -- shell prompt, %s will be replaced with working directory. example: "%s > " turns to "/current/working/directory > " ["path"] = { -- default locations where programs will be run from - "/halyde/apps/"} + "/halyde/apps/" + }, ["aliases"] = { -- shell command aliases + ["copy"] = "cp", + ["move"] = "mv", + ["rename"] = "mv", + ["ren"] = "mv", + ["dir"] = "ls", + ["man"] = "help", + ["del"] = "rm", + [".."] = "cd .." + }, ["defaultWorkingDirectory"] = "/home/" -- the working directory that gets set when halyde starts } return shellcfg diff --git a/halyde/core/boot.lua b/halyde/core/boot.lua index da01491..1f97c68 100644 --- a/halyde/core/boot.lua +++ b/halyde/core/boot.lua @@ -1,7 +1,7 @@ local loadfile = ... local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile) -_G._OSVERSION = "Halyde 0.6.0" +_G._OSVERSION = "Halyde 0.7.0" function _G.import(module, ...) local args = table.pack(...) diff --git a/halyde/core/cormgr.lua b/halyde/core/cormgr.lua index 053fab0..3f7e4f2 100644 --- a/halyde/core/cormgr.lua +++ b/halyde/core/cormgr.lua @@ -28,7 +28,7 @@ local function runCoroutines() end if coroutine.status(_G.cormgr.corList[i]) == "dead" then table.remove(_G.cormgr.corList, i) - break + i = i - 1 end --computer.pullSignal(0) --coroutine.yield() diff --git a/halyde/core/datatools.lua b/halyde/core/datatools.lua index e958ed5..e56d591 100644 --- a/halyde/core/datatools.lua +++ b/halyde/core/datatools.lua @@ -1,7 +1,21 @@ +local conversionTables = { + ["bytes"] = { + ["B"] = 1, + ["KB"] = 1000, + ["MB"] = 1000000, + ["GB"] = 1000000000 + }, ["bibytes"] = { + ["B"] = 1, + ["KiB"] = 1024, + ["MiB"] = 1048576, + ["GiB"] = 1073741824 + } +} + function table.find(table, item) for k, v in pairs(table) do if v == item then - return(v) + return k end end end @@ -20,3 +34,12 @@ function table.copy(orig) end return copy end + +function convert(amount, fromUnit, toUnit) + for _, convTable in pairs(conversionTables) do + if convTable[toUnit] then + return amount / convTable[toUnit] * convTable[fromUnit] + end + end + return false, "unit does not exist" +end diff --git a/halyde/core/shell.lua b/halyde/core/shell.lua index d2c6afa..6f78ef3 100644 --- a/halyde/core/shell.lua +++ b/halyde/core/shell.lua @@ -1,15 +1,19 @@ local shellcfg = import("/halyde/config/shell.cfg") -import("termlib") +import("/halyde/core/termlib.lua") local event = import("event") -local ocelot = component.proxy(component.list("ocelot")()) +--local ocelot = component.proxy(component.list("ocelot")()) local filesystem = import("filesystem") _G.shell = {} -_G.shell.workingDirectory = "/" +_G.shell.workingDirectory = shellcfg["defaultWorkingDirectory"] +_G.shell.aliases = shellcfg["aliases"] local function parseCommand(command) - checkArg(1, command, "string") - local gm, result, args, trimmedCommand = command:gmatch('[^ ]+'), nil, {}, command + if shell.aliases[command:match("[^ ]+")] then + local _, cmdend = command:find("[^ ]+") + command = shell.aliases[command:match("[^ ]+")] .. command:sub(cmdend + 1) + end + local gm, result, args, trimmedCommand = command:gmatch("[^ ]+"), nil, {}, command while true do result = gm() if not result then @@ -63,7 +67,7 @@ local function parseCommand(command) end end if not foundfile then - print("no such file or command: "..args[1]) + print("No such file or command: "..args[1]) end end @@ -75,6 +79,5 @@ while true do -- termlib.cursorPosX = #(shell.workingDirectory .. " > ") -- termlib.cursorPosY = termlib.cursorPosY - 1 local shellCommand = read() - ocelot.log("parsing "..shellCommand) parseCommand(shellCommand) end diff --git a/halyde/lib/termlib.lua b/halyde/core/termlib.lua similarity index 99% rename from halyde/lib/termlib.lua rename to halyde/core/termlib.lua index 9fe81ff..441f327 100644 --- a/halyde/lib/termlib.lua +++ b/halyde/core/termlib.lua @@ -73,6 +73,7 @@ function _G.print(text,endNewLine) if not text or not tostring(text) then return end + text = "\27[0m" .. text:gsub("\t", " ") text = tostring(text) readBreak = 0 -- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character. diff --git a/halyde/lib/component.lua b/halyde/lib/component.lua index 5a35e8f..b9c7e55 100644 --- a/halyde/lib/component.lua +++ b/halyde/lib/component.lua @@ -1,4 +1,9 @@ -local componentlib = {} +local componentlib +if table.copy then + componentlib = table.copy(component) +else + componentlib = {} +end function componentlib.get(address) checkArg(1, address, "string") @@ -14,4 +19,4 @@ end componentlib.invoke = component.invoke -return(componentlib) \ No newline at end of file +return(componentlib) diff --git a/halyde/lib/filesystem.lua b/halyde/lib/filesystem.lua index 6dfa5fc..a5c2b72 100644 --- a/halyde/lib/filesystem.lua +++ b/halyde/lib/filesystem.lua @@ -3,7 +3,7 @@ local component if loadfile then component = loadfile("/halyde/lib/component.lua")(loadfile) -else +elseif import then component = import("component") end @@ -67,11 +67,102 @@ function filesystem.open(path, mode) -- opens a file and returns its handle end function filesystem.list(path) + checkArg(1, path, "string") + if path == "/mnt/" then + -- list drives + local returnTable = {} + for address, _ in component.list("filesystem") do + table.insert(returnTable, address:sub(1, 3) .. "/") + end + return returnTable + else + local address, absPath = filesystem.processPath(path) + if not address then + return false + end + return component.invoke(address, "list", absPath) + end +end + +function filesystem.size(path) checkArg(1, path, "string") local address, absPath = filesystem.processPath(path) if not address then return false end - return component.invoke(address, "list", absPath) + return component.invoke(address, "size", absPath) end + +function filesystem.isDirectory(path) + checkArg(1, path, "string") + local address, absPath = filesystem.processPath(path) + if not address then + return false + end + return component.invoke(address, "isDirectory", absPath) +end + +function filesystem.rename(fromPath, toPath) + checkArg(1, fromPath, "string") + checkArg(2, toPath, "string") + local fromAddress, fromAbsPath = filesystem.processPath(fromPath) + local toAddress, toAbsPath = filesystem.processPath(toPath) + if not fromAddress or not toAddress then + return false + end + if fromAddress == toAddress then + return component.invoke(fromAddress, "rename", fromAbsPath, toAbsPath) + else + local handle = component.invoke(fromAddress, "open", fromAbsPath, "r") + local data, tmpdata + repeat + tmpdata = component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger) + data = data .. (tmpdata or "") + until not tmpdata + tmpdata = component.invoke(fromAddress, "close", handle) + local handle = component.invoke(toAddress, "open", toAbsPath, "w") + component.invoke(toAddress, "write", handle, data) + component.invoke(toAddress, "close", handle) + component.invoke(fromAddress, "remove", fromAbsPath) + end +end + +function filesystem.copy(fromPath, toPath) + checkArg(1, fromPath, "string") + checkArg(2, toPath, "string") + local fromAddress, fromAbsPath = filesystem.processPath(fromPath) + local toAddress, toAbsPath = filesystem.processPath(toPath) + if not fromAddress or not toAddress then + return false + end + local handle = component.invoke(fromAddress, "open", fromAbsPath, "r") + local data, tmpdata = "", nil + repeat + tmpdata = component.invoke(fromAddress, "read", handle, math.huge or math.maxinteger) + data = data .. (tmpdata or "") + until not tmpdata + tmpdata = component.invoke(fromAddress, "close", handle) + local handle = component.invoke(toAddress, "open", toAbsPath, "w") + component.invoke(toAddress, "write", handle, data) + component.invoke(toAddress, "close", handle) +end + +function filesystem.isDirectory(path) + checkArg(1, path, "string") + local address, absPath = filesystem.processPath(path) + if not address then + return false + end + return component.invoke(address, "isDirectory", absPath) +end + +function filesystem.remove(path) + checkArg(1, path, "string") + local address, absPath = filesystem.processPath(path) + if not address then + return false + end + return component.invoke(address, "remove", absPath) +end + return(filesystem) diff --git a/init.lua b/init.lua index 7232f03..12f2b76 100644 --- a/init.lua +++ b/init.lua @@ -10,7 +10,7 @@ local function loadfile(file) data = data .. (tmpdata or "") until not tmpdata component.invoke(computer.getBootAddress(), "close", handle) - return(assert(load(data, "=" .. file))) + return assert(load(data, "=" .. file)) end local function handleError(errorMessage)