1 /**
2 * Copyright (c) 2006-2016 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21 #include "wrap_File.h"
22
23 #include "common/Data.h"
24 #include "common/Exception.h"
25 #include "common/int.h"
26
27 namespace love
28 {
29 namespace filesystem
30 {
31
luax_ioError(lua_State * L,const char * fmt,...)32 int luax_ioError(lua_State *L, const char *fmt, ...)
33 {
34 va_list args;
35 va_start(args, fmt);
36
37 lua_pushnil(L);
38 lua_pushvfstring(L, fmt, args);
39
40 va_end(args);
41 return 2;
42 }
43
luax_checkfile(lua_State * L,int idx)44 File *luax_checkfile(lua_State *L, int idx)
45 {
46 return luax_checktype<File>(L, idx, FILESYSTEM_FILE_ID);
47 }
48
w_File_getSize(lua_State * L)49 int w_File_getSize(lua_State *L)
50 {
51 File *t = luax_checkfile(L, 1);
52
53 int64 size = -1;
54 try
55 {
56 size = t->getSize();
57 }
58 catch (love::Exception &e)
59 {
60 return luax_ioError(L, "%s", e.what());
61 }
62
63 // Push nil on failure or if size does not fit into a double precision floating-point number.
64 if (size == -1)
65 return luax_ioError(L, "Could not determine file size.");
66 else if (size >= 0x20000000000000LL)
67 return luax_ioError(L, "Size is too large.");
68
69 lua_pushnumber(L, (lua_Number) size);
70 return 1;
71 }
72
w_File_open(lua_State * L)73 int w_File_open(lua_State *L)
74 {
75 File *file = luax_checkfile(L, 1);
76 const char *str = luaL_checkstring(L, 2);
77 File::Mode mode;
78
79 if (!File::getConstant(str, mode))
80 return luaL_error(L, "Incorrect file open mode: %s", str);
81
82 try
83 {
84 luax_pushboolean(L, file->open(mode));
85 }
86 catch (love::Exception &e)
87 {
88 return luax_ioError(L, "%s", e.what());
89 }
90
91 return 1;
92 }
93
w_File_close(lua_State * L)94 int w_File_close(lua_State *L)
95 {
96 File *file = luax_checkfile(L, 1);
97 luax_pushboolean(L, file->close());
98 return 1;
99 }
100
w_File_isOpen(lua_State * L)101 int w_File_isOpen(lua_State *L)
102 {
103 File *file = luax_checkfile(L, 1);
104 luax_pushboolean(L, file->isOpen());
105 return 1;
106 }
107
w_File_read(lua_State * L)108 int w_File_read(lua_State *L)
109 {
110 File *file = luax_checkfile(L, 1);
111 StrongRef<Data> d = nullptr;
112
113 int64 size = (int64) luaL_optnumber(L, 2, (lua_Number) File::ALL);
114
115 try
116 {
117 d.set(file->read(size), Acquire::NORETAIN);
118 }
119 catch (love::Exception &e)
120 {
121 return luax_ioError(L, "%s", e.what());
122 }
123
124 lua_pushlstring(L, (const char *) d->getData(), d->getSize());
125 lua_pushnumber(L, d->getSize());
126 return 2;
127 }
128
w_File_write(lua_State * L)129 int w_File_write(lua_State *L)
130 {
131 File *file = luax_checkfile(L, 1);
132 bool result = false;
133
134 if (lua_isstring(L, 2))
135 {
136 try
137 {
138 size_t datasize = 0;
139 const char *data = lua_tolstring(L, 2, &datasize);
140
141 if (!lua_isnoneornil(L, 3))
142 datasize = luaL_checkinteger(L, 3);
143
144 result = file->write(data, datasize);
145 }
146 catch (love::Exception &e)
147 {
148 return luax_ioError(L, "%s", e.what());
149 }
150 }
151 else if (luax_istype(L, 2, DATA_ID))
152 {
153 try
154 {
155 love::Data *data = luax_totype<love::Data>(L, 2, DATA_ID);
156 result = file->write(data, luaL_optinteger(L, 3, data->getSize()));
157 }
158 catch (love::Exception &e)
159 {
160 return luax_ioError(L, "%s", e.what());
161 }
162 }
163 else
164 {
165 return luaL_argerror(L, 2, "string or data expected");
166 }
167
168 luax_pushboolean(L, result);
169 return 1;
170 }
171
w_File_flush(lua_State * L)172 int w_File_flush(lua_State *L)
173 {
174 File *file = luax_checkfile(L, 1);
175 bool success = false;
176 try
177 {
178 success = file->flush();
179 }
180 catch (love::Exception &e)
181 {
182 return luax_ioError(L, "%s", e.what());
183 }
184 luax_pushboolean(L, success);
185 return 1;
186 }
187
w_File_isEOF(lua_State * L)188 int w_File_isEOF(lua_State *L)
189 {
190 File *file = luax_checkfile(L, 1);
191 luax_pushboolean(L, file->isEOF());
192 return 1;
193 }
194
w_File_tell(lua_State * L)195 int w_File_tell(lua_State *L)
196 {
197 File *file = luax_checkfile(L, 1);
198 int64 pos = file->tell();
199 // Push nil on failure or if pos does not fit into a double precision floating-point number.
200 if (pos == -1)
201 return luax_ioError(L, "Invalid position.");
202 else if (pos >= 0x20000000000000LL)
203 return luax_ioError(L, "Number is too large.");
204 else
205 lua_pushnumber(L, (lua_Number)pos);
206 return 1;
207 }
208
w_File_seek(lua_State * L)209 int w_File_seek(lua_State *L)
210 {
211 File *file = luax_checkfile(L, 1);
212 lua_Number pos = luaL_checknumber(L, 2);
213
214 // Push false on negative and precision-problematic numbers.
215 // Better fail than seek to an unknown position.
216 if (pos < 0.0 || pos >= 9007199254740992.0)
217 luax_pushboolean(L, false);
218 else
219 luax_pushboolean(L, file->seek((uint64)pos));
220 return 1;
221 }
222
w_File_lines_i(lua_State * L)223 int w_File_lines_i(lua_State *L)
224 {
225 const int bufsize = 1024;
226 char buf[bufsize];
227 int linesize = 0;
228 bool newline = false;
229
230 File *file = luax_checktype<File>(L, lua_upvalueindex(1), FILESYSTEM_FILE_ID);
231
232 // Only accept read mode at this point.
233 if (file->getMode() != File::MODE_READ)
234 return luaL_error(L, "File needs to stay in read mode.");
235
236 int64 pos = file->tell();
237 int64 userpos = -1;
238
239 if (lua_isnoneornil(L, lua_upvalueindex(2)) == 0)
240 {
241 // User may have changed the file position.
242 userpos = pos;
243 pos = (int64) lua_tonumber(L, lua_upvalueindex(2));
244 if (userpos != pos)
245 file->seek(pos);
246 }
247
248 while (!newline && !file->isEOF())
249 {
250 // This 64-bit to 32-bit integer cast should be safe as it never exceeds bufsize.
251 int read = (int) file->read(buf, bufsize);
252 if (read < 0)
253 return luaL_error(L, "Could not read from file.");
254
255 linesize += read;
256
257 for (int i = 0; i < read; i++)
258 {
259 if (buf[i] == '\n')
260 {
261 linesize -= read - i;
262 newline = true;
263 break;
264 }
265 }
266 }
267
268 if (newline || (file->isEOF() && linesize > 0))
269 {
270 if (linesize < bufsize)
271 {
272 // We have the line in the buffer on the stack. No 'new' and 'read' needed.
273 lua_pushlstring(L, buf, linesize > 0 && buf[linesize - 1] == '\r' ? linesize - 1 : linesize);
274 if (userpos < 0)
275 file->seek(pos + linesize + 1);
276 }
277 else
278 {
279 char *str = 0;
280 try
281 {
282 str = new char[linesize + 1];
283 }
284 catch(std::bad_alloc &)
285 {
286 // Can't lua_error (longjmp) in exception handlers.
287 }
288
289 if (!str)
290 return luaL_error(L, "Out of memory.");
291
292 file->seek(pos);
293
294 // Read the \n anyway and save us a call to seek.
295 if (file->read(str, linesize + 1) == -1)
296 {
297 delete [] str;
298 return luaL_error(L, "Could not read from file.");
299 }
300
301 lua_pushlstring(L, str, str[linesize - 1] == '\r' ? linesize - 1 : linesize);
302 delete [] str;
303 }
304
305 if (userpos >= 0)
306 {
307 // Save new position in upvalue.
308 lua_pushnumber(L, (lua_Number)(pos + linesize + 1));
309 lua_replace(L, lua_upvalueindex(2));
310 file->seek(userpos);
311 }
312
313 return 1;
314 }
315
316 // EOF reached.
317 if (userpos >= 0 && luax_toboolean(L, lua_upvalueindex(3)))
318 file->seek(userpos);
319 else
320 file->close();
321
322 return 0;
323 }
324
w_File_lines(lua_State * L)325 int w_File_lines(lua_State *L)
326 {
327 File *file = luax_checkfile(L, 1);
328
329 lua_pushnumber(L, 0); // File position.
330 luax_pushboolean(L, file->getMode() != File::MODE_CLOSED); // Save current file mode.
331
332 if (file->getMode() != File::MODE_READ)
333 {
334 if (file->getMode() != File::MODE_CLOSED)
335 file->close();
336
337 bool success = false;
338 luax_catchexcept(L, [&](){ success = file->open(File::MODE_READ); });
339
340 if (!success)
341 return luaL_error(L, "Could not open file.");
342 }
343
344 lua_pushcclosure(L, w_File_lines_i, 3);
345 return 1;
346 }
347
w_File_setBuffer(lua_State * L)348 int w_File_setBuffer(lua_State *L)
349 {
350 File *file = luax_checkfile(L, 1);
351 const char *str = luaL_checkstring(L, 2);
352 int64 size = (int64) luaL_optnumber(L, 3, 0.0);
353
354 File::BufferMode bufmode;
355 if (!File::getConstant(str, bufmode))
356 return luaL_error(L, "Incorrect file buffer mode: %s", str);
357
358 bool success = false;
359 try
360 {
361 success = file->setBuffer(bufmode, size);
362 }
363 catch (love::Exception &e)
364 {
365 return luax_ioError(L, "%s", e.what());
366 }
367
368 luax_pushboolean(L, success);
369 return 1;
370 }
371
w_File_getBuffer(lua_State * L)372 int w_File_getBuffer(lua_State *L)
373 {
374 File *file = luax_checkfile(L, 1);
375 int64 size = 0;
376 File::BufferMode bufmode = file->getBuffer(size);
377 const char *str = 0;
378
379 if (!File::getConstant(bufmode, str))
380 return luax_ioError(L, "Unknown file buffer mode.");
381
382 lua_pushstring(L, str);
383 lua_pushnumber(L, (lua_Number) size);
384 return 2;
385 }
386
w_File_getMode(lua_State * L)387 int w_File_getMode(lua_State *L)
388 {
389 File *file = luax_checkfile(L, 1);
390
391 File::Mode mode = file->getMode();
392 const char *str = 0;
393
394 if (!File::getConstant(mode, str))
395 return luax_ioError(L, "Unknown file mode.");
396
397 lua_pushstring(L, str);
398 return 1;
399 }
400
w_File_getFilename(lua_State * L)401 int w_File_getFilename(lua_State *L)
402 {
403 File *file = luax_checkfile(L, 1);
404 luax_pushstring(L, file->getFilename());
405 return 1;
406 }
407
w_File_getExtension(lua_State * L)408 int w_File_getExtension(lua_State *L)
409 {
410 File *file = luax_checkfile(L, 1);
411 luax_pushstring(L, file->getExtension());
412 return 1;
413 }
414
415 const luaL_Reg w_File_functions[] =
416 {
417 { "getSize", w_File_getSize },
418 { "open", w_File_open },
419 { "close", w_File_close },
420 { "isOpen", w_File_isOpen },
421 { "read", w_File_read },
422 { "write", w_File_write },
423 { "flush", w_File_flush },
424 { "isEOF", w_File_isEOF },
425 { "tell", w_File_tell },
426 { "seek", w_File_seek },
427 { "lines", w_File_lines },
428 { "setBuffer", w_File_setBuffer },
429 { "getBuffer", w_File_getBuffer },
430 { "getMode", w_File_getMode },
431 { "getFilename", w_File_getFilename },
432 { "getExtension", w_File_getExtension },
433 { 0, 0 }
434 };
435
luaopen_file(lua_State * L)436 extern "C" int luaopen_file(lua_State *L)
437 {
438 return luax_register_type(L, FILESYSTEM_FILE_ID, "File", w_File_functions, nullptr);
439 }
440
441 } // filesystem
442 } // love
443