Get rid of DECLARE_LUACLASS
[lsnes.git] / src / lua / loadfile.cpp
blob59aea32691ad63ef3f29bc0e7695c382b5840de0
1 #include "lua/internal.hpp"
2 #include "library/minmax.hpp"
3 #include "library/zip.hpp"
4 #include "core/memorymanip.hpp"
5 #include <functional>
7 namespace
9 std::string dashstring(char ch, int dashes)
11 if(dashes)
12 return std::string(1, ch) + std::string(dashes, '=') + std::string(1, ch);
13 else
14 return std::string(1, ch) + std::string(1, ch);
17 struct replace
19 replace()
21 upper_buf = NULL;
22 upper_ptr = 0;
23 upper_size = 0;
24 upper_eof = false;
25 matched = 0;
26 copied = 0;
27 source = 0;
28 first_chunk = true;
29 target = "[[]]";
31 replace(const std::string& _target)
32 : replace()
34 target = _target;
36 std::pair<const char*, size_t> run(std::function<std::pair<const char*, size_t>()> fn);
37 private:
38 char buffer[4096];
39 std::string target;
40 size_t matched;
41 size_t copied;
42 int source;
43 const char* upper_buf;
44 size_t upper_ptr;
45 size_t upper_size;
46 bool upper_eof;
47 bool first_chunk;
50 std::string pattern = "@@LUA_SCRIPT_FILENAME@@";
52 std::pair<const char*, size_t> replace::run(std::function<std::pair<const char*, size_t>()> fn)
54 size_t emitted = 0;
55 while(emitted < sizeof(buffer)) {
56 while(upper_ptr == upper_size && !upper_eof) {
57 auto g = fn();
58 upper_buf = g.first;
59 upper_size = g.second;
60 upper_ptr = 0;
61 if(!upper_buf && !upper_size)
62 upper_eof = true;
63 if(first_chunk) {
64 static char binary_lua_id[] = {0x1B, 0x4C, 0x75, 0x61};
65 if(upper_size >= 4 && !memcmp(upper_buf, binary_lua_id, 4))
66 throw std::runtime_error("Binary Lua chunks are not allowed");
67 first_chunk = false;
70 if(upper_ptr == upper_size && source == 0) {
71 if(!matched)
72 break;
73 copied = 0;
74 source = 1;
76 switch(source) {
77 case 0: //Upper_buf.
78 if(upper_buf[upper_ptr] == pattern[matched]) {
79 matched++;
80 upper_ptr++;
81 if(matched == pattern.length()) {
82 source = 2;
83 copied = 0;
85 } else if(matched) {
86 //Flush the rest.
87 source = 1;
88 copied = 0;
89 } else {
90 buffer[emitted++] = upper_buf[upper_ptr++];
92 break;
93 case 1: //Source.
94 if(matched == 2 && upper_ptr < upper_size && upper_buf[upper_ptr] == '@') {
95 //This is exceptional, just flush the first '@'.
96 buffer[emitted++] = '@';
97 upper_ptr++;
98 matched = 2;
99 source = 0;
100 } else if(copied == matched) {
101 //End.
102 matched = 0;
103 source = 0;
104 } else {
105 buffer[emitted++] = pattern[copied++];
107 break;
108 case 2: //Target.
109 if(copied == target.size()) {
110 //End
111 matched = 0;
112 source = 0;
113 } else {
114 buffer[emitted++] = target[copied++];
116 break;
119 if(!emitted)
120 return std::make_pair(reinterpret_cast<const char*>(NULL), 0);
121 return std::make_pair(buffer, emitted);
124 struct reader
126 reader(std::istream& _s, const std::string& fn)
127 : s(_s)
129 int dashes = 0;
130 while(true) {
131 std::string tmpl = dashstring(']', dashes);
132 if(fn.find(tmpl) == std::string::npos)
133 break;
135 rpl = replace(dashstring('[', dashes) + fn + dashstring(']', dashes));
137 const char* rfn(lua_State* L, size_t* size);
138 static const char* rfn(lua_State* L, void* data, size_t* size)
140 return reinterpret_cast<reader*>(data)->rfn(L, size);
142 const std::string& get_err() { return err; }
143 private:
144 std::istream& s;
145 replace rpl;
146 std::string err;
149 class lua_file_reader
151 public:
152 lua_file_reader(lua_state& L, std::istream* strm);
153 ~lua_file_reader()
155 delete &s;
157 int read(lua_state& L, const std::string& fname)
159 if(L.type(2) == LUA_TNUMBER) {
160 //Read specified number of bytes.
161 size_t sz = L.get_numeric_argument<size_t>(2, fname.c_str());
162 std::vector<char> buf;
163 buf.resize(sz);
164 s.read(&buf[0], sz);
165 if(!s && !s.gcount()) {
166 L.pushnil();
167 return 1;
169 L.pushlstring(&buf[0], s.gcount());
170 return 1;
171 } else if(L.type(2) == LUA_TNIL || L.type(2) == LUA_TNONE) {
172 //Read next line.
173 std::string tmp;
174 std::getline(s, tmp);
175 if(!s) {
176 L.pushnil();
177 return 1;
179 istrip_CR(tmp);
180 L.pushlstring(tmp);
181 return 1;
182 } else
183 (stringfmt() << "Expected number or nil as the 2nd argument of " << fname).throwex();
185 int lines(lua_state& L, const std::string& fname)
187 L.pushlightuserdata(this);
188 L.pushcclosure(lua_file_reader::lines_helper2, 1);
189 //Trick: The first parameter is the userdata for this object, so by making it state, we
190 //can pin this object.
191 L.pushvalue(1);
192 L.pushboolean(true);
193 return 3;
195 int lines_helper(lua_State* L)
197 std::string tmp;
198 std::getline(s, tmp);
199 if(!s) {
200 lua_pushnil(L);
201 return 1;
203 istrip_CR(tmp);
204 lua_pushlstring(L, tmp.c_str(), tmp.length());
205 return 1;
207 static int lines_helper2(lua_State* L)
209 reinterpret_cast<lua_file_reader*>(lua_touserdata(L, lua_upvalueindex(1)))->lines_helper(L);
211 std::string print()
213 return "";
215 private:
216 std::istream& s;
219 const char* reader::rfn(lua_State* L, size_t* size)
221 try {
222 auto g = rpl.run([this]() -> std::pair<const char*, size_t> {
223 size_t size;
224 static char buffer[4096];
225 if(!this->s)
226 return std::make_pair(reinterpret_cast<const char*>(NULL), 0);
227 this->s.read(buffer, sizeof(buffer));
228 size = this->s.gcount();
229 if(!size) {
230 return std::make_pair(reinterpret_cast<const char*>(NULL), 0);
232 return std::make_pair(buffer, size);
234 *size = g.second;
235 return g.first;
236 } catch(std::exception& e) {
237 err = e.what();
238 *size = 0;
239 return NULL;
243 void load_chunk(lua_state& L, const std::string& fname)
245 std::string file2;
246 std::string file1 = L.get_string(1, fname.c_str());
247 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
248 file2 = L.get_string(2, fname.c_str());
249 std::string absfilename = zip::resolverel(file1, file2);
250 std::istream& file = zip::openrel(file1, file2);
251 std::string chunkname;
252 if(file2 != "")
253 chunkname = file2 + "[" + file1 + "]";
254 else
255 chunkname = file1;
256 reader rc(file, absfilename);
257 int r = lua_load(L.handle(), reader::rfn, &rc, chunkname.c_str()
258 #if LUA_VERSION_NUM == 502
259 , "t"
260 #endif
262 delete &file;
263 if(rc.get_err() != "")
264 throw std::runtime_error(rc.get_err());
265 if(r == 0) {
266 return;
267 } else if(r == LUA_ERRSYNTAX) {
268 (stringfmt() << "Syntax error: " << L.tostring(-1)).throwex();
269 } else if(r == LUA_ERRMEM) {
270 (stringfmt() << "Out of memory: " << L.tostring(-1)).throwex();
271 } else {
272 (stringfmt() << "Unknown error: " << L.tostring(-1)).throwex();
276 function_ptr_luafun loadfile2(lua_func_load, "loadfile2", [](lua_state& L, const std::string& fname)
277 -> int {
278 load_chunk(L, fname);
279 return 1;
282 function_ptr_luafun dofile2(lua_func_load, "dofile2", [](lua_state& L, const std::string& fname)
283 -> int {
284 load_chunk(L, fname);
285 int old_sp = lua_gettop(L.handle());
286 lua_call(L.handle(), 0, LUA_MULTRET);
287 int new_sp = lua_gettop(L.handle());
288 return new_sp - (old_sp - 1);
291 function_ptr_luafun resolvefile(lua_func_load, "resolve_filename", [](lua_state& L, const std::string& fname)
293 std::string file2;
294 std::string file1 = L.get_string(1, fname.c_str());
295 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
296 file2 = L.get_string(2, fname.c_str());
297 std::string absfilename = zip::resolverel(file1, file2);
298 L.pushlstring(absfilename);
299 return 1;
302 function_ptr_luafun openfile(lua_func_load, "open_file", [](lua_state& L, const std::string& fname) -> int {
303 std::string file2;
304 std::string file1 = L.get_string(1, fname.c_str());
305 if(L.type(2) != LUA_TNIL && L.type(2) != LUA_TNONE)
306 file2 = L.get_string(2, fname.c_str());
307 std::istream& s = zip::openrel(file1, file2);
308 try {
309 lua_class<lua_file_reader>::create(L, &s);
310 return 1;
311 } catch(...) {
312 delete &s;
313 throw;
317 lua_class<lua_file_reader> class_filreader("FILEREADER");
319 lua_file_reader::lua_file_reader(lua_state& L, std::istream* strm)
320 : s(*strm)
322 objclass<lua_file_reader>().bind_multi(L, {
323 {"__call", &lua_file_reader::read},
324 {"lines", &lua_file_reader::lines}