--: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 function abspath(p) if not p or p == "" then return syscall.getcwd() end if p:sub(1,1) ~= "/" then p = syscall.getcwd().."/"..p end local parts = {} for seg in p:gmatch("[^/]+") do if seg == ".." then if #parts > 0 then table.remove(parts) end elseif seg ~= "." then table.insert(parts, seg) end end return "/"..table.concat(parts, "/") end local function basename(p) p = p:gsub("/$","") return p:match("([^/]+)$") or p end local function dirname(p) p = p:gsub("/$","") local d = p:match("^(.*)/[^/]+$") if not d then return "." end if d == "" then return "/" end return d end local function readall(fd) local t = {} while true do local ok, chunk = pcall(syscall.read, fd, 65536) if not ok or not chunk or chunk == "" then break end t[#t+1] = chunk end return table.concat(t) end local function readfile(path) local ok, fd = pcall(syscall.open, path, "r") if not ok then return nil, fd end local data = readall(fd) pcall(syscall.close, fd) return data end local function writefile(path, data) local ok, fd = pcall(syscall.open, path, "w") if not ok then return false, fd end pcall(syscall.write, fd, data) pcall(syscall.close, fd) return true end local function parseargs(rawargs, flagspec, longflags) local opts = {} local args = {} longflags = longflags or {} local i = 1 while i <= #rawargs do local v = rawargs[i] if v == "--" then for j = i+1, #rawargs do args[#args+1] = rawargs[j] end break elseif v:sub(1,2) == "--" then local lf = v:sub(3) if longflags[lf] ~= nil then opts[lf] = true else opts["_unknown"] = v end elseif v:sub(1,1) == "-" and #v > 1 then for k = 2, #v do local c = v:sub(k,k) if flagspec:find(c, 1, true) then opts[c] = true else opts["_unknown"] = "-"..c end end else args[#args+1] = v end i = i + 1 end return opts, args end local function eachline(text, fn) local pos = 1 while pos <= #text do local nl = text:find("\n", pos, true) if nl then fn(text:sub(pos, nl-1)); pos = nl+1 else fn(text:sub(pos)); break end end end local function splitlines(text) local lines = {} eachline(text, function(l) lines[#lines+1] = l end) return lines end local function copyfile(src, dst) local data, err = readfile(src) if not data then return false, err end local ok, err2 = writefile(dst, data) if not ok then return false, err2 end local ok2, stat = pcall(syscall.stat, src) if ok2 and stat and stat.perms then pcall(syscall.chmod, dst, stat.perms) end return true end local function rmtree(path) local t = syscall.type(path) if t == "directory" then local ok, list = pcall(syscall.listdir, path) if ok then for _, e in ipairs(list) do rmtree(path:gsub("/$","").."/"..e) end end end if t then pcall(syscall.remove, path) end end local function copytree(src, dst) pcall(syscall.mkdir, dst) local ok, list = pcall(syscall.listdir, src) if not ok then return end for _, e in ipairs(list) do local s = src:gsub("/$","").."/"..e local d = dst:gsub("/$","").."/"..e local t = syscall.type(s) if t == "directory" then copytree(s, d) elseif t == "file" then copyfile(s, d) elseif t == "symlink" then local ok2, tgt = pcall(syscall.readlink, s) if ok2 then pcall(syscall.remove, d); pcall(syscall.symlink, tgt, d) end end 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 directory"); return end print(oldWD); local tmp = oldWD; oldWD = cwd; syscall.chdir(tmp); return end local target = abspath(dirIn) if target:sub(-1) ~= "/" then target = target.."/" end if not fs.isDir(target) then print("hysh: cd: "..dirIn..": No such directory"); return end oldWD = cwd; syscall.chdir(target) end builtinCmds.exit = function(code) syscall.exit(tonumber(code) or 0) end builtinCmds.echo = function(...) local args = {...} local n = false local start = 1 if args[1] == "-n" then n = true; start = 2 end local out = table.concat(args, " ", start) if n then printInline(out) else print(out) end end builtinCmds.pwd = function() print(syscall.getcwd()) end builtinCmds["true"] = function() end builtinCmds["false"] = function() end builtinCmds.sleep = function(...) local args = {...} if #args == 0 then print("sleep: missing operand"); return end local total = 0 for _, a in ipairs(args) do local n, u = a:match("^([%d%.]+)([smhd]?)$") if not n then print("sleep: invalid time '"..a.."'"); return end n = tonumber(n) if u == "m" then n = n * 60 elseif u == "h" then n = n * 3600 elseif u == "d" then n = n * 86400 end total = total + n end sleep(total) end builtinCmds.clear = function() syscall.devctl(1,"clear") syscall.devctl(1,"sfgc",1) syscall.devctl(1,"spos",1,1) end builtinCmds.whoami = function() print(syscall.getUsername() or "unknown") end builtinCmds.hostname = function(...) local args = {...} if #args == 0 then print(syscall.getHostname() or "unknown") else local ok, err = pcall(syscall.setHostname, args[1]) if not ok then print("hostname: "..tostring(err)) end end end builtinCmds.uname = function(...) local opts, _ = parseargs({...}, "asnrm") local all = opts.a local parts = {} local results = {} for word in string.gmatch(syscall.version(), "%S+") do table.insert(results, word) end if all or opts.s or (not opts.s and not opts.n and not opts.r and not opts.m) then parts[#parts+1] = results[1] end if all or opts.n then parts[#parts+1] = (syscall.getHostname() or "hyperion") end if all or opts.r then parts[#parts+1] = results[2] end if all or opts.m then parts[#parts+1] = "virtual" end print(table.concat(parts, " ")) end builtinCmds.printenv = function(...) local args = {...} local vars = {"PATH","HOME","USER","SHELL","TERM","HOSTNAME","PWD","OLDPWD","LANG","TZ","EDITOR"} if #args == 0 then for _, k in ipairs(vars) do local ok, v = pcall(syscall.getEnviron, k) if ok and v then print(k.."="..v) end end else for _, k in ipairs(args) do local ok, v = pcall(syscall.getEnviron, k) if ok and v then print(v) end end end end builtinCmds.env = function(...) local args = {...} local i = 1 while i <= #args and args[i]:match("^[%w_]+=") do local k, v = args[i]:match("^([^=]+)=(.*)") pcall(syscall.setEnviron, k, v) i = i + 1 end if i > #args then builtinCmds.printenv() end end builtinCmds.touch = function(...) local _, args = parseargs({...}, "amc") if #args == 0 then print("touch: missing operand"); return end for _, a in ipairs(args) do local path = abspath(a) if not syscall.exists(path) then local ok, fd = pcall(syscall.open, path, "w") if ok then pcall(syscall.close, fd) else print("touch: cannot create '"..a.."': "..tostring(fd)) end end end end builtinCmds.mkdir = function(...) local opts, args = parseargs({...}, "p") if #args == 0 then print("mkdir: missing operand"); return end for _, a in ipairs(args) do local path = abspath(a) if path:sub(-1) ~= "/" then path = path.."/" end if fs.isDir(path) then print("mkdir: cannot create '"..a.."': Directory exists"); return end local ok, err = pcall(syscall.mkdir, path) if not ok then print("mkdir: cannot create '"..a.."': "..tostring(err)) end end end builtinCmds.rm = function(...) local opts, args = parseargs({...}, "rRf") if #args == 0 then print("rm: missing operand"); return end local recursive = opts.r or opts.R for _, a in ipairs(args) do local path = abspath(a) local t = syscall.type(path) if not t then if not opts.f then print("rm: cannot remove '"..a.."': No such file or directory") end elseif t == "directory" then if not recursive then print("rm: cannot remove '"..a.."': Is a directory") else rmtree(path) end else local ok, err = pcall(syscall.remove, path) if not ok then print("rm: cannot remove '"..a.."': "..tostring(err)) end end end end builtinCmds.cp = function(...) local opts, args = parseargs({...}, "rRp") if #args < 2 then print("cp: missing operand"); return end local recursive = opts.r or opts.R local dst = abspath(args[#args]) local dstIsDir = syscall.type(dst) == "directory" if #args > 2 and not dstIsDir then print("cp: target '"..args[#args].."' is not a directory"); return end for i = 1, #args-1 do local src = abspath(args[i]) local t = syscall.type(src) if not t then print("cp: '"..args[i].."': No such file or directory") elseif t == "directory" then if not recursive then print("cp: omitting directory '"..args[i].."'") else copytree(src, dstIsDir and (dst:gsub("/$","").."/"..basename(args[i])) or dst) end else local d = dstIsDir and (dst:gsub("/$","").."/"..basename(args[i])) or dst local ok, err = copyfile(src, d) if not ok then print("cp: '"..args[i].."': "..tostring(err)) end end end end builtinCmds.mv = function(...) local opts, args = parseargs({...}, "f") if #args < 2 then print("mv: missing operand"); return end local dst = abspath(args[#args]) local dstIsDir = syscall.type(dst) == "directory" if #args > 2 and not dstIsDir then print("mv: target '"..args[#args].."' is not a directory"); return end for i = 1, #args-1 do local src = abspath(args[i]) local t = syscall.type(src) if not t then print("mv: '"..args[i].."': No such file or directory") else local d = dstIsDir and (dst:gsub("/$","").."/"..basename(args[i])) or dst if t == "directory" then copytree(src, d); rmtree(src) else local ok, err = copyfile(src, d) if ok then pcall(syscall.remove, src) else print("mv: '"..args[i].."': "..tostring(err)) end end end end end builtinCmds.cat = function(...) local args = {...} if #args == 0 then while true do local ok, chunk = pcall(syscall.read, 0, 4096) if not ok or not chunk or chunk == "" then break end printInline(chunk) end print("") return end for _, a in ipairs(args) do local path = abspath(a) if not syscall.exists(path) then print("cat: "..a..": No such file or directory") else local ok, fd = pcall(syscall.open, path, "r") if not ok then print("cat: "..a..": "..tostring(fd)) else while true do local ok2, chunk = pcall(syscall.read, fd, 65536) if not ok2 or not chunk or chunk == "" then break end printInline(chunk) end pcall(syscall.close, fd) end end end end builtinCmds.head = function(...) local raw = {...} local n = 10 local files = {} local i = 1 while i <= #raw do local v = raw[i] if v == "-n" then i=i+1; n=tonumber(raw[i]) or 10 elseif v:match("^%-n%d+$") then n=tonumber(v:sub(3)) elseif v:match("^%-%d+$") then n=tonumber(v:sub(2)) elseif v:sub(1,1) ~= "-" or v == "-" then files[#files+1] = v end i=i+1 end local multi = #files > 1 local function dohead(text, label) if multi then syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1) end local count = 0 for line in (text.."\n"):gmatch("([^\n]*)\n") do if count >= n then break end print(line); count = count+1 end end if #files == 0 then dohead(readall(0), "stdin") else for _, a in ipairs(files) do if a == "-" then dohead(readall(0), "stdin") else local data, err = readfile(abspath(a)) if not data then print("head: "..a..": "..tostring(err)) else dohead(data, a) end end end end end builtinCmds.tail = function(...) local raw = {...} local n = 10 local files = {} local i = 1 while i <= #raw do local v = raw[i] if v == "-n" then i=i+1; n=tonumber(raw[i]) or 10 elseif v:match("^%-n%d+$") then n=tonumber(v:sub(3)) elseif v:match("^%-%d+$") then n=tonumber(v:sub(2)) elseif v:sub(1,1) ~= "-" or v == "-" then files[#files+1] = v end i=i+1 end local multi = #files > 1 local function dotail(text, label) if multi then syscall.devctl(1,"sfgc",4); print("==> "..label.." <=="); syscall.devctl(1,"sfgc",1) end local lines = splitlines(text) local start = math.max(1, #lines - n + 1) for j = start, #lines do print(lines[j]) end end if #files == 0 then dotail(readall(0), "stdin") else for _, a in ipairs(files) do if a == "-" then dotail(readall(0), "stdin") else local data, err = readfile(abspath(a)) if not data then print("tail: "..a..": "..tostring(err)) else dotail(data, a) end end end end end builtinCmds.wc = function(...) local opts, args = parseargs({...}, "lwc") local showAll = not opts.l and not opts.w and not opts.c if showAll then opts.l=true; opts.w=true; opts.c=true end local function count(text) local l,w,b = 0,0,#text for _ in text:gmatch("\n") do l=l+1 end for _ in text:gmatch("%S+") do w=w+1 end return l,w,b end local function fmt(l,w,c,lbl) local p={} if opts.l then p[#p+1]=string.format("%7d",l) end if opts.w then p[#p+1]=string.format("%7d",w) end if opts.c then p[#p+1]=string.format("%7d",c) end if lbl then p[#p+1]=" "..lbl end print(table.concat(p)) end local tl,tw,tc = 0,0,0 if #args == 0 then local l,w,c = count(readall(0)); fmt(l,w,c) else for _, a in ipairs(args) do local data, err = readfile(abspath(a)) if not data then print("wc: "..a..": "..tostring(err)) else local l,w,c = count(data); fmt(l,w,c,a) tl=tl+l; tw=tw+w; tc=tc+c end end if #args > 1 then fmt(tl,tw,tc,"total") end end end builtinCmds.grep = function(...) local opts, args = parseargs({...}, "ivnlcrR", {["ignore-case"]=true,["invert-match"]=true}) if #args == 0 then print("grep: missing pattern"); return end local pat = args[1] if opts.i or opts["ignore-case"] then pat = pat:lower() end local recursive = opts.r or opts.R local found = false local function greptext(text, label, showlabel) local count = 0 local linenum = 0 local hasMatch = false eachline(text, function(line) linenum = linenum+1 local test = (opts.i or opts["ignore-case"]) and line:lower() or line local m = (test:find(pat) ~= nil) if opts.v or opts["invert-match"] then m = not m end if m then count=count+1; hasMatch=true; found=true if not opts.l and not opts.c then local out = "" if showlabel then out=out..label..":" end if opts.n then out=out..linenum..":" end print(out..line) end end end) if opts.l and hasMatch then print(label) end if opts.c then print((showlabel and label..":" or "")..count) end end local function grepfile(path, label, showlabel) local data, err = readfile(path) if not data then print("grep: "..label..": "..tostring(err)); return end greptext(data, label, showlabel) end local function grepdir(dir, prefix) local ok, list = pcall(syscall.listdir, dir) if not ok then return end for _, e in ipairs(list) do local fp = dir:gsub("/$","").."/"..e local lbl = (prefix ~= "" and prefix.."/" or "")..e local t = syscall.type(fp) if t == "directory" then grepdir(fp, lbl) elseif t == "file" then grepfile(fp, lbl, true) end end end if #args == 1 then greptext(readall(0), "(stdin)", false) else local showlabel = #args > 2 or recursive for i = 2, #args do local path = abspath(args[i]) local t = syscall.type(path) if t == "directory" then if recursive then grepdir(path, args[i]) else print("grep: "..args[i]..": Is a directory") end elseif t then grepfile(path, args[i], showlabel) else print("grep: "..args[i]..": No such file or directory") end end end end builtinCmds.sort = function(...) local opts, args = parseargs({...}, "rnu") local lines = {} local function addlines(text) local ls = splitlines(text) for _, l in ipairs(ls) do lines[#lines+1] = l end end if #args == 0 then addlines(readall(0)) else for _, a in ipairs(args) do local data, err = readfile(abspath(a)) if not data then print("sort: "..a..": "..tostring(err)) else addlines(data) end end end table.sort(lines, function(a,b) if opts.n then local na = tonumber(a:match("^%-?%d+%.?%d*")) or 0 local nb = tonumber(b:match("^%-?%d+%.?%d*")) or 0 return opts.r and na > nb or na < nb end return opts.r and a > b or a < b end) local prev = nil for _, l in ipairs(lines) do if not opts.u or l ~= prev then print(l); prev = l end end end builtinCmds.uniq = function(...) local opts, args = parseargs({...}, "cdui") local text if args[1] then local data, err = readfile(abspath(args[1])) if not data then print("uniq: "..args[1]..": "..tostring(err)); return end text = data else text = readall(0) end local prev, prevCount = nil, 0 local out = {} local function emit(line, count) if opts.d and count == 1 then return end if opts.u and count > 1 then return end out[#out+1] = opts.c and string.format("%7d %s", count, line) or line end eachline(text, function(line) local cmp = opts.i and line:lower() or line local cprev = opts.i and (prev and prev:lower()) or prev if cmp == cprev then prevCount = prevCount+1 else if prev ~= nil then emit(prev, prevCount) end prev = line; prevCount = 1 end end) if prev ~= nil then emit(prev, prevCount) end if args[2] then local ok, err = writefile(abspath(args[2]), table.concat(out, "\n").."\n") if not ok then print("uniq: "..args[2]..": "..tostring(err)) end else for _, l in ipairs(out) do print(l) end end end builtinCmds.tee = function(...) local opts, args = parseargs({...}, "a") local mode = opts.a and "a" or "w" local fds = {} for _, a in ipairs(args) do local ok, fd = pcall(syscall.open, abspath(a), mode) if not ok then print("tee: "..a..": "..tostring(fd)) else fds[#fds+1] = fd end end while true do local ok, chunk = pcall(syscall.read, 0, 4096) if not ok or not chunk or chunk == "" then break end pcall(syscall.write, 1, chunk) for _, fd in ipairs(fds) do pcall(syscall.write, fd, chunk) end end for _, fd in ipairs(fds) do pcall(syscall.close, fd) end end builtinCmds.find = function(...) local raw = {...} local roots, filters = {}, {} local maxdepth, mindepth = nil, 0 local i = 1 while i <= #raw and raw[i]:sub(1,1) ~= "-" do roots[#roots+1] = raw[i]; i=i+1 end if #roots == 0 then roots = {"."} end while i <= #raw do local tok = raw[i] if tok == "-name" then i=i+1 local pat = "^"..(raw[i] or ""):gsub("([%.%+%-%^%$%(%)%[%]%%])","%%%1"):gsub("%*",".*"):gsub("%?",".").. "$" filters[#filters+1] = function(_, e, _2, _3) return e:match(pat) ~= nil end elseif tok == "-type" then i=i+1; local ft=raw[i] or "" filters[#filters+1] = function(_, _, t, _2) if ft=="f" then return t=="file" elseif ft=="d" then return t=="directory" elseif ft=="l" then return t=="symlink" end; return true end elseif tok == "-maxdepth" then i=i+1; maxdepth=tonumber(raw[i]) or 0 elseif tok == "-mindepth" then i=i+1; mindepth=tonumber(raw[i]) or 0 elseif tok == "-empty" then filters[#filters+1] = function(path, _, t, _2) if t=="file" then local ok,s=pcall(syscall.stat,path); return ok and s and (s.size or 0)==0 elseif t=="directory" then local ok,l=pcall(syscall.listdir,path); return ok and l and #l==0 end return false end end i=i+1 end local function match(path, e, t, depth) for _, f in ipairs(filters) do if not f(path,e,t,depth) then return false end end return true end local function walk(path, disp, depth) local t = syscall.type(path) if not t then return end local e = path:match("([^/]+)/?$") or path if depth >= mindepth and match(path,e,t,depth) then print(disp) end if t=="directory" and (maxdepth==nil or depth 1 and p:sub(-1)=="/" do p=p:sub(1,-2) end local b = p:match("([^/]+)$") or p if args[2] and b:sub(-#args[2]) == args[2] then b = b:sub(1, #b-#args[2]) end print(b) end builtinCmds.dirname = function(...) local args = {...} if #args == 0 then print("dirname: missing operand"); return end for _, p in ipairs(args) do while #p > 1 and p:sub(-1)=="/" do p=p:sub(1,-2) end local d = p:match("^(.*)/[^/]+$") if not d then print(".") elseif d == "" then print("/") else print(d) end end end builtinCmds.df = function(...) local opts, _ = parseargs({...}, "h") local function hfmt(n) local u={"K","M","G","T"}; local i=0 while n>=1024 and i<#u do n=n/1024; i=i+1 end if i==0 then return tostring(math.floor(n)) end if n<10 then return string.format("%.1f%s",n,u[i]) end return math.floor(n)..u[i] end local function fmt(n) return opts.h and hfmt(n) or tostring(n) end print(string.format("%-20s %10s %10s %10s %6s %-s","Filesystem","Size","Used","Avail","Use%","Mounted on")) local mounts = {{"$","/"}, {"devfs0000","/dev/"}, {"tmpfs0000","/tmp/"}} for _, m in ipairs(mounts) do local id, mp = m[1], m[2] print(string.format("%-20s %10s %10s %10s %6s %-s", id, "?", "?", "?", "?%", mp)) end 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 local function redraw() syscall.devctl(1,"spos",curOffsetX,curOffsetY) syscall.write(1, string.sub(input, 1, cursorPos-1)) 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) 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 if cursorPos>1 then cursorPos=cursorPos-1;dirty=true end elseif key=="\20" then if cursorPos<=#input then cursorPos=cursorPos+1;dirty=true end elseif key=="\17" then if history<#commandHistory then history=history+1 input=commandHistory[#commandHistory-history+1] cursorPos=#input+1;dirty=true end elseif key=="\18" then 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 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 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 printError(progName, msg) syscall.devctl(1,"sfgc",2) local s = tostring(msg) local line, rest = s:match("%]:(%d+): (.+)$") if not line then line, rest = s:match(":(%d+): (.+)$") end if line then printInline(progName..": error on line "..line..": "); print(rest) else print(progName..": "..s) end syscall.devctl(1,"sfgc",1) end local function runCommand(command) do local func = load("return "..command, "@equation", "t", {}) if func then local ok, result = pcall(func) if ok and type(result)=="number" then print(result); return end end end terminate = false local args = string.split(command, " ") for i = #args, 1, -1 do if args[i] == "" then table.remove(args, i) end end if #args == 0 then return end if builtinCmds[args[1]] then local ok, err = pcall(builtinCmds[args[1]], table.unpack(args, 2)) if not ok then printError(args[1], err) end return end local cmdPath = "" if string.find(args[1], "/") then local candidate = args[1] if candidate:sub(1,1) ~= "/" then candidate = syscall.getcwd().."/"..candidate end if fs.exists(candidate) then cmdPath = candidate end else local paths = string.split(syscall.getEnviron("PATH"), ":") for _, p in pairs(paths) do if fs.exists(p..args[1]) then cmdPath = p..args[1]; break end end if cmdPath == "" then local cwd = syscall.getcwd() local candidate = cwd..(cwd:sub(-1)=="/" and "" or "/")..args[1] if fs.exists(candidate) then cmdPath = candidate end end end if cmdPath == "" then print(args[1]..": Command not found"); return end local progName = cmdPath:match("([^/]+)$") or args[1] local xok, xerr = pcall(syscall.access, cmdPath, "x") if not xok then syscall.devctl(1,"sfgc",2); print(progName..": Permission denied"); syscall.devctl(1,"sfgc",1) return end local text = fs.readAllText(cmdPath) local program, err = load(text, progName) if not program then syscall.devctl(1,"sfgc",2) local line, rest = tostring(err):match(":(%d+): (.+)$") if line then printInline(progName..": load error on line "..line..": "); print(rest) else print(progName..": load error: "..tostring(err)) end syscall.devctl(1,"sfgc",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 ok2, msg = pcall(program, ...) if not ok2 then printError(progName, msg) 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 ok2 = syscall.kill(proc) if ok2 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 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