-
Notifications
You must be signed in to change notification settings - Fork 34
Expand file tree
/
Copy pathlua_base.h
More file actions
199 lines (161 loc) · 5.74 KB
/
Copy pathlua_base.h
File metadata and controls
199 lines (161 loc) · 5.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/*
This file is part of Iceball.
Iceball is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Iceball is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Iceball. If not, see <http://www.gnu.org/licenses/>.
*/
int icelua_fn_base_loadfile(lua_State *L)
{
int top = icelua_assert_stack(L, 1, 1);
const char *fname = lua_tostring(L, 1);
if(L == lstate_server
? !path_type_server_readable(path_get_type(fname))
: !path_type_client_readable(path_get_type(fname)))
{
return luaL_error(L, "cannot read from there");
}
lua_getglobal(L, "common");
lua_getfield(L, -1, "fetch_block");
lua_remove(L, -2);
lua_pushstring(L, "lua");
lua_pushvalue(L, 1);
lua_call(L, 2, 1);
return 1;
}
int icelua_fn_base_dofile(lua_State *L)
{
int top = icelua_assert_stack(L, 1, 1);
lua_pushcfunction(L, icelua_fn_base_loadfile);
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
// TODO: pcall this
lua_call(L, 0, 0);
return 0;
}
static const int sentinel = 0;
// Attempt to fetch requested module from multiple possible lookup paths
// i.e. "derp.herp" -> {"pkg/derp/herp.lua", "pkg/derp/herp/init.lua", ...}
// Params: mod_name
// Returns chunk if found, errors if not
static int icelua_fn_base_require_helper(lua_State *L)
{
// mod_name = stack[1]
const char *mod_name = luaL_checkstring(L, 1);
// mod_path = stack[2] -- Convert . to /
const char *mod_path = luaL_gsub(L, mod_name, ".", "/");
// paths = stack[3] = ICELUA_REQUIRE_PATH
lua_getfield(L, LUA_REGISTRYINDEX, "ICELUA_REQUIRE_PATH");
int i;
for (i = 1;; i++) {
// path_template = stack[4]
lua_rawgeti(L, 3, i);
if (lua_isnil(L, -1)) {
// End of list
lua_pop(L, 1); // Pop off nil
break;
}
lua_pushcfunction(L, icelua_fn_base_loadfile);
// file_path = stack[6] -- sub mod_path into path_template in place of "?"
luaL_gsub(L, lua_tostring(L, 4), "?", lua_tostring(L, 2));
if(lua_pcall(L, 1, 1, 0) == 0 && !lua_isnil(L, -1)) {
// Loaded! Return loadfile return value
return 1;
} // else, continue looking
lua_pop(L, 2); // Pop off path value and loadfile result
}
// Module not found in any of paths
luaL_error(L, "module " LUA_QS " not found", mod_name);
return 0;
}
int icelua_fn_base_require(lua_State *L)
{
// TODO: Do we want to offer the functionality that the vanilla
// package module provides? i.e. custom loaders, search path, etc.?
// We don't use the builtin one as we'd have to remove a lot of stuff
// to ensure security, so this is easier. If we want full functionality
// though, that may be something to look into. - rakiru
int top = icelua_assert_stack(L, 1, 1);
// mod_name = stack[1]
const char *mod_name = luaL_checkstring(L, 1);
// loaded = stack[2] = LUA_REGISTRYINDEX["_LOADED"]
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
// if loaded == nil
if (lua_isnil(L, 2)) {
lua_pop(L, 2);
// loaded = LUA_REGISTRYINDEX["_LOADED"] = {}
lua_createtable(L, 0, 2);
lua_pushvalue(L, 2); // Dupe table since setfield pops but we want a copy remaining
lua_setfield(L, LUA_REGISTRYINDEX, "_LOADED");
}
// module = stack[3] = loaded[mod_name]
lua_getfield(L, 2, mod_name);
// module?
if (lua_toboolean(L, 3)) {
// Check for loops or previous error while loading
if (lua_touserdata(L, 3) == ((void*)&sentinel)) {
luaL_error(L, "loop or previous error loading module " LUA_QS, mod_name);
}
// Already loaded successfully
return 1;
}
lua_pop(L, 1); // Remove nil module from stack
// Set sentinel to detect loops/error on future require(mod_name)
lua_pushlightuserdata(L, (void*)&sentinel);
lua_setfield(L, 2, mod_name);
// Attempt module path lookup
// module_file = stack[3] = loadfile(mod_name) magic
lua_pushcfunction(L, icelua_fn_base_require_helper);
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
// module = stack[3] = module_file(mod_name) -- module_file is popped
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
// if module == nil then module = true
if (lua_isnil(L, 3)) {
lua_pop(L, 1); // Remove nil from stack
lua_pushboolean(L, 1); // Push true in its place
}
// loaded[mod_name] = module
lua_pushvalue(L, 3); // Dupe value as setfield pops
lua_setfield(L, 2, mod_name);
// return module
return 1;
}
// Install package related stuff to state
void icelua_openpackage(lua_State *L)
{
// We're not providing most of `package` anyway, so just make this a table
// instead of a string as in `package.path` - rakiru
// TODO: Actually, keeping as a hardcoded, internal path allows some
// optimisations, // like doing the lookup server-side to avoid the extra
// latency for files further down the list of paths.
int key = 1;
// Note: Ensure you alter this value if adding/removing paths
lua_createtable(L, 5, 0);
// Most packages will be in pkg/
lua_pushinteger(L, key++);
lua_pushstring(L, "pkg/?.lua");
lua_settable(L, -3);
lua_pushinteger(L, key++);
lua_pushstring(L, "pkg/?/init.lua");
lua_settable(L, -3);
// 3rd party libs can go here without cluttering up the base pkg/ dir
lua_pushinteger(L, key++);
lua_pushstring(L, "pkg/vendor/?.lua");
lua_settable(L, -3);
lua_pushinteger(L, key++);
lua_pushstring(L, "pkg/vendor/?/init.lua");
lua_settable(L, -3);
// On the off-chance you wanted to load code from clsave/svsave or whatever
lua_pushinteger(L, key++);
lua_pushstring(L, "?.lua");
lua_settable(L, -3);
lua_setfield(L, LUA_REGISTRYINDEX, "ICELUA_REQUIRE_PATH");
}