blob: 514222ee124e906ceedae54888658c12fae8d611 [file] [log] [blame] [raw]
local cwd = "/"
local path = {"/bin/", "/usr/bin/", "/home/bin/"}
local function onTermAvailable()
term.clear()
print("OpenOS v1.0 (" .. math.floor(os.totalMemory() / 1024) .. "k RAM)")
while term.isAvailable() do
io.write("> ")
local command = io.read()
if not command then
return -- eof
end
local parts = command:gmatch("%S+")
command = parts()
local args = {}
for part in parts do
table.insert(args, part)
end
local result, reason = shell.execute(command, table.unpack(args))
if not result then
print(reason)
end
end
end
local function findFile(name, path, ext)
checkArg(1, name, "string")
local function findIn(path)
path = fs.concat(fs.concat(path, name), "..")
name = fs.name(name)
if fs.isDirectory(path) then
if fs.exists(fs.concat(path, name)) then
return true, fs.concat(path, name)
elseif ext then
name = name .. "." .. ext
if fs.exists(fs.concat(path, name)) then
return true, fs.concat(path, name)
end
end
end
return false
end
if name:usub(1, 1) == "/" then
local found, where = findIn("/")
if found then return where end
else
local found, where = findIn(cwd)
if found then return where end
if path then
for _, p in ipairs(path) do
local found, where = findIn(p)
if found then return where end
end
end
end
return false
end
shell = {}
function shell.cwd(...)
local args = table.pack(...)
if args.n > 0 then
checkArg(1, args[1], "string")
local path = fs.canonical(args[1]) .. "/"
if fs.isDirectory(path) then
cwd = path
else
return nil, "not a directory"
end
end
return cwd
end
function shell.resolve(path)
if path:usub(1, 1) == "/" then
return fs.canonical(path)
else
return fs.concat(shell.cwd(), path)
end
end
function shell.which(program)
local where = findFile(program, path, "lua")
if where then
return where
else
return nil, "program not found"
end
end
function shell.execute(program, ...)
local where = findFile(program, path, "lua")
if not where then
return nil, "program not found"
end
-- Track listeners and timers registered by spawned programs so we can kill
-- them all when the coroutine dies.
local listeners, weakListeners, timers = {}, {}, {}
local pevent = {}
function pevent.ignore(name, callback, weak)
local list
if weak then
if weakListeners[name] and weakListeners[name][callback] then
list = weakListeners
end
elseif listeners[name] and listeners[name][callback] then
list = listeners
end
if list then
event.ignore(name, callback)
list[name][callback] = nil
return true
end
return false
end
function pevent.listen(name, callback, weak)
if event.listen(name, callback, weak) then
if weak then
weakListeners[name] = weakListeners[name] or {}
weakListeners[name][callback] = true
else
listeners[name] = listeners[name] or {}
listeners[name][callback] = nil
end
return true
end
return false
end
function pevent.cancel(timerId)
if timers[timerId] then
timers[timerId] = nil
return event.cancel(timerId)
end
return false
end
function pevent.timer(timeout, callback)
local id
local function onTimer()
timers[id] = nil
callback()
end
id = event.timer(timeout, onTimer)
timers[id] = true
return id
end
pevent = setmetatable(pevent, {__index = event, __metatable = {}})
local env = setmetatable({event = pevent}, {__index = _ENV, __metatable = {}})
program, reason = loadfile(where, env)
if not program then
return nil, reason
end
event.ignore("term_available", onTermAvailable)
local result = table.pack(pcall(program, ...))
event.listen("term_available", onTermAvailable)
for name, list in pairs(listeners) do
for listener in pairs(list) do
event.ignore(name, listener, false)
end
end
for name, list in pairs(weakListeners) do
for listener in pairs(list) do
event.ignore(name, listener, true)
end
end
for id in pairs(timers) do
event.cancel(id)
end
return table.unpack(result, 1, result.n)
end
return function()
event.listen("term_available", onTermAvailable)
end