mirror of
https://github.com/NeoFlock/neonucleus.git
synced 2025-09-25 01:23:31 +02:00
145 lines
3.9 KiB
Lua
145 lines
3.9 KiB
Lua
local fs = require("filesystem")
|
|
local unicode = require("unicode")
|
|
local process = require("process")
|
|
|
|
local shell = {}
|
|
|
|
-- Cache loaded shells for command execution. This puts the requirement on
|
|
-- shells that they do not keep a global state, since they may be called
|
|
-- multiple times, but reduces memory usage a lot.
|
|
local shells = setmetatable({}, {__mode="v"})
|
|
|
|
function shell.getShell()
|
|
local shellPath = os.getenv("SHELL") or "/bin/sh"
|
|
local shellName, reason = shell.resolve(shellPath, "lua")
|
|
if not shellName then
|
|
return nil, "cannot resolve shell `" .. shellPath .. "': " .. reason
|
|
end
|
|
if shells[shellName] then
|
|
return shells[shellName]
|
|
end
|
|
local sh, load_reason = loadfile(shellName, nil, setmetatable({}, {__index=_G}))
|
|
if sh then
|
|
shells[shellName] = sh
|
|
end
|
|
return sh, load_reason
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
function shell.prime()
|
|
local data = process.info().data
|
|
for _,key in ipairs({'aliases','vars'}) do
|
|
-- first time get need to populate
|
|
local raw = rawget(data, key)
|
|
if not raw then
|
|
-- current process does not have the key
|
|
local current = data[key]
|
|
data[key] = {}
|
|
if current then
|
|
for k,v in pairs(current) do
|
|
data[key][k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function shell.getAlias(alias)
|
|
return process.info().data.aliases[alias]
|
|
end
|
|
|
|
function shell.setAlias(alias, value)
|
|
checkArg(1, alias, "string")
|
|
checkArg(2, value, "string", "nil")
|
|
process.info().data.aliases[alias] = value
|
|
end
|
|
|
|
function shell.getWorkingDirectory()
|
|
-- if no env PWD default to /
|
|
return os.getenv("PWD") or "/"
|
|
end
|
|
|
|
function shell.setWorkingDirectory(dir)
|
|
checkArg(1, dir, "string")
|
|
-- ensure at least /
|
|
-- and remove trailing /
|
|
dir = fs.canonical(dir):gsub("^$", "/"):gsub("(.)/$", "%1")
|
|
if fs.isDirectory(dir) then
|
|
os.setenv("PWD", dir)
|
|
return true
|
|
else
|
|
return nil, "not a directory"
|
|
end
|
|
end
|
|
|
|
function shell.resolve(path, ext)
|
|
checkArg(1, path, "string")
|
|
|
|
local dir = path
|
|
if dir:find("/") ~= 1 then
|
|
dir = fs.concat(shell.getWorkingDirectory(), dir)
|
|
end
|
|
local name = fs.name(path)
|
|
dir = fs[name and "path" or "canonical"](dir)
|
|
local fullname = fs.concat(dir, name or "")
|
|
|
|
if not ext then
|
|
return fullname
|
|
elseif name then
|
|
checkArg(2, ext, "string")
|
|
-- search for name in PATH if no dir was given
|
|
-- no dir was given if path has no /
|
|
local search_in = path:find("/") and dir or os.getenv("PATH")
|
|
for search_path in string.gmatch(search_in, "[^:]+") do
|
|
-- resolve search_path because they may be relative
|
|
local search_name = fs.concat(shell.resolve(search_path), name)
|
|
if not fs.exists(search_name) then
|
|
search_name = search_name .. "." .. ext
|
|
end
|
|
-- extensions are provided when the caller is looking for a file
|
|
if fs.exists(search_name) and not fs.isDirectory(search_name) then
|
|
return search_name
|
|
end
|
|
end
|
|
end
|
|
|
|
return nil, "file not found"
|
|
end
|
|
|
|
function shell.parse(...)
|
|
local params = table.pack(...)
|
|
local args = {}
|
|
local options = {}
|
|
local doneWithOptions = false
|
|
for i = 1, params.n do
|
|
local param = params[i]
|
|
if not doneWithOptions and type(param) == "string" then
|
|
if param == "--" then
|
|
doneWithOptions = true -- stop processing options at `--`
|
|
elseif param:sub(1, 2) == "--" then
|
|
local key, value = param:match("%-%-(.-)=(.*)")
|
|
if not key then
|
|
key, value = param:sub(3), true
|
|
end
|
|
options[key] = value
|
|
elseif param:sub(1, 1) == "-" and param ~= "-" then
|
|
for j = 2, unicode.len(param) do
|
|
options[unicode.sub(param, j, j)] = true
|
|
end
|
|
else
|
|
table.insert(args, param)
|
|
end
|
|
else
|
|
table.insert(args, param)
|
|
end
|
|
end
|
|
return args, options
|
|
end
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
require("package").delay(shell, "/lib/core/full_shell.lua")
|
|
|
|
return shell
|