Cleanup lua code by introducing lua::functions
[lsnes.git] / src / lua / loadfile.cpp
blobb63ce1beb4e175569b1163669275fe2b459f4556
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 static int create(lua::state& L, lua::parameters& P)
159 auto file1 = P.arg<std::string>();
160 auto file2 = P.arg_opt<std::string>("");
161 std::istream& s = zip::openrel(file1, file2);
162 try {
163 lua::_class<lua_file_reader>::create(L, &s);
164 return 1;
165 } catch(...) {
166 delete &s;
167 throw;
170 int read(lua::state& L, lua::parameters& P)
172 P(P.skipped());
174 if(P.is_number()) {
175 //Read specified number of bytes.
176 size_t sz;
177 P(sz);
178 std::vector<char> buf;
179 buf.resize(sz);
180 s.read(&buf[0], sz);
181 if(!s && !s.gcount()) {
182 L.pushnil();
183 return 1;
185 L.pushlstring(&buf[0], s.gcount());
186 return 1;
187 } else if(P.is_novalue()) {
188 //Read next line.
189 std::string tmp;
190 std::getline(s, tmp);
191 if(!s) {
192 L.pushnil();
193 return 1;
195 istrip_CR(tmp);
196 L.pushlstring(tmp);
197 return 1;
198 } else
199 P.expected("number or nil");
201 int lines(lua::state& L, lua::parameters& P)
203 L.pushlightuserdata(this);
204 L.pushcclosure(lua_file_reader::lines_helper2, 1);
205 //Trick: The first parameter is the userdata for this object, so by making it state, we
206 //can pin this object.
207 L.pushvalue(1);
208 L.pushboolean(true);
209 return 3;
211 int lines_helper(lua_State* L)
213 std::string tmp;
214 std::getline(s, tmp);
215 if(!s) {
216 lua_pushnil(L);
217 return 1;
219 istrip_CR(tmp);
220 lua_pushlstring(L, tmp.c_str(), tmp.length());
221 return 1;
223 static int lines_helper2(lua_State* L)
225 reinterpret_cast<lua_file_reader*>(lua_touserdata(L, lua_upvalueindex(1)))->lines_helper(L);
227 private:
228 std::istream& s;
231 const char* reader::rfn(lua_State* L, size_t* size)
233 try {
234 auto g = rpl.run([this]() -> std::pair<const char*, size_t> {
235 size_t size;
236 static char buffer[4096];
237 if(!this->s)
238 return std::make_pair(reinterpret_cast<const char*>(NULL), 0);
239 this->s.read(buffer, sizeof(buffer));
240 size = this->s.gcount();
241 if(!size) {
242 return std::make_pair(reinterpret_cast<const char*>(NULL), 0);
244 return std::make_pair(buffer, size);
246 *size = g.second;
247 return g.first;
248 } catch(std::exception& e) {
249 err = e.what();
250 *size = 0;
251 return NULL;
255 void load_chunk(lua::state& L, lua::parameters& P)
257 auto file1 = P.arg<std::string>();
258 auto file2 = P.arg_opt<std::string>("");
259 std::string absfilename = zip::resolverel(file1, file2);
260 std::istream& file = zip::openrel(file1, file2);
261 std::string chunkname;
262 if(file2 != "")
263 chunkname = file2 + "[" + file1 + "]";
264 else
265 chunkname = file1;
266 reader rc(file, absfilename);
267 int r = lua_load(L.handle(), reader::rfn, &rc, chunkname.c_str()
268 #if LUA_VERSION_NUM == 502
269 , "t"
270 #endif
272 delete &file;
273 if(rc.get_err() != "")
274 throw std::runtime_error(rc.get_err());
275 if(r == 0) {
276 return;
277 } else if(r == LUA_ERRSYNTAX) {
278 (stringfmt() << "Syntax error: " << L.tostring(-1)).throwex();
279 } else if(r == LUA_ERRMEM) {
280 (stringfmt() << "Out of memory: " << L.tostring(-1)).throwex();
281 } else {
282 (stringfmt() << "Unknown error: " << L.tostring(-1)).throwex();
286 lua_file_reader::lua_file_reader(lua::state& L, std::istream* strm)
287 : s(*strm)
291 int loadfile2(lua::state& L, lua::parameters& P)
293 load_chunk(L, P);
294 return 1;
297 int dofile2(lua::state& L, lua::parameters& P)
299 load_chunk(L, P);
300 int old_sp = lua_gettop(L.handle());
301 lua_call(L.handle(), 0, LUA_MULTRET);
302 int new_sp = lua_gettop(L.handle());
303 return new_sp - (old_sp - 1);
306 int resolve_filename(lua::state& L, lua::parameters& P)
308 auto file1 = P.arg<std::string>();
309 auto file2 = P.arg_opt<std::string>("");
310 std::string absfilename = zip::resolverel(file1, file2);
311 L.pushlstring(absfilename);
312 return 1;
315 lua::functions load_fns(lua_func_load, "", {
316 {"loadfile2", loadfile2},
317 {"dofile2", dofile2},
318 {"resolve_filename", resolve_filename},
321 lua::_class<lua_file_reader> class_filreader(lua_class_fileio, "FILEREADER", {
322 {"open", lua_file_reader::create},
323 }, {
324 {"__call", &lua_file_reader::read},
325 {"lines", &lua_file_reader::lines}