forked from Hyperion/HyperionOS
300 lines
9.1 KiB
Plaintext
300 lines
9.1 KiB
Plaintext
--:Minify:--
|
|
syscall.open("/dev/tty/TTY1","r") --stdin (Device 0)
|
|
syscall.open("/dev/tty/TTY1","w") --stdout (Device 1)
|
|
syscall.open("/dev/null","w") --stderr (device 2)
|
|
|
|
local success, errorMsg = xpcall(function()
|
|
|
|
|
|
|
|
local fs = require("sys.fs")
|
|
|
|
syscall.devctl(1,"clear")
|
|
syscall.devctl(1,"sfgc",1)
|
|
syscall.devctl(1,"spos",1,1)
|
|
print("HyperionOS hysh Shell")
|
|
|
|
local userhost = (syscall.getUsername() or "Unknown").."@"..(syscall.getHostname() or "Unknown")
|
|
local commandHistory = {}
|
|
local terminate = false
|
|
syscall.setEnviron("SHELL","rtbash")
|
|
syscall.setEnviron("PATH","/bin/")
|
|
syscall.chdir("/")
|
|
local oldWD = ""
|
|
|
|
for i = 1, 16 do
|
|
syscall.devctl(1,"sbgc",i)
|
|
printInline(" ");
|
|
end
|
|
print("\n")
|
|
|
|
syscall.sigcatch(function(sig)
|
|
if sig == 1 then
|
|
terminate = true
|
|
end
|
|
end)
|
|
|
|
local builtinCmds = {}
|
|
|
|
builtinCmds.cd = function(path)
|
|
local cwd = syscall.getcwd()
|
|
local dirIn = (path or "")
|
|
if dirIn == "-" then
|
|
if oldWD == "" then
|
|
print("hysh-cd: No previous working directory set.")
|
|
else
|
|
print(oldWD)
|
|
syscall.chdir(oldWD)
|
|
oldWD = cwd
|
|
end
|
|
return
|
|
end
|
|
local dirInMod = dirIn
|
|
if dirIn:sub(1, 1) ~= "/" then dirInMod = cwd .. "/" .. dirIn end
|
|
local parts = {}
|
|
for part in dirInMod:gmatch("[^/]+") do
|
|
if part == ".." then
|
|
if #parts > 0 then table.remove(parts) end
|
|
elseif part ~= "." and part ~= "" then
|
|
table.insert(parts, part)
|
|
end
|
|
end
|
|
local normDir = "/" .. table.concat(parts, "/")
|
|
if normDir:sub(#normDir, #normDir) ~= "/" then normDir = normDir .. "/" end
|
|
|
|
if not fs.isDir(normDir) then
|
|
print("hysh-cd: "..dirIn..": No such directory.")
|
|
return
|
|
end
|
|
oldWD = cwd
|
|
syscall.chdir(normDir)
|
|
end
|
|
|
|
|
|
local function getUserInput()
|
|
syscall.devctl(1,"sfgc",3)
|
|
syscall.write(1, userhost)
|
|
syscall.devctl(1,"sfgc",1)
|
|
syscall.write(1, ":")
|
|
syscall.devctl(1,"sfgc",10)
|
|
syscall.write(1, syscall.getcwd())
|
|
syscall.devctl(1,"sfgc",1)
|
|
syscall.write(1, "$ ")
|
|
local curOffsetStr = syscall.devctl(1, "gpos")
|
|
local curOffsetX = tonumber(curOffsetStr:sub(1, curOffsetStr:find(";")-1))
|
|
local curOffsetY = tonumber(curOffsetStr:sub(curOffsetStr:find(";")+1))
|
|
|
|
local input = ""
|
|
local blinkState = false
|
|
local cursorPos = 1
|
|
local history = 0
|
|
local dirty = true -- redraw on first iteration
|
|
|
|
local function redraw()
|
|
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
-- text before cursor
|
|
syscall.write(1, string.sub(input, 1, cursorPos-1))
|
|
-- cursor character (inverted if blinking on)
|
|
if blinkState then
|
|
syscall.devctl(1,"sfgc",16)
|
|
syscall.devctl(1,"sbgc",1)
|
|
end
|
|
if cursorPos > #input then
|
|
syscall.write(1, " ")
|
|
else
|
|
syscall.write(1, string.sub(input, cursorPos, cursorPos))
|
|
end
|
|
syscall.devctl(1,"sfgc",1)
|
|
syscall.devctl(1,"sbgc",16)
|
|
-- text after cursor + trailing space to erase old chars
|
|
syscall.write(1, string.sub(input, cursorPos+1) .. " ")
|
|
end
|
|
|
|
while true do
|
|
local key = syscall.read(0)
|
|
if key and key ~= "" then
|
|
if key == "\19" then -- left arrow
|
|
if cursorPos > 1 then
|
|
cursorPos = cursorPos - 1
|
|
dirty = true
|
|
end
|
|
elseif key == "\20" then -- right arrow
|
|
if cursorPos <= #input then
|
|
cursorPos = cursorPos + 1
|
|
dirty = true
|
|
end
|
|
elseif key == "\17" then -- up arrow
|
|
if history < #commandHistory then
|
|
history = history + 1
|
|
input = commandHistory[#commandHistory - history + 1]
|
|
cursorPos = #input + 1
|
|
dirty = true
|
|
end
|
|
elseif key == "\18" then -- down arrow
|
|
if history > 1 then
|
|
history = history - 1
|
|
input = commandHistory[#commandHistory - history + 1]
|
|
cursorPos = #input + 1
|
|
dirty = true
|
|
elseif history == 1 then
|
|
history = 0
|
|
input = ""
|
|
cursorPos = 1
|
|
dirty = true
|
|
end
|
|
elseif key == "\b" then
|
|
if cursorPos > 1 then
|
|
input = string.sub(input, 1, cursorPos-2) .. string.sub(input, cursorPos)
|
|
cursorPos = cursorPos - 1
|
|
dirty = true
|
|
end
|
|
elseif key == "\n" then
|
|
-- redraw cleanly with no cursor highlight before committing
|
|
syscall.devctl(1,"sfgc",1)
|
|
syscall.devctl(1,"sbgc",16)
|
|
syscall.devctl(1,"spos",curOffsetX,curOffsetY)
|
|
syscall.write(1, input .. " \n")
|
|
return input
|
|
else
|
|
input = string.sub(input, 1, cursorPos-1) .. key .. string.sub(input, cursorPos)
|
|
cursorPos = cursorPos + 1
|
|
dirty = true
|
|
end
|
|
end
|
|
|
|
-- cursor blink
|
|
local curBlink = ((math.floor(syscall.getUptime() / 500) % 2) == 0)
|
|
if curBlink ~= blinkState then
|
|
blinkState = curBlink
|
|
dirty = true
|
|
end
|
|
|
|
if dirty then
|
|
redraw()
|
|
dirty = false
|
|
end
|
|
end
|
|
end
|
|
|
|
local function runCommand(command)
|
|
do
|
|
local func = load("return " .. command, "@equation", "t", {})
|
|
if func then
|
|
local success, result = pcall(func)
|
|
if success and type(result) == "number" then
|
|
print(result)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
terminate = false
|
|
local args = string.split(command, " ")
|
|
if builtinCmds[args[1]] then
|
|
local success, msg = pcall(builtinCmds[args[1]], table.unpack(args, 2))
|
|
if not success then
|
|
local errSL = string.sub(msg, string.find(msg, "]") + 2)
|
|
syscall.devctl(1,"sfgc",2)
|
|
printInline(args[1]..": Program runtime error on line ")
|
|
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
syscall.devctl(1,"sfgc",1)
|
|
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
end
|
|
return
|
|
end
|
|
|
|
local cmdPath = ""
|
|
if string.find(args[1], "/") then
|
|
if fs.exists(args[1]) then
|
|
cmdPath = args[1]
|
|
end
|
|
else
|
|
local paths = string.split(syscall.getEnviron("PATH"), ":")
|
|
for _, path in pairs(paths) do
|
|
if fs.exists(path..args[1]) then
|
|
cmdPath = path..args[1]
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if cmdPath == "" then
|
|
print(args[1]..": Command not found")
|
|
return
|
|
end
|
|
|
|
local progName = string.sub(cmdPath, #cmdPath - string.find(string.reverse(cmdPath), "/") + 2)
|
|
|
|
local text = fs.readAllText(cmdPath)
|
|
local program, err = load(text, progName)
|
|
if not program then
|
|
local errSL = string.sub(err, string.find(err, ":") + 1)
|
|
syscall.devctl(1,"sfgc",2)
|
|
printInline(progName..": Program load error on line ")
|
|
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
syscall.devctl(1,"sfgc",1)
|
|
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
return
|
|
end
|
|
|
|
local proc = syscall.spawn(function(...)
|
|
syscall.open("/dev/tty/TTY1","r")
|
|
syscall.open("/dev/tty/TTY1","w")
|
|
syscall.open("/dev/null","w")
|
|
local success, msg = pcall(program, ...)
|
|
if not success then
|
|
local errSL = string.sub(msg, string.find(msg, ":") + 1)
|
|
syscall.devctl(1,"sfgc",2)
|
|
printInline(progName..": Program runtime error on line ")
|
|
print(string.sub(errSL, 1, string.find(errSL, ":") - 1))
|
|
syscall.devctl(1,"sfgc",1)
|
|
print(string.sub(errSL, string.find(errSL, ":") + 1))
|
|
end
|
|
end, progName, nil, {table.unpack(args, 2)})
|
|
|
|
while true do
|
|
local exited, code = syscall.collect(proc)
|
|
if exited then
|
|
if code then
|
|
print("\nTask exited with code:\n"..tostring(code))
|
|
end
|
|
return
|
|
end
|
|
if terminate then
|
|
local success, err = syscall.kill(proc)
|
|
if success then
|
|
syscall.devctl(1,"sbgc",16)
|
|
syscall.devctl(1,"sfgc",2)
|
|
print("\nProgram Terminated.")
|
|
syscall.devctl(1,"sfgc",1)
|
|
end
|
|
terminate = false
|
|
break
|
|
end
|
|
sleep(0.05)
|
|
end
|
|
end
|
|
|
|
while true do
|
|
local command = getUserInput()
|
|
if command ~= "" then
|
|
if command ~= commandHistory[#commandHistory] then
|
|
table.insert(commandHistory, command)
|
|
end
|
|
runCommand(command)
|
|
end
|
|
end
|
|
|
|
|
|
--ERROR HANDLING
|
|
end, debug.traceback)
|
|
|
|
if not success then
|
|
syscall.log("Error running shell: "..errorMsg, "ERROR")
|
|
syscall.devctl(1,"sfgc",2)
|
|
syscall.devctl(1,"sbgc",16)
|
|
print()
|
|
print("Error running shell: ")
|
|
print(errorMsg)
|
|
end
|