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