diff --git a/sys/apps/compat.lua b/sys/apps/compat.lua new file mode 100644 index 0000000..840f517 --- /dev/null +++ b/sys/apps/compat.lua @@ -0,0 +1,25 @@ +local Util = require('opus.util') + +-- some programs expect to be run in the global scope +-- ie. busted, moonscript + +-- create a new sandbox mimicing pure lua + +local fs = _G.fs +local shell = _ENV.shell + +local env = Util.shallowCopy(_G) +Util.merge(env, _ENV) +env._G = env + +env.arg = { ... } +env.arg[0] = shell.resolveProgram(table.remove(env.arg, 1)) + +_G.requireInjector(env, fs.getDir(env.arg[0])) + +local s, m = Util.run(env, env.arg[0], table.unpack(env.arg)) + +if not s then + error(m) +end + diff --git a/sys/apps/shell.lua b/sys/apps/shell.lua index d0002ce..b1cbe69 100644 --- a/sys/apps/shell.lua +++ b/sys/apps/shell.lua @@ -6,7 +6,7 @@ local fs = _G.fs local settings = _G.settings local shell = _ENV.shell -_G.requireInjector(_ENV) +--_G.requireInjector(_ENV) local trace = require('opus.trace') local Util = require('opus.util') @@ -43,6 +43,11 @@ local function run(...) local isUrl = not not command:match("^(https?:)") local env = shell.makeEnv(_ENV) + if command:match('(.+)%.moon$') then + table.insert(args, 1, command) + command = 'moon' + end + local path, loadFn if isUrl then path = command diff --git a/sys/boot/opus.boot b/sys/boot/opus.boot index f189ad5..c0d022d 100644 --- a/sys/boot/opus.boot +++ b/sys/boot/opus.boot @@ -1,5 +1,10 @@ local fs = _G.fs +-- override bios function to use the global scope of the current env +function _G.loadstring(string, chunkname) + return load(string, chunkname, nil, getfenv(2)._G) +end + -- override bios function to include the actual filename function _G.loadfile(filename, mode, env) -- Support the previous `loadfile(filename, env)` form instead. @@ -20,12 +25,17 @@ for k,v in pairs(_ENV) do sandboxEnv[k] = v end +-- Install require shim +_G.requireInjector = loadfile('sys/modules/opus/injector.lua', _ENV)() + local function run(file, ...) local env = setmetatable({ }, { __index = _G }) for k,v in pairs(sandboxEnv) do env[k] = v end + _G.requireInjector(env) + local s, m = loadfile(file, env) if s then return s(...) @@ -36,9 +46,6 @@ end _G._syslog = function() end _G.OPUS_BRANCH = 'develop-1.8' --- Install require shim -_G.requireInjector = run('sys/modules/opus/injector.lua') - local s, m = pcall(run, 'sys/apps/shell.lua', 'sys/kernel.lua', ...) if not s then diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 4734981..35fd6ce 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -83,6 +83,18 @@ function nativefs.isDir(node, dir) return fs.native.isDir(dir) end +function nativefs.attributes(node, path) + if node.mountPoint == path then + return { + created = node.created or os.epoch('utc'), + modification = node.modification or os.epoch('utc'), + isDir = not not node.nodes, + size = node.size or 0, + } + end + return fs.native.attributes(path) +end + function nativefs.exists(node, dir) if node.mountPoint == dir then return true @@ -307,12 +319,16 @@ function fs.mount(path, fstype, ...) tp.nodes[d] = Util.shallowCopy(tp) tp.nodes[d].nodes = { } tp.nodes[d].mountPoint = fs.combine(tp.mountPoint, d) + tp.nodes[d].created = os.epoch('utc') + tp.nodes[d].modification = os.epoch('utc') end tp = tp.nodes[d] end node.fs = vfs node.fstype = fstype + node.created = node.created or os.epoch('utc') + node.modification = node.modification or os.epoch('utc') if not targetName then node.mountPoint = '' fs.nodes = node diff --git a/sys/kernel.lua b/sys/kernel.lua index 3a86708..833631d 100644 --- a/sys/kernel.lua +++ b/sys/kernel.lua @@ -20,7 +20,7 @@ local w, h = term.getSize() kernel.terminal = term.current() kernel.window = Terminal.window(kernel.terminal, 1, 1, w, h, false) -kernel.window.setMaxScroll(100) +kernel.window.setMaxScroll(200) local focusedRoutineEvents = Util.transpose { 'char', 'key', 'key_up', diff --git a/sys/modules/opus/fs/urlfs.lua b/sys/modules/opus/fs/urlfs.lua index e9a4193..ff49176 100644 --- a/sys/modules/opus/fs/urlfs.lua +++ b/sys/modules/opus/fs/urlfs.lua @@ -20,8 +20,8 @@ function urlfs.mount(path, url, force) end end -function urlfs.attributes(node) - return { +function urlfs.attributes(node, path) + return path == node.mountPoint and { created = node.created, isDir = false, modification = node.modification, @@ -29,16 +29,18 @@ function urlfs.attributes(node) } end -function urlfs.delete(_, dir) - fs.unmount(dir) +function urlfs.delete(node, path) + if path == node.mountPoint then + fs.unmount(path) + end end -function urlfs.exists() - return true +function urlfs.exists(node, path) + return path == node.mountPoint end -function urlfs.getSize(node) - return node.size or 0 +function urlfs.getSize(node, path) + return path == node.mountPoint and node.size or 0 end function urlfs.isReadOnly() @@ -65,14 +67,6 @@ function urlfs.open(node, fn, fl) local c = node.cache if not c then - --[[ - if node.url:match("^(rttps?:)") then - local s, response = rttp.get(node.url) - c = s and response.statusCode == 200 and response.data - else - c = Util.httpGet(node.url) - end - ]]-- c = Util.httpGet(node.url) if c then node.cache = c diff --git a/sys/modules/opus/injector.lua b/sys/modules/opus/injector.lua index 634babd..8f66685 100644 --- a/sys/modules/opus/injector.lua +++ b/sys/modules/opus/injector.lua @@ -1,73 +1,69 @@ -- https://www.lua.org/manual/5.1/manual.html#pdf-require +-- https://github.com/LuaDist/lua/blob/d2e7e7d4d43ff9068b279a617c5b2ca2c2771676/src/loadlib.c -local function split(str, pattern) - local t = { } - local function helper(line) table.insert(t, line) return "" end - helper((str:gsub(pattern, helper))) - return t -end +local defaultPath = { } -local hasMain -local luaPaths = package and package.path and split(package.path, '(.-);') or { } -for i = 1, #luaPaths do - if luaPaths[i] == '?' or luaPaths[i] == '?.lua' or luaPaths[i] == '?/init.lua' then - luaPaths[i] = nil - elseif string.find(luaPaths[i], '/rom/modules/main') then - hasMain = true +do + local function split(str) + local t = { } + local function helper(line) table.insert(t, line) return "" end + helper((str:gsub('(.-);', helper))) + return t + end + + local function insert(p) + for _,v in pairs(defaultPath) do + if v == p then + return + end + end + table.insert(defaultPath, p) + + end + + local paths = '?.lua;?/init.lua;' + paths = paths .. '/usr/modules/?.lua;/usr/modules/?/init.lua;' + paths = paths .. '/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua;' + paths = paths .. '/sys/modules/?.lua;/sys/modules/?/init.lua' + + for _,v in pairs(split(paths)) do + insert(v) + end + + local luaPaths = package and package.path and split(package.path) or { } + for _,v in pairs(luaPaths) do + if v ~= '?' then + insert(v) + end end end -table.insert(luaPaths, 1, '?.lua') -table.insert(luaPaths, 2, '?/init.lua') -table.insert(luaPaths, 3, '/usr/modules/?.lua') -table.insert(luaPaths, 4, '/usr/modules/?/init.lua') -if not hasMain then - table.insert(luaPaths, 5, '/rom/modules/main/?') - table.insert(luaPaths, 6, '/rom/modules/main/?.lua') - table.insert(luaPaths, 7, '/rom/modules/main/?/init.lua') -end -table.insert(luaPaths, '/sys/modules/?.lua') -table.insert(luaPaths, '/sys/modules/?/init.lua') - -local DEFAULT_PATH = table.concat(luaPaths, ';') +local DEFAULT_PATH = table.concat(defaultPath, ';') local fs = _G.fs local os = _G.os local string = _G.string -- Add require and package to the environment -return function(env) +return function(env, programDir) local function preloadSearcher(modname) if env.package.preload[modname] then return function() return env.package.preload[modname](modname, env) end end - --end - - --local function loadedSearcher(modname) - if env.package.loaded[modname] then - return function() - return env.package.loaded[modname] - end - end end - local sentinel = { } - local function pathSearcher(modname) - if env.package.loaded[modname] == sentinel then - error("loop or previous error loading module '" .. modname .. "'", 0) - end - env.package.loaded[modname] = sentinel - local fname = modname:gsub('%.', '/') for pattern in string.gmatch(env.package.path, "[^;]+") do local sPath = string.gsub(pattern, "%?", fname) -- TODO: if there's no shell, we should not be checking relative paths below -- as they will resolve to root directory - if env.shell + if programDir and sPath:sub(1, 1) ~= "/" then + sPath = fs.combine(programDir, sPath) + elseif env.shell and type(env.shell.getRunningProgram) == 'function' and sPath:sub(1, 1) ~= "/" then @@ -81,38 +77,58 @@ return function(env) -- place package and require function into env env.package = { - path = env.LUA_PATH or _G.LUA_PATH or DEFAULT_PATH, - config = '/\n:\n?\n!\n-', + path = env.LUA_PATH or _G.LUA_PATH or DEFAULT_PATH, + cpath = '', + config = '/\n:\n?\n!\n-', preload = { }, - loaded = { + loaded = { + bit32 = bit32, coroutine = coroutine, + _G = env._G, io = io, math = math, os = os, string = string, table = table, debug = debug, + utf8 = utf8, }, loaders = { preloadSearcher, - --loadedSearcher, pathSearcher, } } + env.package.loaded.package = env.package + + local sentinel = { } function env.require(modname) + if env.package.loaded[modname] then + if env.package.loaded[modname] == sentinel then + error("loop or previous error loading module '" .. modname .. "'", 0) + end + return env.package.loaded[modname] + end + + local t = { } for _,searcher in ipairs(env.package.loaders) do local fn, msg = searcher(modname) - if fn then + if type(fn) == 'function' then + env.package.loaded[modname] = sentinel + local module = fn(modname, env) or true env.package.loaded[modname] = module return module end if msg then - error(msg, 2) + table.insert(t, msg) end end + + if #t > 0 then + error(table.concat(t, '\n'), 2) + end error('Unable to find module ' .. modname, 2) end diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 8dfa148..484231b 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -596,7 +596,6 @@ function Util.loadUrl(url, env) -- loadfile equivalent end function Util.runUrl(env, url, ...) -- os.run equivalent - setmetatable(env, { __index = _G }) local fn, m = Util.loadUrl(url, env) if fn then return pcall(fn, ...) @@ -606,7 +605,6 @@ end function Util.run(env, path, ...) if type(env) ~= 'table' then error('Util.run: env must be a table', 2) end - setmetatable(env, { __index = _G }) local fn, m = loadfile(path, env) if fn then return pcall(fn, ...) @@ -616,7 +614,6 @@ end function Util.runFunction(env, fn, ...) setfenv(fn, env) - setmetatable(env, { __index = _G }) return pcall(fn, ...) end