v0.7.0 - Added basic CLI tools: ls, cd, cp, mv, rm, lua

This commit is contained in:
TheWahlolly
2025-04-27 10:13:12 +03:00
parent 64a761fdde
commit 109c0beffb
19 changed files with 347 additions and 16 deletions
+17
View File
@@ -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
+26
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
clear()
-- truly so much going on here
+25
View File
@@ -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)
+1
View File
@@ -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 ")
+1
View File
@@ -0,0 +1 @@
+70
View File
@@ -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
+19
View File
@@ -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
+25
View File
@@ -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)
+12
View File
@@ -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)
+11 -1
View File
@@ -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 ["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 > " ["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 ["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 return shellcfg
+1 -1
View File
@@ -1,7 +1,7 @@
local loadfile = ... local loadfile = ...
local filesystem = loadfile("/halyde/lib/filesystem.lua")(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, ...) function _G.import(module, ...)
local args = table.pack(...) local args = table.pack(...)
+1 -1
View File
@@ -28,7 +28,7 @@ local function runCoroutines()
end end
if coroutine.status(_G.cormgr.corList[i]) == "dead" then if coroutine.status(_G.cormgr.corList[i]) == "dead" then
table.remove(_G.cormgr.corList, i) table.remove(_G.cormgr.corList, i)
break i = i - 1
end end
--computer.pullSignal(0) --computer.pullSignal(0)
--coroutine.yield() --coroutine.yield()
+24 -1
View File
@@ -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) function table.find(table, item)
for k, v in pairs(table) do for k, v in pairs(table) do
if v == item then if v == item then
return(v) return k
end end
end end
end end
@@ -20,3 +34,12 @@ function table.copy(orig)
end end
return copy return copy
end 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
+10 -7
View File
@@ -1,15 +1,19 @@
local shellcfg = import("/halyde/config/shell.cfg") local shellcfg = import("/halyde/config/shell.cfg")
import("termlib") import("/halyde/core/termlib.lua")
local event = import("event") local event = import("event")
local ocelot = component.proxy(component.list("ocelot")()) --local ocelot = component.proxy(component.list("ocelot")())
local filesystem = import("filesystem") local filesystem = import("filesystem")
_G.shell = {} _G.shell = {}
_G.shell.workingDirectory = "/" _G.shell.workingDirectory = shellcfg["defaultWorkingDirectory"]
_G.shell.aliases = shellcfg["aliases"]
local function parseCommand(command) local function parseCommand(command)
checkArg(1, command, "string") if shell.aliases[command:match("[^ ]+")] then
local gm, result, args, trimmedCommand = command:gmatch('[^ ]+'), nil, {}, command 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 while true do
result = gm() result = gm()
if not result then if not result then
@@ -63,7 +67,7 @@ local function parseCommand(command)
end end
end end
if not foundfile then if not foundfile then
print("no such file or command: "..args[1]) print("No such file or command: "..args[1])
end end
end end
@@ -75,6 +79,5 @@ while true do
-- termlib.cursorPosX = #(shell.workingDirectory .. " > ") -- termlib.cursorPosX = #(shell.workingDirectory .. " > ")
-- termlib.cursorPosY = termlib.cursorPosY - 1 -- termlib.cursorPosY = termlib.cursorPosY - 1
local shellCommand = read() local shellCommand = read()
ocelot.log("parsing "..shellCommand)
parseCommand(shellCommand) parseCommand(shellCommand)
end end
@@ -73,6 +73,7 @@ function _G.print(text,endNewLine)
if not text or not tostring(text) then if not text or not tostring(text) then
return return
end end
text = "\27[0m" .. text:gsub("\t", " ")
text = tostring(text) text = tostring(text)
readBreak = 0 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. -- 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.
+7 -2
View File
@@ -1,4 +1,9 @@
local componentlib = {} local componentlib
if table.copy then
componentlib = table.copy(component)
else
componentlib = {}
end
function componentlib.get(address) function componentlib.get(address)
checkArg(1, address, "string") checkArg(1, address, "string")
@@ -14,4 +19,4 @@ end
componentlib.invoke = component.invoke componentlib.invoke = component.invoke
return(componentlib) return(componentlib)
+93 -2
View File
@@ -3,7 +3,7 @@ local component
if loadfile then if loadfile then
component = loadfile("/halyde/lib/component.lua")(loadfile) component = loadfile("/halyde/lib/component.lua")(loadfile)
else elseif import then
component = import("component") component = import("component")
end end
@@ -67,11 +67,102 @@ function filesystem.open(path, mode) -- opens a file and returns its handle
end end
function filesystem.list(path) 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") checkArg(1, path, "string")
local address, absPath = filesystem.processPath(path) local address, absPath = filesystem.processPath(path)
if not address then if not address then
return false return false
end end
return component.invoke(address, "list", absPath) return component.invoke(address, "size", absPath)
end 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) return(filesystem)
+1 -1
View File
@@ -10,7 +10,7 @@ local function loadfile(file)
data = data .. (tmpdata or "") data = data .. (tmpdata or "")
until not tmpdata until not tmpdata
component.invoke(computer.getBootAddress(), "close", handle) component.invoke(computer.getBootAddress(), "close", handle)
return(assert(load(data, "=" .. file))) return assert(load(data, "=" .. file))
end end
local function handleError(errorMessage) local function handleError(errorMessage)