lsnes rr2-β24
[lsnes.git] / src / lua / loadfile.cpp
blob597f81e11d022f71d7d1eae25dde08b74e6220ca
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 CONST_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 const 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] == CONST_pattern[matched]) {
79 matched++;
80 upper_ptr++;
81 if(matched == CONST_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++] = CONST_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(size_t* size);
138 static const char* rfn(lua_State* unused, void* data, size_t* size)
140 return reinterpret_cast<reader*>(data)->rfn(size);
142 const std::string& get_err() { return err; }
143 private:
144 std::istream& s;
145 replace rpl;
146 std::string err;
147 char buffer[4096];
150 class lua_file_reader
152 public:
153 lua_file_reader(lua::state& L, std::istream* strm);
154 static size_t overcommit(std::istream* strm) { return 0; }
155 ~lua_file_reader()
157 delete &s;
159 static int create(lua::state& L, lua::parameters& P)
161 auto file1 = P.arg<std::string>();
162 auto file2 = P.arg_opt<std::string>("");
163 std::istream& s = zip::openrel(file1, file2);
164 try {
165 lua::_class<lua_file_reader>::create(L, &s);
166 return 1;
167 } catch(...) {
168 delete &s;
169 throw;
172 int read(lua::state& L, lua::parameters& P)
174 P(P.skipped());
176 if(P.is_number()) {
177 //Read specified number of bytes.
178 size_t sz;
179 P(sz);
180 std::vector<char> buf;
181 buf.resize(sz);
182 s.read(&buf[0], sz);
183 if(!s && !s.gcount()) {
184 L.pushnil();
185 return 1;
187 L.pushlstring(&buf[0], s.gcount());
188 return 1;
189 } else if(P.is_novalue()) {
190 //Read next line.
191 std::string tmp;
192 std::getline(s, tmp);
193 if(!s) {
194 L.pushnil();
195 return 1;
197 istrip_CR(tmp);
198 L.pushlstring(tmp);
199 return 1;
200 } else
201 P.expected("number or nil");
202 return 0; //NOTREACHED
204 int lines(lua::state& L, lua::parameters& P)
206 L.pushlightuserdata(this);
207 L.push_trampoline(lua_file_reader::lines_helper2, 1);
208 //Trick: The first parameter is the userdata for this object, so by making it state, we
209 //can pin this object.
210 L.pushvalue(1);
211 L.pushboolean(true);
212 return 3;
214 int lines_helper(lua::state& L)
216 std::string tmp;
217 std::getline(s, tmp);
218 if(!s) {
219 L.pushnil();
220 return 1;
222 istrip_CR(tmp);
223 L.pushlstring(tmp.c_str(), tmp.length());
224 return 1;
226 static int lines_helper2(lua::state& L)
228 return reinterpret_cast<lua_file_reader*>(L.touserdata(L.trampoline_upval(1)))->
229 lines_helper(L);
231 private:
232 std::istream& s;
235 const char* reader::rfn(size_t* size)
237 try {
238 auto g = rpl.run([this]() -> std::pair<const char*, size_t> {
239 size_t size;
240 if(!this->s)
241 return std::make_pair(reinterpret_cast<const char*>(NULL), 0);
242 this->s.read(this->buffer, sizeof(this->buffer));
243 size = this->s.gcount();
244 if(!size) {
245 return std::make_pair(reinterpret_cast<const char*>(NULL), 0);
247 return std::make_pair(this->buffer, size);
249 *size = g.second;
250 return g.first;
251 } catch(std::exception& e) {
252 err = e.what();
253 *size = 0;
254 return NULL;
258 void load_chunk(lua::state& L, lua::parameters& P)
260 auto file1 = P.arg<std::string>();
261 auto file2 = P.arg_opt<std::string>("");
262 std::string absfilename = zip::resolverel(file1, file2);
263 std::istream& file = zip::openrel(file1, file2);
264 std::string chunkname;
265 if(file2 != "")
266 chunkname = file2 + "[" + file1 + "]";
267 else
268 chunkname = file1;
269 reader rc(file, absfilename);
270 int r = lua_load(L.handle(), reader::rfn, &rc, chunkname.c_str() LUA_LOADMODE_ARG("t") );
271 delete &file;
272 if(rc.get_err() != "")
273 throw std::runtime_error(rc.get_err());
274 if(r == 0) {
275 return;
276 } else if(r == LUA_ERRSYNTAX) {
277 (stringfmt() << "Syntax error: " << L.tostring(-1)).throwex();
278 } else if(r == LUA_ERRMEM) {
279 (stringfmt() << "Out of memory: " << L.tostring(-1)).throwex();
280 } else {
281 (stringfmt() << "Unknown error: " << L.tostring(-1)).throwex();
285 lua_file_reader::lua_file_reader(lua::state& L, std::istream* strm)
286 : s(*strm)
290 int loadfile2(lua::state& L, lua::parameters& P)
292 load_chunk(L, P);
293 return 1;
296 int dofile2(lua::state& L, lua::parameters& P)
298 load_chunk(L, P);
299 int old_sp = lua_gettop(L.handle());
300 lua_call(L.handle(), 0, LUA_MULTRET);
301 int new_sp = lua_gettop(L.handle());
302 return new_sp - (old_sp - 1);
305 int resolve_filename(lua::state& L, lua::parameters& P)
307 auto file1 = P.arg<std::string>();
308 auto file2 = P.arg_opt<std::string>("");
309 std::string absfilename = zip::resolverel(file1, file2);
310 L.pushlstring(absfilename);
311 return 1;
314 lua::functions LUA_load_fns(lua_func_load, "", {
315 {"loadfile2", loadfile2},
316 {"dofile2", dofile2},
317 {"resolve_filename", resolve_filename},
320 lua::_class<lua_file_reader> LUA_class_filreader(lua_class_fileio, "FILEREADER", {
321 {"open", lua_file_reader::create},
322 }, {
323 {"__call", &lua_file_reader::read},
324 {"lines", &lua_file_reader::lines}