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