mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-10 15:56:41 -04:00
OpenOS 1.6.3 update (#2414)
Why a version change? > This minor version change should not introduce breaking changes, but older code doing non standard things with process environments, `require` and the package library, shebang redirections, or direct calls to /bin/lua, may see some breakages. Also, `shell.resolve` has been fully reworked. This is a crucial and heavily used api. Thus, in case of any mistakes or bugs a version change will help pinpoint regressions. I've heavily tested the resolve code, but it is new code and deserves some bake time in the wild. Changelog: /bin/lua removes shebang line and calls load directly. Improves workflows that define custom environments, and loadfile no longer removes shebang lines load: loaded code chunks now inherit the parent _ENV naturally, having a real-lua behavior require: heavily optimized and made errors more natural and helpful, exposing more information about failure to require a library /lib/pipes: commented out pipes.create which is a future feature to create event-boxed threads process: more process crash text is dumped to the shell to help identify "out of memory" issues shell.resolve: reworked and optimized! possibly breaking change: specifying an extension (ext) to `shell.resolve(name, ext)` will no longer return results to directories, but only files (if they exist). However, it was never the intent of this method to return results to directories when specifying an extension various memory optimizations throughout. openos now allocates ~153k to boot to shell.
This commit is contained in:
parent
f297eefd7d
commit
06a0861597
@ -5,13 +5,23 @@ if #args == 0 then
|
||||
args = {"/opt/core/lua_shell.lua"}
|
||||
end
|
||||
|
||||
local script, reason = loadfile(args[1], nil, setmetatable({},{__index=_ENV}))
|
||||
local filename = args[1]
|
||||
local buffer, script, reason
|
||||
buffer = io.lines(filename, "*a")()
|
||||
if buffer then
|
||||
buffer = buffer:gsub("^#![^\n]+", "") -- remove shebang if any
|
||||
script, reason = load(buffer, "="..filename)
|
||||
else
|
||||
reason = string.format("could not open %s for reading", filename)
|
||||
end
|
||||
|
||||
if not script then
|
||||
io.stderr:write(tostring(reason) .. "\n")
|
||||
os.exit(false)
|
||||
end
|
||||
local result, reason = pcall(script, table.unpack(args, 2))
|
||||
if not result then
|
||||
|
||||
buffer, reason = pcall(script, table.unpack(args, 2))
|
||||
if not buffer then
|
||||
io.stderr:write(reason, "\n")
|
||||
os.exit(false)
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
function loadfile(filename, mode, env)
|
||||
function loadfile(filename, ...)
|
||||
if filename:sub(1,1) ~= "/" then
|
||||
filename = (os.getenv("PWD") or "/") .. "/" .. filename
|
||||
end
|
||||
@ -16,11 +16,9 @@ function loadfile(filename, mode, env)
|
||||
end
|
||||
break
|
||||
end
|
||||
table.insert(buffer, data)
|
||||
buffer[#buffer + 1] = data
|
||||
end
|
||||
buffer[1] = (buffer[1] or ""):gsub("^#![^\n]+", "") -- remove shebang if any
|
||||
buffer = table.concat(buffer)
|
||||
return load(buffer, "=" .. filename, mode, env)
|
||||
return load(table.concat(buffer), "=" .. filename, ...)
|
||||
end
|
||||
|
||||
function dofile(filename)
|
||||
@ -35,12 +33,10 @@ function print(...)
|
||||
local args = table.pack(...)
|
||||
local stdout = io.stdout
|
||||
stdout:setvbuf("line")
|
||||
local pre = ""
|
||||
for i = 1, args.n do
|
||||
local arg = tostring(args[i])
|
||||
if i > 1 then
|
||||
arg = "\t" .. arg
|
||||
end
|
||||
stdout:write(arg)
|
||||
stdout:write(pre, tostring(args[i]))
|
||||
pre = "\t"
|
||||
end
|
||||
stdout:write("\n")
|
||||
stdout:setvbuf("no")
|
||||
|
@ -27,10 +27,18 @@ local kernel_load = _G.load
|
||||
local intercept_load
|
||||
intercept_load = function(source, label, mode, env)
|
||||
if env then
|
||||
env.load = kernel_load([[
|
||||
local source, label, mode, env = ...
|
||||
return load(source, label, mode, env or fenv)
|
||||
]], "=load", "t", {fenv=env, load=intercept_load})
|
||||
local loader = setmetatable(
|
||||
{
|
||||
env = env,
|
||||
load = intercept_load,
|
||||
},
|
||||
{__call = function(tbl, _source, _label, _mode, _env)
|
||||
return tbl.load(_source, _label, _mode, _env or tbl.env)
|
||||
end})
|
||||
if env.load and (type(env.load) ~= "table" or env.load.load ~= intercept_load) then
|
||||
loader.load = env.load
|
||||
end
|
||||
env.load = loader
|
||||
end
|
||||
return kernel_load(source, label, mode, env or process.info().env)
|
||||
end
|
||||
|
@ -2,11 +2,7 @@ local computer = require("computer")
|
||||
local event = require("event")
|
||||
local fs = require("filesystem")
|
||||
local shell = require("shell")
|
||||
local process = require("process")
|
||||
|
||||
local function env()
|
||||
return process.info().data.vars
|
||||
end
|
||||
local info = require("process").info
|
||||
|
||||
os.execute = function(command)
|
||||
if not command then
|
||||
@ -20,7 +16,7 @@ function os.exit(code)
|
||||
end
|
||||
|
||||
function os.getenv(varname)
|
||||
local env = env()
|
||||
local env = info().data.vars
|
||||
if not varname then
|
||||
return env
|
||||
elseif varname == '#' then
|
||||
@ -34,7 +30,7 @@ function os.setenv(varname, value)
|
||||
if value ~= nil then
|
||||
value = tostring(value)
|
||||
end
|
||||
env()[varname] = value
|
||||
info().data.vars[varname] = value
|
||||
return value
|
||||
end
|
||||
|
||||
|
@ -1,11 +1,6 @@
|
||||
local buffer = require("buffer")
|
||||
local tty = require("tty")
|
||||
|
||||
local io_open = io.open
|
||||
function io.open(path, mode)
|
||||
return io_open(require("shell").resolve(path), mode)
|
||||
end
|
||||
|
||||
local stdinStream = {handle="stdin"}
|
||||
local stdoutStream = {handle="stdout"}
|
||||
local stderrStream = {handle="stderr"}
|
||||
@ -15,13 +10,13 @@ local function badFileDescriptor()
|
||||
return nil, "bad file descriptor"
|
||||
end
|
||||
|
||||
function stdinStream:close()
|
||||
function stdinStream.close()
|
||||
return nil, "cannot close standard file"
|
||||
end
|
||||
stdoutStream.close = stdinStream.close
|
||||
stderrStream.close = stdinStream.close
|
||||
|
||||
function stdinStream:read()
|
||||
function stdinStream.read()
|
||||
return tty.read(stdinHistory)
|
||||
end
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
#!/bin/lua
|
||||
|
||||
local component = require("component")
|
||||
local computer = require("computer")
|
||||
local text = require("text")
|
||||
|
@ -36,7 +36,8 @@ end
|
||||
|
||||
function io.open(path, mode)
|
||||
-- These requires are not on top because this is a bootstrapped file.
|
||||
local stream, result = require("filesystem").open(path, mode)
|
||||
local resolved_path = require("shell").resolve(path)
|
||||
local stream, result = require("filesystem").open(resolved_path, mode)
|
||||
if stream then
|
||||
return require("buffer").new(mode, stream)
|
||||
else
|
||||
|
@ -1,5 +1,3 @@
|
||||
local component = require("component")
|
||||
|
||||
local keyboard = {pressedChars = {}, pressedCodes = {}}
|
||||
|
||||
-- these key definitions are only a subset of all the defined keys
|
||||
@ -32,16 +30,6 @@ keyboard.keys = {
|
||||
numpadenter = 0x9C,
|
||||
}
|
||||
|
||||
-- Create inverse mapping for name lookup.
|
||||
setmetatable(keyboard.keys,
|
||||
{
|
||||
__index = function(tbl, k)
|
||||
getmetatable(keyboard.keys).__index = nil -- to be safe
|
||||
loadfile(package.searchpath("tools/keyboard_full", package.path), "t", setmetatable({keyboard=keyboard},{__index=_G}))()
|
||||
return tbl[k]
|
||||
end
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function getKeyboardAddress(address)
|
||||
@ -94,4 +82,13 @@ end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
setmetatable(keyboard.keys,
|
||||
{
|
||||
__index = function(tbl, key)
|
||||
setmetatable(tbl, nil)
|
||||
dofile("/opt/core/full_keyboard.lua")
|
||||
return tbl[key] -- some keyboard keys are handled by __index by design
|
||||
end
|
||||
})
|
||||
|
||||
return keyboard
|
||||
|
@ -16,11 +16,6 @@ local loaded = {
|
||||
}
|
||||
package.loaded = loaded
|
||||
|
||||
local preload = {}
|
||||
package.preload = preload
|
||||
|
||||
package.searchers = {}
|
||||
|
||||
function package.searchpath(name, path, sep, rep)
|
||||
checkArg(1, name, "string")
|
||||
checkArg(2, path, "string")
|
||||
@ -47,67 +42,28 @@ function package.searchpath(name, path, sep, rep)
|
||||
return nil, table.concat(errorFiles, "\n")
|
||||
end
|
||||
|
||||
local function preloadSearcher(module)
|
||||
if preload[module] ~= nil then
|
||||
return preload[module]
|
||||
else
|
||||
return "\tno field package.preload['" .. module .. "']"
|
||||
end
|
||||
end
|
||||
|
||||
local function pathSearcher(module)
|
||||
local filepath, reason = package.searchpath(module, package.path)
|
||||
if filepath then
|
||||
local loader, reason = loadfile(filepath, "bt", _G)
|
||||
if loader then
|
||||
return loader, filepath
|
||||
else
|
||||
return reason
|
||||
end
|
||||
else
|
||||
return reason
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(package.searchers, preloadSearcher)
|
||||
table.insert(package.searchers, pathSearcher)
|
||||
|
||||
function require(module)
|
||||
checkArg(1, module, "string")
|
||||
if loaded[module] ~= nil then
|
||||
return loaded[module]
|
||||
elseif not loading[module] then
|
||||
loading[module] = true
|
||||
local loader, value, errorMsg = nil, nil, {"module '" .. module .. "' not found:"}
|
||||
for i = 1, #package.searchers do
|
||||
-- the pcall is mostly for out of memory errors
|
||||
local ok, f, extra = pcall(package.searchers[i], module)
|
||||
if not ok then
|
||||
table.insert(errorMsg, "\t" .. (f or "nil"))
|
||||
elseif f and type(f) ~= "string" then
|
||||
loader = f
|
||||
value = extra
|
||||
break
|
||||
elseif f then
|
||||
table.insert(errorMsg, f)
|
||||
end
|
||||
local library, status, step
|
||||
|
||||
step, library, status = "not found", package.searchpath(module, package.path)
|
||||
|
||||
if library then
|
||||
step, library, status = "loadfile failed", loadfile(library)
|
||||
end
|
||||
if loader then
|
||||
local success, result = pcall(loader, module, value)
|
||||
|
||||
if library then
|
||||
loading[module] = true
|
||||
step, library, status = "load failed", pcall(library, module)
|
||||
loading[module] = false
|
||||
if not success then
|
||||
error(result, 2)
|
||||
end
|
||||
if result then
|
||||
loaded[module] = result
|
||||
elseif not loaded[module] then
|
||||
loaded[module] = true
|
||||
end
|
||||
return loaded[module]
|
||||
else
|
||||
loading[module] = false
|
||||
error(table.concat(errorMsg, "\n"), 2)
|
||||
end
|
||||
|
||||
assert(library, string.format("module '%s' %s:\n%s", module, step, status))
|
||||
loaded[module] = status
|
||||
return status
|
||||
else
|
||||
error("already loading: " .. module .. "\n" .. debug.traceback(), 2)
|
||||
end
|
||||
|
@ -371,11 +371,14 @@ function plib.popen(prog, mode, env)
|
||||
return pfd
|
||||
end
|
||||
|
||||
function plib.create(fp, name)
|
||||
checkArg(1, fp, "function")
|
||||
checkArg(2, name, "string", "nil")
|
||||
local pco = plib.internal.create(fp, nil, name)
|
||||
return pco.stack[1]
|
||||
end
|
||||
-- function plib.create(fp, name)
|
||||
-- checkArg(1, fp, "function")
|
||||
-- checkArg(2, name, "string", "nil")
|
||||
|
||||
-- local pco = plib.internal.create(fp, function()end, name)
|
||||
-- -- process.info(pco.stack[1]).data.event = nil
|
||||
|
||||
-- return pco.stack[1]
|
||||
-- end
|
||||
|
||||
return plib
|
||||
|
@ -32,10 +32,10 @@ function process.load(path, env, init, name)
|
||||
local p = process.findProcess()
|
||||
env = env or p.env
|
||||
local code
|
||||
if type(path) == 'string' then
|
||||
if type(path) == "string" then
|
||||
code = function(...)
|
||||
local fs, shell = require("filesystem"), require("shell")
|
||||
local program, reason = shell.resolve(path, 'lua')
|
||||
local program, reason = shell.resolve(path, "lua")
|
||||
if not program then
|
||||
if fs.isDirectory(shell.resolve(path)) then
|
||||
io.stderr:write(path .. ": is a directory\n")
|
||||
@ -48,7 +48,7 @@ function process.load(path, env, init, name)
|
||||
os.setenv("_", program)
|
||||
local f = fs.open(program)
|
||||
if f then
|
||||
local shebang = f:read(1024):match("^#!([^\n]+)")
|
||||
local shebang = (f:read(1024) or ""):match("^#!([^\n]+)")
|
||||
f:close()
|
||||
if shebang then
|
||||
local result = table.pack(shell.execute(shebang:gsub("%s",""), env, program, ...))
|
||||
@ -59,7 +59,7 @@ function process.load(path, env, init, name)
|
||||
local command
|
||||
command, reason = loadfile(program, "bt", env)
|
||||
if not command then
|
||||
io.stderr:write(program..(reason or ""):gsub("^[^:]*", "").."\n")
|
||||
io.stderr:write(program, " ", reason or "", "\n")
|
||||
return 128
|
||||
end
|
||||
return command(...)
|
||||
@ -80,14 +80,14 @@ function process.load(path, env, init, name)
|
||||
end,
|
||||
function(msg)
|
||||
-- msg can be a custom error object
|
||||
if type(msg) == 'table' then
|
||||
if type(msg) == "table" then
|
||||
if msg.reason ~= "terminated" then
|
||||
io.stderr:write(msg.reason.."\n")
|
||||
end
|
||||
return msg.code or 0
|
||||
end
|
||||
local stack = debug.traceback():gsub('^([^\n]*\n)[^\n]*\n[^\n]*\n','%1')
|
||||
io.stderr:write(string.format('%s:\n%s', msg or '', stack))
|
||||
local stack = debug.traceback():gsub("^([^\n]*\n)[^\n]*\n[^\n]*\n","%1")
|
||||
io.stderr:write(string.format("%s:\n%s", msg or "", stack))
|
||||
return 128 -- syserr
|
||||
end, ...)
|
||||
}
|
||||
@ -107,13 +107,11 @@ function process.load(path, env, init, name)
|
||||
{
|
||||
handles = {},
|
||||
io = {},
|
||||
coroutine_handler = {}
|
||||
},
|
||||
parent = p,
|
||||
instances = setmetatable({}, {__mode="v"}),
|
||||
}
|
||||
setmetatable(new_proc.data.io, {__index=p.data.io})
|
||||
setmetatable(new_proc.data.coroutine_handler, {__index=p.data.coroutine_handler})
|
||||
setmetatable(new_proc.data, {__index=p.data})
|
||||
process.list[thread] = new_proc
|
||||
|
||||
|
@ -246,12 +246,12 @@ function sh.internal.createThreads(commands, env, start_args)
|
||||
local thread_env = type(program) == "string" and env or nil
|
||||
local thread, reason = process.load(program, thread_env, function(...)
|
||||
local cdata = process.info().data.command
|
||||
local args, has_tokens, start_args = cdata.args, cdata.has_tokens, cdata.start_args
|
||||
local args, has_tokens, c_start_args = cdata.args, cdata.has_tokens, cdata.start_args
|
||||
if has_tokens then
|
||||
args = sh.internal.buildCommandRedirects(args)
|
||||
end
|
||||
|
||||
sh.internal.concatn(args, start_args)
|
||||
sh.internal.concatn(args, c_start_args)
|
||||
sh.internal.concatn(args, {...}, select('#', ...))
|
||||
|
||||
-- popen expects each process to first write an empty string
|
||||
|
@ -18,60 +18,13 @@ function shell.getShell()
|
||||
if shells[shellName] then
|
||||
return shells[shellName]
|
||||
end
|
||||
local sh, reason = loadfile(shellName, "t")
|
||||
local sh, reason = loadfile(shellName)
|
||||
if sh then
|
||||
shells[shellName] = sh
|
||||
end
|
||||
return sh, reason
|
||||
end
|
||||
|
||||
local function findFile(name, ext)
|
||||
checkArg(1, name, "string")
|
||||
local function findIn(dir)
|
||||
if dir:sub(1, 1) ~= "/" then
|
||||
dir = shell.resolve(dir)
|
||||
end
|
||||
dir = fs.concat(fs.concat(dir, name), "..")
|
||||
local name = fs.name(name)
|
||||
local list = fs.list(dir)
|
||||
if list and name then
|
||||
local files = {}
|
||||
for file in list do
|
||||
files[file] = true
|
||||
end
|
||||
if ext and name:sub(-(1 + ext:len())) == "." .. ext then
|
||||
-- Name already contains extension, prioritize.
|
||||
if files[name] then
|
||||
return true, fs.concat(dir, name)
|
||||
end
|
||||
elseif files[name] then
|
||||
-- Check exact name.
|
||||
return true, fs.concat(dir, name)
|
||||
elseif ext then
|
||||
-- Check name with automatially added extension.
|
||||
local name = name .. "." .. ext
|
||||
if files[name] then
|
||||
return true, fs.concat(dir, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
if name:sub(1, 1) == "/" then
|
||||
local found, where = findIn("/")
|
||||
if found then return where end
|
||||
elseif name:sub(1, 2) == "./" then
|
||||
local found, where = findIn(shell.getWorkingDirectory())
|
||||
if found then return where end
|
||||
else
|
||||
for path in string.gmatch(shell.getPath(), "[^:]+") do
|
||||
local found, where = findIn(path)
|
||||
if found then return where end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function shell.prime()
|
||||
@ -133,21 +86,37 @@ function shell.setPath(value)
|
||||
end
|
||||
|
||||
function shell.resolve(path, ext)
|
||||
if ext then
|
||||
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")
|
||||
local where = findFile(path, ext)
|
||||
if where then
|
||||
return where
|
||||
else
|
||||
return nil, "file not found"
|
||||
end
|
||||
else
|
||||
if path:sub(1, 1) == "/" then
|
||||
return fs.canonical(path)
|
||||
else
|
||||
return fs.concat(shell.getWorkingDirectory(), path)
|
||||
-- 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 shell.getPath()
|
||||
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.execute(command, env, ...)
|
||||
@ -179,11 +148,11 @@ function shell.parse(...)
|
||||
if param == "--" then
|
||||
doneWithOptions = true -- stop processing options at `--`
|
||||
elseif param:sub(1, 2) == "--" then
|
||||
if param:match("%-%-(.-)=") ~= nil then
|
||||
options[param:match("%-%-(.-)=")] = param:match("=(.*)")
|
||||
else
|
||||
options[param:sub(3)] = true
|
||||
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
|
||||
|
@ -1,7 +1,7 @@
|
||||
-- called from /init.lua
|
||||
local raw_loadfile = ...
|
||||
|
||||
_G._OSVERSION = "OpenOS 1.6.2"
|
||||
_G._OSVERSION = "OpenOS 1.6.3"
|
||||
|
||||
local component = component
|
||||
local computer = computer
|
||||
@ -96,11 +96,11 @@ do
|
||||
package.loaded.component = component
|
||||
package.loaded.computer = computer
|
||||
package.loaded.unicode = unicode
|
||||
package.preload["buffer"] = loadfile("/lib/buffer.lua")
|
||||
package.preload["filesystem"] = loadfile("/lib/filesystem.lua")
|
||||
package.loaded.buffer = assert(loadfile("/lib/buffer.lua"))()
|
||||
package.loaded.filesystem = assert(loadfile("/lib/filesystem.lua"))()
|
||||
|
||||
-- Inject the io modules
|
||||
_G.io = loadfile("/lib/io.lua")()
|
||||
_G.io = assert(loadfile("/lib/io.lua"))()
|
||||
end
|
||||
|
||||
status("Initializing file system...")
|
||||
|
@ -1,3 +1,5 @@
|
||||
local keyboard = require("keyboard")
|
||||
|
||||
keyboard.keys["1"] = 0x02
|
||||
keyboard.keys["2"] = 0x03
|
||||
keyboard.keys["3"] = 0x04
|
||||
@ -132,7 +134,7 @@ setmetatable(keyboard.keys,
|
||||
{
|
||||
__index = function(tbl, k)
|
||||
if type(k) ~= "number" then return end
|
||||
for name,value in pairs(keyboard.keys) do
|
||||
for name,value in pairs(tbl.keys) do
|
||||
if value == k then
|
||||
return name
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user