1 /*
2    Copyright (C) 2014 - 2018 by Chris Beck <render787@gmail.com>
3    Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY.
11 
12    See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Contains code common to the application and game lua kernels which
18  * cannot or should not go into the lua kernel base files.
19  *
20  * Currently contains implementation functions related to vconfig and
21  * gettext, also some macros to assist in writing C lua callbacks.
22  */
23 
24 #include "scripting/lua_common.hpp"
25 
26 #include "config.hpp"
27 #include "scripting/lua_unit.hpp"
28 #include "tstring.hpp"                  // for t_string
29 #include "variable.hpp" // for vconfig
30 #include "log.hpp"
31 #include "gettext.hpp"
32 #include "lua_jailbreak_exception.hpp"
33 #include "game_display.hpp"
34 
35 #include <cstring>
36 #include <iterator>                     // for distance, advance
37 #include <new>                          // for operator new
38 #include <string>                       // for string, basic_string
39 
40 #include "lua/lauxlib.h"
41 #include "lua/lua.h"
42 
43 static const char gettextKey[] = "gettext";
44 static const char vconfigKey[] = "vconfig";
45 static const char vconfigpairsKey[] = "vconfig pairs";
46 static const char vconfigipairsKey[] = "vconfig ipairs";
47 static const char tstringKey[] = "translatable string";
48 static const char executeKey[] = "err";
49 
50 static lg::log_domain log_scripting_lua("scripting/lua");
51 #define LOG_LUA LOG_STREAM(info, log_scripting_lua)
52 #define WRN_LUA LOG_STREAM(warn, log_scripting_lua)
53 #define ERR_LUA LOG_STREAM(err, log_scripting_lua)
54 
55 namespace lua_common {
56 
57 /**
58  * Creates a t_string object (__call metamethod).
59  * - Arg 1: userdata containing the domain.
60  * - Arg 2: string to translate.
61  * - Ret 1: string containing the translatable string.
62  */
impl_gettext(lua_State * L)63 static int impl_gettext(lua_State *L)
64 {
65 	char const *m = luaL_checkstring(L, 2);
66 	char const *d = static_cast<char *>(lua_touserdata(L, 1));
67 	// Hidden metamethod, so d has to be a string. Use it to create a t_string.
68 	if(lua_isstring(L, 3)) {
69 		const char* pl = luaL_checkstring(L, 3);
70 		int count = luaL_checkinteger(L, 4);
71 		luaW_pushtstring(L, t_string(m, pl, count, d));
72 	} else {
73 		luaW_pushtstring(L, t_string(m, d));
74 	}
75 	return 1;
76 }
77 
78 /**
79  * Creates an interface for gettext
80  * - Arg 1: string containing the domain.
81  * - Ret 1: a full userdata with __call pointing to lua_gettext.
82  */
intf_textdomain(lua_State * L)83 int intf_textdomain(lua_State *L)
84 {
85 	size_t l;
86 	char const *m = luaL_checklstring(L, 1, &l);
87 
88 	void *p = lua_newuserdata(L, l + 1);
89 	memcpy(p, m, l + 1);
90 
91 	luaL_setmetatable(L, gettextKey);
92 	return 1;
93 }
94 
95 /**
96  * Converts a Lua value at position @a src and appends it to @a dst.
97  * @note This function is private to lua_tstring_concat. It expects two things.
98  *       First, the t_string metatable is at the top of the stack on entry. (It
99  *       is still there on exit.) Second, the caller hasn't any valuable object
100  *       with dynamic lifetime, since they would be leaked on error.
101  */
tstring_concat_aux(lua_State * L,t_string & dst,int src)102 static void tstring_concat_aux(lua_State *L, t_string &dst, int src)
103 {
104 	switch (lua_type(L, src)) {
105 		case LUA_TNUMBER:
106 		case LUA_TSTRING:
107 			dst += lua_tostring(L, src);
108 			return;
109 		case LUA_TUSERDATA:
110 			// Compare its metatable with t_string's metatable.
111 			if (t_string * src_ptr = static_cast<t_string *> (luaL_testudata(L, src, tstringKey))) {
112 				dst += *src_ptr;
113 				return;
114 			}
115 			//intentional fall-through
116 		default:
117 			luaW_type_error(L, src, "string");
118 	}
119 }
120 
121 /**
122  * Appends a scalar to a t_string object (__concat metamethod).
123  */
impl_tstring_concat(lua_State * L)124 static int impl_tstring_concat(lua_State *L)
125 {
126 	// Create a new t_string.
127 	t_string *t = new(L) t_string;
128 	luaL_setmetatable(L, tstringKey);
129 
130 	// Append both arguments to t.
131 	tstring_concat_aux(L, *t, 1);
132 	tstring_concat_aux(L, *t, 2);
133 
134 	return 1;
135 }
136 
impl_tstring_len(lua_State * L)137 static int impl_tstring_len(lua_State* L)
138 {
139 	t_string* t = static_cast<t_string*>(lua_touserdata(L, 1));
140 	lua_pushnumber(L, t->size());
141 	return 1;
142 }
143 
144 /**
145  * Destroys a t_string object before it is collected (__gc metamethod).
146  */
impl_tstring_collect(lua_State * L)147 static int impl_tstring_collect(lua_State *L)
148 {
149 	t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
150 	t->t_string::~t_string();
151 	return 0;
152 }
153 
impl_tstring_lt(lua_State * L)154 static int impl_tstring_lt(lua_State *L)
155 {
156 	t_string *t1 = static_cast<t_string *>(luaL_checkudata(L, 1, tstringKey));
157 	t_string *t2 = static_cast<t_string *>(luaL_checkudata(L, 2, tstringKey));
158 	lua_pushboolean(L, translation::compare(t1->get(), t2->get()) < 0);
159 	return 1;
160 }
161 
impl_tstring_le(lua_State * L)162 static int impl_tstring_le(lua_State *L)
163 {
164 	t_string *t1 = static_cast<t_string *>(luaL_checkudata(L, 1, tstringKey));
165 	t_string *t2 = static_cast<t_string *>(luaL_checkudata(L, 2, tstringKey));
166 	lua_pushboolean(L, translation::compare(t1->get(), t2->get()) < 1);
167 	return 1;
168 }
169 
impl_tstring_eq(lua_State * L)170 static int impl_tstring_eq(lua_State *L)
171 {
172 	t_string *t1 = static_cast<t_string *>(lua_touserdata(L, 1));
173 	t_string *t2 = static_cast<t_string *>(lua_touserdata(L, 2));
174 	lua_pushboolean(L, translation::compare(t1->get(), t2->get()) == 0);
175 	return 1;
176 }
177 
178 /**
179  * Converts a t_string object to a string (__tostring metamethod);
180  * that is, performs a translation.
181  */
impl_tstring_tostring(lua_State * L)182 static int impl_tstring_tostring(lua_State *L)
183 {
184 	t_string *t = static_cast<t_string *>(lua_touserdata(L, 1));
185 	lua_pushstring(L, t->c_str());
186 	return 1;
187 }
188 
189 /**
190  * Gets the parsed field of a vconfig object (_index metamethod).
191  * Special fields __literal, __shallow_literal, __parsed, and
192  * __shallow_parsed, return Lua tables.
193  */
impl_vconfig_get(lua_State * L)194 static int impl_vconfig_get(lua_State *L)
195 {
196 	vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
197 
198 	if (lua_isnumber(L, 2))
199 	{
200 		vconfig::all_children_iterator i = v->ordered_begin();
201 		unsigned len = std::distance(i, v->ordered_end());
202 		unsigned pos = lua_tointeger(L, 2) - 1;
203 		if (pos >= len) return 0;
204 		std::advance(i, pos);
205 
206 		lua_createtable(L, 2, 0);
207 		lua_pushstring(L, i.get_key().c_str());
208 		lua_rawseti(L, -2, 1);
209 		luaW_pushvconfig(L, i.get_child());
210 		lua_rawseti(L, -2, 2);
211 		return 1;
212 	}
213 
214 	char const *m = luaL_checkstring(L, 2);
215 	if (strcmp(m, "__literal") == 0) {
216 		luaW_pushconfig(L, v->get_config());
217 		return 1;
218 	}
219 	if (strcmp(m, "__parsed") == 0) {
220 		luaW_pushconfig(L, v->get_parsed_config());
221 		return 1;
222 	}
223 
224 	bool shallow_literal = strcmp(m, "__shallow_literal") == 0;
225 	if (shallow_literal || strcmp(m, "__shallow_parsed") == 0)
226 	{
227 		lua_newtable(L);
228 		for (const config::attribute &a : v->get_config().attribute_range()) {
229 			if (shallow_literal)
230 				luaW_pushscalar(L, a.second);
231 			else
232 				luaW_pushscalar(L, v->expand(a.first));
233 			lua_setfield(L, -2, a.first.c_str());
234 		}
235 		vconfig::all_children_iterator i = v->ordered_begin(),
236 			i_end = v->ordered_end();
237 		if (shallow_literal) {
238 			i.disable_insertion();
239 			i_end.disable_insertion();
240 		}
241 		for (int j = 1; i != i_end; ++i, ++j)
242 		{
243 			lua_createtable(L, 2, 0);
244 			lua_pushstring(L, i.get_key().c_str());
245 			lua_rawseti(L, -2, 1);
246 			luaW_pushvconfig(L, i.get_child());
247 			lua_rawseti(L, -2, 2);
248 			lua_rawseti(L, -2, j);
249 		}
250 		return 1;
251 	}
252 
253 	if (v->null() || !v->has_attribute(m)) return 0;
254 	luaW_pushscalar(L, (*v)[m]);
255 	return 1;
256 }
257 
258 /**
259  * Returns the number of a child of a vconfig object.
260  */
impl_vconfig_size(lua_State * L)261 static int impl_vconfig_size(lua_State *L)
262 {
263 	vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
264 	lua_pushinteger(L, v->null() ? 0 :
265 		std::distance(v->ordered_begin(), v->ordered_end()));
266 	return 1;
267 }
268 
269 /**
270  * Destroys a vconfig object before it is collected (__gc metamethod).
271  */
impl_vconfig_collect(lua_State * L)272 static int impl_vconfig_collect(lua_State *L)
273 {
274 	vconfig *v = static_cast<vconfig *>(lua_touserdata(L, 1));
275 	v->~vconfig();
276 	return 0;
277 }
278 
279 /**
280  * Iterate through the attributes of a vconfig
281  */
impl_vconfig_pairs_iter(lua_State * L)282 static int impl_vconfig_pairs_iter(lua_State *L)
283 {
284 	vconfig vcfg = luaW_checkvconfig(L, 1);
285 	void* p = luaL_checkudata(L, lua_upvalueindex(1), vconfigpairsKey);
286 	config::const_attr_itors& range = *static_cast<config::const_attr_itors*>(p);
287 	if (range.empty()) {
288 		return 0;
289 	}
290 	config::attribute value = range.front();
291 	range.pop_front();
292 	lua_pushlstring(L, value.first.c_str(), value.first.length());
293 	luaW_pushscalar(L, vcfg[value.first]);
294 	return 2;
295 }
296 
297 /**
298  * Destroy a vconfig pairs iterator
299  */
impl_vconfig_pairs_collect(lua_State * L)300 static int impl_vconfig_pairs_collect(lua_State *L)
301 {
302 	typedef config::const_attr_itors const_attr_itors;
303 	void* p = lua_touserdata(L, 1);
304 	const_attr_itors* cai = static_cast<const_attr_itors*>(p);
305 	cai->~const_attr_itors();
306 	return 0;
307 }
308 
309 /**
310  * Construct an iterator to iterate through the attributes of a vconfig
311  */
impl_vconfig_pairs(lua_State * L)312 static int impl_vconfig_pairs(lua_State *L)
313 {
314 	vconfig vcfg = luaW_checkvconfig(L, 1);
315 	new(L) config::const_attr_itors(vcfg.get_config().attribute_range());
316 	luaL_newmetatable(L, vconfigpairsKey);
317 	lua_setmetatable(L, -2);
318 	lua_pushcclosure(L, &impl_vconfig_pairs_iter, 1);
319 	lua_pushvalue(L, 1);
320 	return 2;
321 }
322 
323 typedef std::pair<vconfig::all_children_iterator, vconfig::all_children_iterator> vconfig_child_range;
324 
325 /**
326  * Iterate through the subtags of a vconfig
327  */
impl_vconfig_ipairs_iter(lua_State * L)328 static int impl_vconfig_ipairs_iter(lua_State *L)
329 {
330 	luaW_checkvconfig(L, 1);
331 	int i = luaL_checkinteger(L, 2);
332 	void* p = luaL_checkudata(L, lua_upvalueindex(1), vconfigipairsKey);
333 	vconfig_child_range& range = *static_cast<vconfig_child_range*>(p);
334 	if (range.first == range.second) {
335 		return 0;
336 	}
337 	std::pair<std::string, vconfig> value = *range.first++;
338 	lua_pushinteger(L, i + 1);
339 	lua_createtable(L, 2, 0);
340 	lua_pushlstring(L, value.first.c_str(), value.first.length());
341 	lua_rawseti(L, -2, 1);
342 	luaW_pushvconfig(L, value.second);
343 	lua_rawseti(L, -2, 2);
344 	return 2;
345 }
346 
347 /**
348  * Destroy a vconfig ipairs iterator
349  */
impl_vconfig_ipairs_collect(lua_State * L)350 static int impl_vconfig_ipairs_collect(lua_State *L)
351 {
352 	void* p = lua_touserdata(L, 1);
353 	vconfig_child_range* vcr = static_cast<vconfig_child_range*>(p);
354 	vcr->~vconfig_child_range();
355 	return 0;
356 }
357 
358 /**
359  * Construct an iterator to iterate through the subtags of a vconfig
360  */
impl_vconfig_ipairs(lua_State * L)361 static int impl_vconfig_ipairs(lua_State *L)
362 {
363 	vconfig cfg = luaW_checkvconfig(L, 1);
364 	new(L) vconfig_child_range(cfg.ordered_begin(), cfg.ordered_end());
365 	luaL_newmetatable(L, vconfigipairsKey);
366 	lua_setmetatable(L, -2);
367 	lua_pushcclosure(L, &impl_vconfig_ipairs_iter, 1);
368 	lua_pushvalue(L, 1);
369 	lua_pushinteger(L, 0);
370 	return 3;
371 }
372 
373 /**
374  * Creates a vconfig containing the WML table.
375  * - Arg 1: WML table.
376  * - Ret 1: vconfig userdata.
377  */
intf_tovconfig(lua_State * L)378 int intf_tovconfig(lua_State *L)
379 {
380 	vconfig vcfg = luaW_checkvconfig(L, 1);
381 	luaW_pushvconfig(L, vcfg);
382 	return 1;
383 }
384 
385 /**
386  * Adds the gettext metatable
387  */
register_gettext_metatable(lua_State * L)388 std::string register_gettext_metatable(lua_State *L)
389 {
390 	luaL_newmetatable(L, gettextKey);
391 
392 	static luaL_Reg const callbacks[] {
393 		{ "__call", 	    &impl_gettext},
394 		{ nullptr, nullptr }
395 	};
396 	luaL_setfuncs(L, callbacks, 0);
397 
398 	lua_pushstring(L, "message domain");
399 	lua_setfield(L, -2, "__metatable");
400 
401 	return "Adding gettext metatable...\n";
402 }
403 
404 /**
405  * Adds the tstring metatable
406  */
register_tstring_metatable(lua_State * L)407 std::string register_tstring_metatable(lua_State *L)
408 {
409 	luaL_newmetatable(L, tstringKey);
410 
411 	static luaL_Reg const callbacks[] {
412 		{ "__concat", 	    &impl_tstring_concat},
413 		{ "__gc",           &impl_tstring_collect},
414 		{ "__tostring",	    &impl_tstring_tostring},
415 		{ "__len",          &impl_tstring_len},
416 		{ "__lt",	        &impl_tstring_lt},
417 		{ "__le",	        &impl_tstring_le},
418 		{ "__eq",	        &impl_tstring_eq},
419 		{ nullptr, nullptr }
420 	};
421 	luaL_setfuncs(L, callbacks, 0);
422 
423 	lua_pushstring(L, "translatable string");
424 	lua_setfield(L, -2, "__metatable");
425 
426 	return "Adding tstring metatable...\n";
427 }
428 
429 /**
430  * Adds the vconfig metatable
431  */
register_vconfig_metatable(lua_State * L)432 std::string register_vconfig_metatable(lua_State *L)
433 {
434 	luaL_newmetatable(L, vconfigKey);
435 
436 	static luaL_Reg const callbacks[] {
437 		{ "__gc",           &impl_vconfig_collect},
438 		{ "__index",        &impl_vconfig_get},
439 		{ "__len",          &impl_vconfig_size},
440 		{ "__pairs",        &impl_vconfig_pairs},
441 		{ "__ipairs",       &impl_vconfig_ipairs},
442 		{ nullptr, nullptr }
443 	};
444 	luaL_setfuncs(L, callbacks, 0);
445 
446 	lua_pushstring(L, "wml object");
447 	lua_setfield(L, -2, "__metatable");
448 
449 	// Metatables for the iterator userdata
450 
451 	// I don't bother setting __metatable because this
452 	// userdata is only ever stored in the iterator's
453 	// upvalues, so it's never visible to the user.
454 	luaL_newmetatable(L, vconfigpairsKey);
455 	lua_pushstring(L, "__gc");
456 	lua_pushcfunction(L, &impl_vconfig_pairs_collect);
457 	lua_rawset(L, -3);
458 
459 	luaL_newmetatable(L, vconfigipairsKey);
460 	lua_pushstring(L, "__gc");
461 	lua_pushcfunction(L, &impl_vconfig_ipairs_collect);
462 	lua_rawset(L, -3);
463 
464 	return "Adding vconfig metatable...\n";
465 }
466 
467 } // end namespace lua_common
468 
operator new(size_t sz,lua_State * L)469 void* operator new(size_t sz, lua_State *L)
470 {
471 	return lua_newuserdata(L, sz);
472 }
473 
operator delete(void *,lua_State * L)474 void operator delete(void*, lua_State *L)
475 {
476 	// Not sure if this is needed since it's a no-op
477 	// It's only called if a constructor throws while using the above operator new
478 	// By removing the userdata from the stack, this should ensure that Lua frees it
479 	lua_pop(L, 1);
480 }
481 
luaW_getmetafield(lua_State * L,int idx,const char * key)482 bool luaW_getmetafield(lua_State *L, int idx, const char* key)
483 {
484 	if(key == nullptr) {
485 		return false;
486 	}
487 	int n = strlen(key);
488 	if(n == 0) {
489 		return false;
490 	}
491 	if(n >= 2 && key[0] == '_' && key[1] == '_') {
492 		return false;
493 	}
494 	return luaL_getmetafield(L, idx, key) != 0;
495 }
496 
luaW_pushvconfig(lua_State * L,const vconfig & cfg)497 void luaW_pushvconfig(lua_State *L, const vconfig& cfg)
498 {
499 	new(L) vconfig(cfg);
500 	luaL_setmetatable(L, vconfigKey);
501 }
502 
luaW_pushtstring(lua_State * L,const t_string & v)503 void luaW_pushtstring(lua_State *L, const t_string& v)
504 {
505 	new(L) t_string(v);
506 	luaL_setmetatable(L, tstringKey);
507 }
508 
509 
510 namespace {
511 	struct luaW_pushscalar_visitor : boost::static_visitor<>
512 	{
513 		lua_State *L;
luaW_pushscalar_visitor__anon925275af0111::luaW_pushscalar_visitor514 		luaW_pushscalar_visitor(lua_State *l): L(l) {}
515 
operator ()__anon925275af0111::luaW_pushscalar_visitor516 		void operator()(const boost::blank&) const
517 		{ lua_pushnil(L); }
operator ()__anon925275af0111::luaW_pushscalar_visitor518 		void operator()(bool b) const
519 		{ lua_pushboolean(L, b); }
operator ()__anon925275af0111::luaW_pushscalar_visitor520 		void operator()(int i) const
521 		{ lua_pushinteger(L, i); }
operator ()__anon925275af0111::luaW_pushscalar_visitor522 		void operator()(unsigned long long ull) const
523 		{ lua_pushnumber(L, ull); }
operator ()__anon925275af0111::luaW_pushscalar_visitor524 		void operator()(double d) const
525 		{ lua_pushnumber(L, d); }
operator ()__anon925275af0111::luaW_pushscalar_visitor526 		void operator()(const std::string& s) const
527 		{ lua_pushstring(L, s.c_str()); }
operator ()__anon925275af0111::luaW_pushscalar_visitor528 		void operator()(const t_string& s) const
529 		{ luaW_pushtstring(L, s); }
530 	};
531 }//unnamed namespace for luaW_pushscalar_visitor
532 
luaW_pushscalar(lua_State * L,const config::attribute_value & v)533 void luaW_pushscalar(lua_State *L, const config::attribute_value& v)
534 {
535 	v.apply_visitor(luaW_pushscalar_visitor(L));
536 }
537 
luaW_toscalar(lua_State * L,int index,config::attribute_value & v)538 bool luaW_toscalar(lua_State *L, int index, config::attribute_value& v)
539 {
540 	switch (lua_type(L, index)) {
541 		case LUA_TBOOLEAN:
542 			v = luaW_toboolean(L, -1);
543 			break;
544 		case LUA_TNUMBER:
545 			v = lua_tonumber(L, -1);
546 			break;
547 		case LUA_TSTRING:
548 			v = lua_tostring(L, -1);
549 			break;
550 		case LUA_TUSERDATA:
551 		{
552 			if (t_string * tptr = static_cast<t_string *>(luaL_testudata(L, -1, tstringKey))) {
553 				v = *tptr;
554 				break;
555 			} else {
556 				return false;
557 			}
558 		}
559 		default:
560 			return false;
561 	}
562 	return true;
563 }
564 
luaW_totstring(lua_State * L,int index,t_string & str)565 bool luaW_totstring(lua_State *L, int index, t_string &str)
566 {
567 	switch (lua_type(L, index)) {
568 		case LUA_TBOOLEAN:
569 			str = lua_toboolean(L, index) ? "yes" : "no";
570 			break;
571 		case LUA_TNUMBER:
572 		case LUA_TSTRING:
573 			str = lua_tostring(L, index);
574 			break;
575 		case LUA_TUSERDATA:
576 		{
577 			if (t_string * tstr = static_cast<t_string *> (luaL_testudata(L, index, tstringKey))) {
578 				str = *tstr;
579 				break;
580 			} else {
581 				return false;
582 			}
583 		}
584 		default:
585 			return false;
586 	}
587 	return true;
588 }
589 
luaW_checktstring(lua_State * L,int index)590 t_string luaW_checktstring(lua_State *L, int index)
591 {
592 	t_string result;
593 	if (!luaW_totstring(L, index, result))
594 		luaW_type_error(L, index, "translatable string");
595 	return result;
596 }
597 
luaW_iststring(lua_State * L,int index)598 bool luaW_iststring(lua_State* L, int index)
599 {
600 	if(lua_isstring(L, index)) {
601 		return true;
602 	}
603 	if(lua_isuserdata(L, index) && luaL_testudata(L, index, tstringKey)) {
604 		return true;
605 	}
606 	return false;
607 }
608 
luaW_filltable(lua_State * L,const config & cfg)609 void luaW_filltable(lua_State *L, const config& cfg)
610 {
611 	if (!lua_checkstack(L, LUA_MINSTACK))
612 		return;
613 
614 	int k = 1;
615 	for (const config::any_child &ch : cfg.all_children_range())
616 	{
617 		lua_createtable(L, 2, 0);
618 		lua_pushstring(L, ch.key.c_str());
619 		lua_rawseti(L, -2, 1);
620 		lua_newtable(L);
621 		luaW_filltable(L, ch.cfg);
622 		lua_rawseti(L, -2, 2);
623 		lua_rawseti(L, -2, k++);
624 	}
625 	for (const config::attribute &attr : cfg.attribute_range())
626 	{
627 		luaW_pushscalar(L, attr.second);
628 		lua_setfield(L, -2, attr.first.c_str());
629 	}
630 }
631 
luaW_pushlocation(lua_State * L,const map_location & ml)632 void luaW_pushlocation(lua_State *L, const map_location& ml)
633 {
634 	lua_createtable(L, 2, 0);
635 
636 	lua_pushinteger(L, ml.wml_x());
637 	lua_rawseti(L, -2, 1);
638 
639 	lua_pushinteger(L, ml.wml_y());
640 	lua_rawseti(L, -2, 2);
641 }
642 
luaW_tolocation(lua_State * L,int index,map_location & loc)643 bool luaW_tolocation(lua_State *L, int index, map_location& loc) {
644 	if (!lua_checkstack(L, LUA_MINSTACK)) {
645 		return false;
646 	}
647 	if (lua_isnoneornil(L, index)) {
648 		// Need this special check because luaW_tovconfig returns true in this case
649 		return false;
650 	}
651 
652 	vconfig dummy_vcfg = vconfig::unconstructed_vconfig();
653 
654 	index = lua_absindex(L, index);
655 
656 	if (lua_istable(L, index) || luaW_tounit(L, index) || luaW_tovconfig(L, index, dummy_vcfg)) {
657 		map_location result;
658 		int x_was_num = 0, y_was_num = 0;
659 		lua_getfield(L, index, "x");
660 		result.set_wml_x(lua_tonumberx(L, -1, &x_was_num));
661 		lua_getfield(L, index, "y");
662 		result.set_wml_y(lua_tonumberx(L, -1, &y_was_num));
663 		lua_pop(L, 2);
664 		if (!x_was_num || !y_was_num) {
665 			// If we get here and it was userdata, checking numeric indices won't help
666 			// (It won't help if it was a config either, but there's no easy way to check that.)
667 			if (lua_isuserdata(L, index)) {
668 				return false;
669 			}
670 			lua_rawgeti(L, index, 1);
671 			result.set_wml_x(lua_tonumberx(L, -1, &x_was_num));
672 			lua_rawgeti(L, index, 2);
673 			result.set_wml_y(lua_tonumberx(L, -1, &y_was_num));
674 			lua_pop(L, 2);
675 		}
676 		if (x_was_num && y_was_num) {
677 			loc = result;
678 			return true;
679 		}
680 	} else if (lua_isnumber(L, index) && lua_isnumber(L, index + 1)) {
681 		// If it's a number, then we consume two elements on the stack
682 		// Since we have no way of notifying the caller that we have
683 		// done this, we remove the first number from the stack.
684 		loc.set_wml_x(lua_tonumber(L, index));
685 		lua_remove(L, index);
686 		loc.set_wml_y(lua_tonumber(L, index));
687 		return true;
688 	}
689 	return false;
690 }
691 
luaW_checklocation(lua_State * L,int index)692 map_location luaW_checklocation(lua_State *L, int index)
693 {
694 	map_location result;
695 	if (!luaW_tolocation(L, index, result))
696 		luaW_type_error(L, index, "location");
697 	return result;
698 }
699 
luaW_pushconfig(lua_State * L,const config & cfg)700 void luaW_pushconfig(lua_State *L, const config& cfg)
701 {
702 	lua_newtable(L);
703 	luaW_filltable(L, cfg);
704 }
705 
706 
707 
708 
709 #define return_misformed() \
710   do { lua_settop(L, initial_top); return false; } while (0)
711 
luaW_toconfig(lua_State * L,int index,config & cfg)712 bool luaW_toconfig(lua_State *L, int index, config &cfg)
713 {
714 	if (!lua_checkstack(L, LUA_MINSTACK))
715 		return false;
716 
717 	// Get the absolute index of the table.
718 	index = lua_absindex(L, index);
719 	int initial_top = lua_gettop(L);
720 
721 	switch (lua_type(L, index))
722 	{
723 		case LUA_TTABLE:
724 			break;
725 		case LUA_TUSERDATA:
726 		{
727 			if (vconfig * ptr = static_cast<vconfig *> (luaL_testudata(L, index, vconfigKey))) {
728 				cfg = ptr->get_parsed_config();
729 				return true;
730 			} else {
731 				return false;
732 			}
733 		}
734 		case LUA_TNONE:
735 		case LUA_TNIL:
736 			return true;
737 		default:
738 			return false;
739 	}
740 
741 	// First convert the children (integer indices).
742 	for (int i = 1, i_end = lua_rawlen(L, index); i <= i_end; ++i)
743 	{
744 		lua_rawgeti(L, index, i);
745 		if (!lua_istable(L, -1)) return_misformed();
746 		lua_rawgeti(L, -1, 1);
747 		char const *m = lua_tostring(L, -1);
748 		if (!m || !config::valid_tag(m) || m[0] == '_') return_misformed();
749 		lua_rawgeti(L, -2, 2);
750 		if (!luaW_toconfig(L, -1, cfg.add_child(m)))
751 			return_misformed();
752 		lua_pop(L, 3);
753 	}
754 
755 	// Then convert the attributes (string indices).
756 	for (lua_pushnil(L); lua_next(L, index); lua_pop(L, 1))
757 	{
758 		int indextype = lua_type(L, -2);
759 		if (indextype == LUA_TNUMBER) continue;
760 		if (indextype != LUA_TSTRING) return_misformed();
761 		config::attribute_value &v = cfg[lua_tostring(L, -2)];
762 		if (lua_istable(L, -1)) {
763 			int subindex = lua_absindex(L, -1);
764 			std::ostringstream str;
765 			for (int i = 1, i_end = lua_rawlen(L, subindex); i <= i_end; ++i, lua_pop(L, 1)) {
766 				lua_rawgeti(L, -1, i);
767 				config::attribute_value item;
768 				if (!luaW_toscalar(L, -1, item)) return_misformed();
769 				if (i > 1) str << ',';
770 				str << item;
771 			}
772 			// If there are any string keys, it's malformed
773 			for (lua_pushnil(L); lua_next(L, subindex); lua_pop(L, 1)) {
774 				if (lua_type(L, -2) != LUA_TNUMBER) return_misformed();
775 			}
776 			v = str.str();
777 		} else if (!luaW_toscalar(L, -1, v)) return_misformed();
778 	}
779 
780 	lua_settop(L, initial_top);
781 	return true;
782 }
783 
784 #undef return_misformed
785 
786 
luaW_checkconfig(lua_State * L,int index)787 config luaW_checkconfig(lua_State *L, int index)
788 {
789 	config result;
790 	if (!luaW_toconfig(L, index, result))
791 		luaW_type_error(L, index, "WML table");
792 	return result;
793 }
794 
luaW_checkconfig(lua_State * L,int index,const vconfig * & vcfg)795 config luaW_checkconfig(lua_State *L, int index, const vconfig*& vcfg)
796 {
797 	config result = luaW_checkconfig(L, index);
798 	if(void* p = luaL_testudata(L, index, vconfigKey)) {
799 		vcfg = static_cast<vconfig*>(p);
800 	}
801 	return result;
802 }
803 
luaW_tovconfig(lua_State * L,int index,vconfig & vcfg)804 bool luaW_tovconfig(lua_State *L, int index, vconfig &vcfg)
805 {
806 	switch (lua_type(L, index))
807 	{
808 		case LUA_TTABLE:
809 		{
810 			config cfg;
811 			bool ok = luaW_toconfig(L, index, cfg);
812 			if (!ok) return false;
813 			vcfg = vconfig(std::move(cfg));
814 			break;
815 		}
816 		case LUA_TUSERDATA:
817 			if (vconfig * ptr = static_cast<vconfig *> (luaL_testudata(L, index, vconfigKey))) {
818 				vcfg = *ptr;
819 			} else {
820 				return false;
821 			}
822 		case LUA_TNONE:
823 		case LUA_TNIL:
824 			break;
825 		default:
826 			return false;
827 	}
828 	return true;
829 }
830 
luaW_checkvconfig(lua_State * L,int index,bool allow_missing)831 vconfig luaW_checkvconfig(lua_State *L, int index, bool allow_missing)
832 {
833 	vconfig result = vconfig::unconstructed_vconfig();
834 	if (!luaW_tovconfig(L, index, result) || (!allow_missing && result.null()))
835 		luaW_type_error(L, index, "WML table");
836 	return result;
837 }
838 
luaW_getglobal(lua_State * L,const std::vector<std::string> & path)839 bool luaW_getglobal(lua_State *L, const std::vector<std::string>& path)
840 {
841 	lua_pushglobaltable(L);
842 	for (const std::string& s : path)
843 	{
844 		if (!lua_istable(L, -1)) goto discard;
845 		lua_pushlstring(L, s.c_str(), s.size());
846 		lua_rawget(L, -2);
847 		lua_remove(L, -2);
848 	}
849 
850 	if (lua_isnil(L, -1)) {
851 		discard:
852 		lua_pop(L, 1);
853 		return false;
854 	}
855 	return true;
856 }
857 
luaW_toboolean(lua_State * L,int n)858 bool luaW_toboolean(lua_State *L, int n)
859 {
860 	return lua_toboolean(L,n) != 0;
861 }
862 
luaW_pushvariable(lua_State * L,variable_access_const & v)863 bool luaW_pushvariable(lua_State *L, variable_access_const& v)
864 {
865 	try
866 	{
867 		if(v.exists_as_attribute())
868 		{
869 			luaW_pushscalar(L, v.as_scalar());
870 			return true;
871 		}
872 		else if(v.exists_as_container())
873 		{
874 			lua_newtable(L);
875 			luaW_filltable(L, v.as_container());
876 			return true;
877 		}
878 		else
879 		{
880 			lua_pushnil(L);
881 			return true;
882 		}
883 	}
884 	catch (const invalid_variablename_exception&)
885 	{
886 		WRN_LUA << v.get_error_message() << "\n";
887 		return false;
888 	}
889 }
890 
luaW_checkvariable(lua_State * L,variable_access_create & v,int n)891 bool luaW_checkvariable(lua_State *L, variable_access_create& v, int n)
892 {
893 	int variabletype = lua_type(L, n);
894 	try
895 	{
896 		switch (variabletype) {
897 		case LUA_TBOOLEAN:
898 			v.as_scalar() = luaW_toboolean(L, n);
899 			return true;
900 		case LUA_TNUMBER:
901 			v.as_scalar() = lua_tonumber(L, n);
902 			return true;
903 		case LUA_TSTRING:
904 			v.as_scalar() = lua_tostring(L, n);
905 			return true;
906 		case LUA_TUSERDATA:
907 			if (t_string * t_str = static_cast<t_string*> (luaL_testudata(L, n, tstringKey))) {
908 				v.as_scalar() = *t_str;
909 				return true;
910 			}
911 			goto default_explicit;
912 		case LUA_TTABLE:
913 			{
914 				config &cfg = v.as_container();
915 				cfg.clear();
916 				if (luaW_toconfig(L, n, cfg)) {
917 					return true;
918 				}
919 				FALLTHROUGH;
920 			}
921 		default:
922 		default_explicit:
923 			return luaW_type_error(L, n, "WML table or scalar") != 0;
924 
925 		}
926 	}
927 	catch (const invalid_variablename_exception&)
928 	{
929 		WRN_LUA << v.get_error_message() << " when attempting to write a '" << lua_typename(L, variabletype) << "'\n";
930 		return false;
931 	}
932 }
933 
chat_message(const std::string & caption,const std::string & msg)934 void chat_message(const std::string& caption, const std::string& msg)
935 {
936 	if (!game_display::get_singleton()) return;
937 	game_display::get_singleton()->get_chat_manager().add_chat_message(time(nullptr), caption, 0, msg,
938 														   events::chat_handler::MESSAGE_PUBLIC, false);
939 }
940 
push_error_handler(lua_State * L)941 void push_error_handler(lua_State *L)
942 {
943 	luaW_getglobal(L, "debug", "traceback");
944 	lua_setfield(L, LUA_REGISTRYINDEX, executeKey);
945 }
946 
luaW_pcall_internal(lua_State * L,int nArgs,int nRets)947 int luaW_pcall_internal(lua_State *L, int nArgs, int nRets)
948 {
949 	// Load the error handler before the function and its arguments.
950 	lua_getfield(L, LUA_REGISTRYINDEX, executeKey);
951 	lua_insert(L, -2 - nArgs);
952 
953 	int error_handler_index = lua_gettop(L) - nArgs - 1;
954 
955 	// Call the function.
956 	int errcode = lua_pcall(L, nArgs, nRets, -2 - nArgs);
957 
958 	lua_jailbreak_exception::rethrow();
959 
960 	// Remove the error handler.
961 	lua_remove(L, error_handler_index);
962 
963 	return errcode;
964 }
965 
966 #ifdef _MSC_VER
967 #pragma warning (push)
968 #pragma warning (disable: 4706)
969 #endif
luaW_pcall(lua_State * L,int nArgs,int nRets,bool allow_wml_error)970 bool luaW_pcall(lua_State *L, int nArgs, int nRets, bool allow_wml_error)
971 {
972 	int res = luaW_pcall_internal(L, nArgs, nRets);
973 
974 	if (res)
975 	{
976 		/*
977 		 * When an exception is thrown which doesn't derive from
978 		 * std::exception m will be nullptr pointer.
979 		 */
980 		char const *m = lua_tostring(L, -1);
981 		if(m) {
982 			if (allow_wml_error && strncmp(m, "~wml:", 5) == 0) {
983 				m += 5;
984 				char const *e = strstr(m, "stack traceback");
985 				lg::wml_error() << std::string(m, e ? e - m : strlen(m));
986 			} else if (allow_wml_error && strncmp(m, "~lua:", 5) == 0) {
987 				m += 5;
988 				char const *e = nullptr, *em = m;
989 				while (em[0] && ((em = strstr(em + 1, "stack traceback"))))
990 #ifdef _MSC_VER
991 #pragma warning (pop)
992 #endif
993 					e = em;
994 				chat_message("Lua error", std::string(m, e ? e - m : strlen(m)));
995 			} else {
996 				ERR_LUA << m << '\n';
997 				chat_message("Lua error", m);
998 			}
999 		} else {
1000 			chat_message("Lua caught unknown exception", "");
1001 		}
1002 		lua_pop(L, 1);
1003 		return false;
1004 	}
1005 
1006 	return true;
1007 }
1008 
1009 // Originally luaL_typerror, now deprecated.
1010 // Easier to define it for Wesnoth and not have to worry about it if we update Lua.
luaW_type_error(lua_State * L,int narg,const char * tname)1011 int luaW_type_error(lua_State *L, int narg, const char *tname) {
1012 	const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg));
1013 	return luaL_argerror(L, narg, msg);
1014 }
1015 
1016 // An alternate version which raises an error for a key in a table.
1017 // In this version, narg should refer to the stack index of the table rather than the stack index of the key.
1018 // kpath should be the key name or a string such as "key[idx].key2" specifying a path to the key.
luaW_type_error(lua_State * L,int narg,const char * kpath,const char * tname)1019 int luaW_type_error (lua_State *L, int narg, const char* kpath, const char *tname) {
1020 	const char *msg = lua_pushfstring(L, "%s expected for '%s', got %s", tname, kpath, luaL_typename(L, narg));
1021 	return luaL_argerror(L, narg, msg);
1022 }
1023