Mercurial > prosody-hg
comparison util/require.lua @ 1965:f641e7ee171c
util.require: A replacement for Lua's require/module that doesn't load into the global environment by default (C modules still get past this)
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Sat, 17 Oct 2009 10:44:19 +0100 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1964:101a8df23b29 | 1965:f641e7ee171c |
|---|---|
| 1 -- Prosody IM | |
| 2 -- Copyright (C) 2008-2009 Matthew Wild | |
| 3 -- Copyright (C) 2008-2009 Waqas Hussain | |
| 4 -- | |
| 5 -- This project is MIT/X11 licensed. Please see the | |
| 6 -- COPYING file in the source package for more information. | |
| 7 -- | |
| 8 -- Based on Kepler Compat-5.1 code | |
| 9 -- Copyright Kepler Project 2004-2006 (http://www.keplerproject.org/compat) | |
| 10 -- $Id: compat-5.1.lua,v 1.22 2006/02/20 21:12:47 carregal Exp $ | |
| 11 -- | |
| 12 | |
| 13 local LUA_DIRSEP = '/' | |
| 14 local LUA_OFSEP = '_' | |
| 15 local OLD_LUA_OFSEP = '' | |
| 16 local POF = 'luaopen_' | |
| 17 local LUA_PATH_MARK = '?' | |
| 18 local LUA_IGMARK = ':' | |
| 19 | |
| 20 local assert, error, getfenv, ipairs, loadfile, loadlib, pairs, setfenv, setmetatable, type = assert, error, getfenv, ipairs, loadfile, loadlib, pairs, setfenv, setmetatable, type | |
| 21 local find, format, gfind, gsub, sub = string.find, string.format, string.gfind, string.gsub, string.sub | |
| 22 | |
| 23 if package.nonglobal_module then return; end | |
| 24 package.nonglobal_module = true; | |
| 25 | |
| 26 local _PACKAGE = package | |
| 27 local _LOADED = package.loaded | |
| 28 local _PRELOAD = package.preload | |
| 29 | |
| 30 -- | |
| 31 -- looks for a file `name' in given path | |
| 32 -- | |
| 33 local function findfile (name, pname) | |
| 34 name = gsub (name, "%.", LUA_DIRSEP) | |
| 35 local path = _PACKAGE[pname] | |
| 36 assert (type(path) == "string", format ("package.%s must be a string", pname)) | |
| 37 for c in gfind (path, "[^;]+") do | |
| 38 c = gsub (c, "%"..LUA_PATH_MARK, name) | |
| 39 local f = io.open (c) | |
| 40 if f then | |
| 41 f:close () | |
| 42 return c | |
| 43 end | |
| 44 end | |
| 45 return nil -- not found | |
| 46 end | |
| 47 | |
| 48 | |
| 49 -- | |
| 50 -- check whether library is already loaded | |
| 51 -- | |
| 52 local function loader_preload (name) | |
| 53 assert (type(name) == "string", format ( | |
| 54 "bad argument #1 to `require' (string expected, got %s)", type(name))) | |
| 55 assert (type(_PRELOAD) == "table", "`package.preload' must be a table") | |
| 56 return _PRELOAD[name] | |
| 57 end | |
| 58 | |
| 59 | |
| 60 -- | |
| 61 -- Lua library loader | |
| 62 -- | |
| 63 local function loader_Lua (name) | |
| 64 assert (type(name) == "string", format ( | |
| 65 "bad argument #1 to `require' (string expected, got %s)", type(name))) | |
| 66 local filename = findfile (name, "path") | |
| 67 if not filename then | |
| 68 return false | |
| 69 end | |
| 70 local f, err = loadfile (filename) | |
| 71 if not f then | |
| 72 error (format ("error loading module `%s' (%s)", name, err)) | |
| 73 end | |
| 74 return f | |
| 75 end | |
| 76 | |
| 77 | |
| 78 local function mkfuncname (name) | |
| 79 name = gsub (name, "^.*%"..LUA_IGMARK, "") | |
| 80 name = gsub (name, "%.", LUA_OFSEP) | |
| 81 return POF..name | |
| 82 end | |
| 83 | |
| 84 local function old_mkfuncname (name) | |
| 85 --name = gsub (name, "^.*%"..LUA_IGMARK, "") | |
| 86 name = gsub (name, "%.", OLD_LUA_OFSEP) | |
| 87 return POF..name | |
| 88 end | |
| 89 | |
| 90 -- | |
| 91 -- C library loader | |
| 92 -- | |
| 93 local function loader_C (name) | |
| 94 assert (type(name) == "string", format ( | |
| 95 "bad argument #1 to `require' (string expected, got %s)", type(name))) | |
| 96 local filename = findfile (name, "cpath") | |
| 97 if not filename then | |
| 98 return false | |
| 99 end | |
| 100 local funcname = mkfuncname (name) | |
| 101 local f, err = loadlib (filename, funcname) | |
| 102 if not f then | |
| 103 funcname = old_mkfuncname (name) | |
| 104 f, err = loadlib (filename, funcname) | |
| 105 if not f then | |
| 106 error (format ("error loading module `%s' (%s)", name, err)) | |
| 107 end | |
| 108 end | |
| 109 return f | |
| 110 end | |
| 111 | |
| 112 | |
| 113 local function loader_Croot (name) | |
| 114 local p = gsub (name, "^([^.]*).-$", "%1") | |
| 115 if p == "" then | |
| 116 return | |
| 117 end | |
| 118 local filename = findfile (p, "cpath") | |
| 119 if not filename then | |
| 120 return | |
| 121 end | |
| 122 local funcname = mkfuncname (name) | |
| 123 local f, err, where = loadlib (filename, funcname) | |
| 124 if f then | |
| 125 return f | |
| 126 elseif where ~= "init" then | |
| 127 error (format ("error loading module `%s' (%s)", name, err)) | |
| 128 end | |
| 129 end | |
| 130 | |
| 131 -- create `loaders' table | |
| 132 package.loaders = package.loaders or { loader_preload, loader_Lua, loader_C, loader_Croot, } | |
| 133 local _LOADERS = package.loaders | |
| 134 | |
| 135 | |
| 136 -- | |
| 137 -- iterate over available loaders | |
| 138 -- | |
| 139 local function load (name, loaders) | |
| 140 -- iterate over available loaders | |
| 141 assert (type (loaders) == "table", "`package.loaders' must be a table") | |
| 142 for i, loader in ipairs (loaders) do | |
| 143 local f = loader (name) | |
| 144 if f then | |
| 145 return f | |
| 146 end | |
| 147 end | |
| 148 error (format ("module `%s' not found", name)) | |
| 149 end | |
| 150 | |
| 151 -- sentinel | |
| 152 local sentinel = function () end | |
| 153 | |
| 154 local old_require = _G.require; | |
| 155 local dep_path = {}; | |
| 156 local current_env = nil; | |
| 157 function _G.require(modname) | |
| 158 --table.insert(dep_path, modname); | |
| 159 --if getfenv(2) == getfenv(0) --[[and rawget(_G, "__locked")]] then | |
| 160 -- print("**** Uh-oh, require called from locked global env at "..table.concat(dep_path, "->"), debug.traceback()); | |
| 161 --end | |
| 162 if not current_env and rawget(_G, "__locked") then | |
| 163 _G.prosody.unlock_globals(); | |
| 164 _G.__locked = false; | |
| 165 end | |
| 166 local old_current_env; | |
| 167 old_current_env, current_env = current_env, getfenv(2); | |
| 168 local ok, ret = pcall(old_require, modname); | |
| 169 current_env = old_current_env; | |
| 170 if not current_env and rawget(_G, "__locked") == false then | |
| 171 _G.prosody.lock_globals(); | |
| 172 end | |
| 173 --table.remove(dep_path); | |
| 174 if not ok then error(ret, 0); end | |
| 175 return ret; | |
| 176 end | |
| 177 | |
| 178 | |
| 179 -- findtable | |
| 180 local function findtable (t, f) | |
| 181 assert (type(f)=="string", "not a valid field name ("..tostring(f)..")") | |
| 182 local ff = f.."." | |
| 183 local ok, e, w = find (ff, '(.-)%.', 1) | |
| 184 while ok do | |
| 185 local nt = rawget (t, w) | |
| 186 if not nt then | |
| 187 nt = {} | |
| 188 t[w] = nt | |
| 189 elseif type(t) ~= "table" then | |
| 190 return sub (f, e+1) | |
| 191 end | |
| 192 t = nt | |
| 193 ok, e, w = find (ff, '(.-)%.', e+1) | |
| 194 end | |
| 195 return t | |
| 196 end | |
| 197 | |
| 198 -- | |
| 199 -- new package.seeall function | |
| 200 -- | |
| 201 function _PACKAGE.seeall (module) | |
| 202 local t = type(module) | |
| 203 assert (t == "table", "bad argument #1 to package.seeall (table expected, got "..t..")") | |
| 204 local meta = getmetatable (module) | |
| 205 if not meta then | |
| 206 meta = {} | |
| 207 setmetatable (module, meta) | |
| 208 end | |
| 209 meta.__index = _G | |
| 210 end | |
| 211 | |
| 212 | |
| 213 -- | |
| 214 -- new module function | |
| 215 -- | |
| 216 function _G.module (modname, ...) | |
| 217 local ns = _LOADED[modname]; | |
| 218 if type(ns) ~= "table" then | |
| 219 --if not current_env then | |
| 220 -- print("module outside require for "..modname.." at "..debug.traceback()); | |
| 221 --end | |
| 222 ns = findtable (current_env or getfenv(2), modname); | |
| 223 if not ns then | |
| 224 error (string.format ("name conflict for module '%s'", modname)) | |
| 225 end | |
| 226 _LOADED[modname] = ns | |
| 227 end | |
| 228 if not ns._NAME then | |
| 229 ns._NAME = modname | |
| 230 ns._M = ns | |
| 231 ns._PACKAGE = gsub (modname, "[^.]*$", "") | |
| 232 end | |
| 233 setfenv (2, ns) | |
| 234 for i, f in ipairs (arg) do | |
| 235 f (ns) | |
| 236 end | |
| 237 end |
