1 /* NetHack 3.7	nhlua.c	$NHDT-Date: 1580506559 2020/01/31 21:35:59 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.32 $ */
2 /*      Copyright (c) 2018 by Pasi Kallinen */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 #include "hack.h"
6 #include "dlb.h"
7 
8 /*
9 #- include <lua5.3/lua.h>
10 #- include <lua5.3/lualib.h>
11 #- include <lua5.3/lauxlib.h>
12 */
13 
14 /*  */
15 
16 /* lua_CFunction prototypes */
17 static int nhl_test(lua_State *);
18 static int nhl_getmap(lua_State *);
19 static void nhl_add_table_entry_bool(lua_State *, const char *, boolean);
20 static char splev_typ2chr(schar);
21 static int nhl_gettrap(lua_State *);
22 static int nhl_deltrap(lua_State *);
23 #if 0
24 static int nhl_setmap(lua_State *);
25 #endif
26 static int nhl_pline(lua_State *);
27 static int nhl_verbalize(lua_State *);
28 static int nhl_parse_config(lua_State *);
29 static int nhl_menu(lua_State *);
30 static int nhl_getlin(lua_State *);
31 static int nhl_makeplural(lua_State *);
32 static int nhl_makesingular(lua_State *);
33 static int nhl_s_suffix(lua_State *);
34 static int nhl_ing_suffix(lua_State *);
35 static int nhl_an(lua_State *);
36 static int nhl_rn2(lua_State *);
37 static int nhl_random(lua_State *);
38 static int nhl_level_difficulty(lua_State *);
39 static void init_nhc_data(lua_State *);
40 static int nhl_push_anything(lua_State *, int, void *);
41 static int nhl_meta_u_index(lua_State *);
42 static int nhl_meta_u_newindex(lua_State *);
43 static int nhl_u_clear_inventory(lua_State *);
44 static int nhl_u_giveobj(lua_State *);
45 static void init_u_data(lua_State *);
46 static int nhl_set_package_path(lua_State *, const char *);
47 static int traceback_handler(lua_State *);
48 
49 void
nhl_error(lua_State * L,const char * msg)50 nhl_error(lua_State *L, const char *msg)
51 {
52     lua_Debug ar;
53     char buf[BUFSZ];
54 
55     lua_getstack(L, 1, &ar);
56     lua_getinfo(L, "lS", &ar);
57     Sprintf(buf, "%s (line %d ", msg, ar.currentline);
58     Sprintf(eos(buf), "%.*s)",
59             /* (max length of ar.short_src is actually LUA_IDSIZE
60                so this is overkill for it, but crucial for ar.source) */
61             (int) (sizeof buf - (strlen(buf) + sizeof ")")),
62             ar.short_src); /* (used to be 'ar.source' here) */
63     lua_pushstring(L, buf);
64 #if 0 /* defined(PANICTRACE) && !defined(NO_SIGNALS) */
65     panictrace_setsignals(FALSE);
66 #endif
67     (void) lua_error(L);
68     /*NOTREACHED*/
69 }
70 
71 /* Check that parameters are nothing but single table,
72    or if no parameters given, put empty table there */
73 void
lcheck_param_table(lua_State * L)74 lcheck_param_table(lua_State *L)
75 {
76     int argc = lua_gettop(L);
77 
78     if (argc < 1)
79         lua_createtable(L, 0, 0);
80 
81     /* discard any extra arguments passed in */
82     lua_settop(L, 1);
83 
84     luaL_checktype(L, 1, LUA_TTABLE);
85 }
86 
87 schar
get_table_mapchr(lua_State * L,const char * name)88 get_table_mapchr(lua_State *L, const char *name)
89 {
90     char *ter;
91     xchar typ;
92 
93     ter = get_table_str(L, name);
94     typ = check_mapchr(ter);
95     if (typ == INVALID_TYPE)
96         nhl_error(L, "Erroneous map char");
97     if (ter)
98         free(ter);
99     return typ;
100 }
101 
102 schar
get_table_mapchr_opt(lua_State * L,const char * name,schar defval)103 get_table_mapchr_opt(lua_State *L, const char *name, schar defval)
104 {
105     char *ter;
106     xchar typ;
107 
108     ter = get_table_str_opt(L, name, emptystr);
109     if (name && *ter) {
110         typ = check_mapchr(ter);
111         if (typ == INVALID_TYPE)
112             nhl_error(L, "Erroneous map char");
113     } else
114         typ = defval;
115     if (ter)
116         free(ter);
117     return typ;
118 }
119 
120 void
nhl_add_table_entry_int(lua_State * L,const char * name,int value)121 nhl_add_table_entry_int(lua_State *L, const char *name, int value)
122 {
123     lua_pushstring(L, name);
124     lua_pushinteger(L, value);
125     lua_rawset(L, -3);
126 }
127 
128 void
nhl_add_table_entry_char(lua_State * L,const char * name,char value)129 nhl_add_table_entry_char(lua_State *L, const char *name, char value)
130 {
131     char buf[2];
132     Sprintf(buf, "%c", value);
133     lua_pushstring(L, name);
134     lua_pushstring(L, buf);
135     lua_rawset(L, -3);
136 }
137 
138 void
nhl_add_table_entry_str(lua_State * L,const char * name,const char * value)139 nhl_add_table_entry_str(lua_State *L, const char *name, const char *value)
140 {
141     lua_pushstring(L, name);
142     lua_pushstring(L, value);
143     lua_rawset(L, -3);
144 }
145 void
nhl_add_table_entry_bool(lua_State * L,const char * name,boolean value)146 nhl_add_table_entry_bool(lua_State *L, const char *name, boolean value)
147 {
148     lua_pushstring(L, name);
149     lua_pushboolean(L, value);
150     lua_rawset(L, -3);
151 }
152 
153 /* converting from special level "map character" to levl location type
154    and back. order here is important. */
155 const struct {
156     char ch;
157     schar typ;
158 } char2typ[] = {
159                 { ' ', STONE },
160                 { '#', CORR },
161                 { '.', ROOM },
162                 { '-', HWALL },
163                 { '-', TLCORNER },
164                 { '-', TRCORNER },
165                 { '-', BLCORNER },
166                 { '-', BRCORNER },
167                 { '-', CROSSWALL },
168                 { '-', TUWALL },
169                 { '-', TDWALL },
170                 { '-', TLWALL },
171                 { '-', TRWALL },
172                 { '-', DBWALL },
173                 { '|', VWALL },
174                 { '+', DOOR },
175                 { 'A', AIR },
176                 { 'C', CLOUD },
177                 { 'S', SDOOR },
178                 { 'H', SCORR },
179                 { '{', FOUNTAIN },
180                 { '\\', THRONE },
181                 { 'K', SINK },
182                 { '}', MOAT },
183                 { 'P', POOL },
184                 { 'L', LAVAPOOL },
185                 { 'I', ICE },
186                 { 'W', WATER },
187                 { 'T', TREE },
188                 { 'F', IRONBARS }, /* Fe = iron */
189                 { ',', GRASS },
190                 { 'g', GRASS },
191                 { 'x', MAX_TYPE }, /* "see-through" */
192                 { 'B', CROSSWALL }, /* hack: boundary location */
193                 { 'w', MATCH_WALL }, /* IS_STWALL() */
194                 { '\0', STONE },
195 };
196 
197 schar
splev_chr2typ(char c)198 splev_chr2typ(char c)
199 {
200     int i;
201 
202     for (i = 0; char2typ[i].ch; i++)
203         if (c == char2typ[i].ch)
204             return char2typ[i].typ;
205     return (INVALID_TYPE);
206 }
207 
208 schar
check_mapchr(const char * s)209 check_mapchr(const char *s)
210 {
211     if (s && strlen(s) == 1)
212         return splev_chr2typ(s[0]);
213     return INVALID_TYPE;
214 }
215 
216 static char
splev_typ2chr(schar typ)217 splev_typ2chr(schar typ)
218 {
219     int i;
220 
221     for (i = 0; char2typ[i].typ < MAX_TYPE; i++)
222         if (typ == char2typ[i].typ)
223             return char2typ[i].ch;
224     return 'x';
225 }
226 
227 /* local t = gettrap(x,y); */
228 static int
nhl_gettrap(lua_State * L)229 nhl_gettrap(lua_State *L)
230 {
231     int argc = lua_gettop(L);
232 
233     if (argc == 2) {
234         int x = (int) lua_tointeger(L, 1);
235         int y = (int) lua_tointeger(L, 2);
236 
237         if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) {
238             struct trap *ttmp = t_at(x,y);
239 
240             if (ttmp) {
241                 lua_newtable(L);
242 
243                 nhl_add_table_entry_int(L, "tx", ttmp->tx);
244                 nhl_add_table_entry_int(L, "ty", ttmp->ty);
245                 nhl_add_table_entry_int(L, "ttyp", ttmp->ttyp);
246                 nhl_add_table_entry_str(L, "ttyp_name",
247                                         get_trapname_bytype(ttmp->ttyp));
248                 nhl_add_table_entry_bool(L, "tseen", ttmp->tseen);
249                 nhl_add_table_entry_bool(L, "madeby_u", ttmp->madeby_u);
250                 switch (ttmp->ttyp) {
251                 case SQKY_BOARD:
252                     nhl_add_table_entry_int(L, "tnote", ttmp->tnote);
253                     break;
254                 case ROLLING_BOULDER_TRAP:
255                     nhl_add_table_entry_int(L, "launchx", ttmp->launch.x);
256                     nhl_add_table_entry_int(L, "launchy", ttmp->launch.y);
257                     nhl_add_table_entry_int(L, "launch2x", ttmp->launch2.x);
258                     nhl_add_table_entry_int(L, "launch2y", ttmp->launch2.y);
259                     break;
260                 case PIT:
261                 case SPIKED_PIT:
262                     nhl_add_table_entry_int(L, "conjoined", ttmp->conjoined);
263                     break;
264                 }
265                 return 1;
266             } else
267                 nhl_error(L, "No trap at location");
268         } else
269             nhl_error(L, "Coordinates out of range");
270     } else
271         nhl_error(L, "Wrong args");
272     return 0;
273 }
274 
275 /* deltrap(x,y); */
276 static int
nhl_deltrap(lua_State * L)277 nhl_deltrap(lua_State *L)
278 {
279     int argc = lua_gettop(L);
280 
281     if (argc == 2) {
282         int x = (int) lua_tointeger(L, 1);
283         int y = (int) lua_tointeger(L, 2);
284 
285         if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) {
286             struct trap *ttmp = t_at(x,y);
287 
288             if (ttmp)
289                 deltrap(ttmp);
290         }
291     }
292     return 0;
293 }
294 
295 DISABLE_WARNING_UNREACHABLE_CODE
296 
297 /* local loc = getmap(x,y) */
298 static int
nhl_getmap(lua_State * L)299 nhl_getmap(lua_State *L)
300 {
301     int argc = lua_gettop(L);
302 
303     if (argc == 2) {
304         int x = (int) lua_tointeger(L, 1);
305         int y = (int) lua_tointeger(L, 2);
306 
307         if (x >= 0 && x < COLNO && y >= 0 && y < ROWNO) {
308             char buf[BUFSZ];
309             lua_newtable(L);
310 
311             /* FIXME: some should be boolean values */
312             nhl_add_table_entry_int(L, "glyph", levl[x][y].glyph);
313             nhl_add_table_entry_int(L, "typ", levl[x][y].typ);
314             nhl_add_table_entry_str(L, "typ_name",
315                                     levltyp_to_name(levl[x][y].typ));
316             Sprintf(buf, "%c", splev_typ2chr(levl[x][y].typ));
317             nhl_add_table_entry_str(L, "mapchr", buf);
318             nhl_add_table_entry_int(L, "seenv", levl[x][y].seenv);
319             nhl_add_table_entry_bool(L, "horizontal", levl[x][y].horizontal);
320             nhl_add_table_entry_bool(L, "lit", levl[x][y].lit);
321             nhl_add_table_entry_bool(L, "waslit", levl[x][y].waslit);
322             nhl_add_table_entry_int(L, "roomno", levl[x][y].roomno);
323             nhl_add_table_entry_bool(L, "edge", levl[x][y].edge);
324             nhl_add_table_entry_bool(L, "candig", levl[x][y].candig);
325 
326             nhl_add_table_entry_bool(L, "has_trap", t_at(x,y) ? 1 : 0);
327 
328             /* TODO: FIXME: levl[x][y].flags */
329 
330             lua_pushliteral(L, "flags");
331             lua_newtable(L);
332 
333             if (IS_DOOR(levl[x][y].typ) || levl[x][y].typ == SDOOR) {
334                 nhl_add_table_entry_bool(L, "nodoor",
335                                          doorstate(&levl[x][y]) == D_NODOOR);
336                 nhl_add_table_entry_bool(L, "broken",
337                                          doorstate(&levl[x][y]) == D_BROKEN);
338                 nhl_add_table_entry_bool(L, "isopen",
339                                          doorstate(&levl[x][y]) == D_ISOPEN);
340                 nhl_add_table_entry_bool(L, "closed",
341                                          doorstate(&levl[x][y]) == D_CLOSED);
342                 nhl_add_table_entry_bool(L, "locked",
343                                          door_is_locked(&levl[x][y]));
344                 nhl_add_table_entry_bool(L, "trapped",
345                                          door_is_trapped(&levl[x][y]));
346                 nhl_add_table_entry_bool(L, "iron",
347                                          door_is_iron(&levl[x][y]));
348             } else if (IS_ALTAR(levl[x][y].typ)) {
349                 /* TODO: bits 0, 1, 2 */
350                 nhl_add_table_entry_bool(L, "shrine",
351                                          (levl[x][y].flags & AM_SHRINE));
352             } else if (IS_THRONE(levl[x][y].typ)) {
353                 nhl_add_table_entry_bool(L, "looted",
354                                          (levl[x][y].flags & T_LOOTED));
355             } else if (levl[x][y].typ == TREE) {
356                 nhl_add_table_entry_bool(L, "looted",
357                                          (levl[x][y].flags & TREE_LOOTED));
358                 nhl_add_table_entry_bool(L, "swarm",
359                                          (levl[x][y].flags & TREE_SWARM));
360             } else if (IS_FOUNTAIN(levl[x][y].typ)) {
361                 nhl_add_table_entry_bool(L, "looted",
362                                          (levl[x][y].flags & F_LOOTED));
363                 nhl_add_table_entry_bool(L, "warned",
364                                          (levl[x][y].flags & F_WARNED));
365             } else if (IS_SINK(levl[x][y].typ)) {
366                 nhl_add_table_entry_bool(L, "pudding",
367                                          (levl[x][y].flags & S_LPUDDING));
368                 nhl_add_table_entry_bool(L, "dishwasher",
369                                          (levl[x][y].flags & S_LDWASHER));
370                 /* vanilla NetHack allows "ring" to be read from this location;
371                  * xNetHack just has the ring buried under the sink, and
372                  * possibly multiple rings. For now, we don't bother searching
373                  * through buriedobjlist to see if there's a ring at (x, y), and
374                  * the lua table won't contain a 'ring' entry. */
375             }
376             /* TODO: drawbridges, walls, ladders, room=>ICED_xxx */
377 
378             lua_settable(L, -3);
379 
380             return 1;
381         } else {
382             /* TODO: return zerorm instead? */
383             nhl_error(L, "Coordinates out of range");
384             return 0;
385         }
386     } else {
387         nhl_error(L, "Incorrect arguments");
388         return 0;
389     }
390     return 1;
391 }
392 
393 RESTORE_WARNING_CONDEXPR_IS_CONSTANT
394 
395 /* pline("It hits!") */
396 static int
nhl_pline(lua_State * L)397 nhl_pline(lua_State *L)
398 {
399     int argc = lua_gettop(L);
400 
401     if (argc == 1)
402         pline("%s", luaL_checkstring(L, 1));
403     else
404         nhl_error(L, "Wrong args");
405 
406     return 0;
407 }
408 
409 /* verbalize("Fool!") */
410 static int
nhl_verbalize(lua_State * L)411 nhl_verbalize(lua_State *L)
412 {
413     int argc = lua_gettop(L);
414 
415     if (argc == 1)
416         verbalize("%s", luaL_checkstring(L, 1));
417     else
418         nhl_error(L, "Wrong args");
419 
420     return 0;
421 }
422 
423 /* parse_config("OPTIONS=!color") */
424 static int
nhl_parse_config(lua_State * L)425 nhl_parse_config(lua_State *L)
426 {
427     int argc = lua_gettop(L);
428 
429     if (argc == 1)
430         parse_conf_str(luaL_checkstring(L, 1), parse_config_line);
431     else
432         nhl_error(L, "Wrong args");
433 
434     return 0;
435 }
436 
437 /* local windowtype = get_config("windowtype"); */
438 static int
nhl_get_config(lua_State * L)439 nhl_get_config(lua_State *L)
440 {
441     int argc = lua_gettop(L);
442 
443     if (argc == 1) {
444         lua_pushstring(L, get_option_value(luaL_checkstring(L, 1)));
445         return 1;
446     } else
447         nhl_error(L, "Wrong args");
448 
449     return 0;
450 }
451 
452 /*
453   str = getlin("What do you want to call this dungeon level?");
454  */
455 static int
nhl_getlin(lua_State * L)456 nhl_getlin(lua_State *L)
457 {
458     int argc = lua_gettop(L);
459 
460     if (argc == 1) {
461         const char *prompt = luaL_checkstring(L, 1);
462         char buf[BUFSZ];
463 
464         getlin(prompt, buf);
465         lua_pushstring(L, buf);
466         return 1;
467     }
468 
469     nhl_error(L, "Wrong args");
470     return 0;
471 }
472 
473 /*
474  selected = menu("prompt", default, pickX, { "a" = "option a", "b" = "option b", ...})
475  pickX = 0,1,2, or "none", "one", "any" (PICK_X in code)
476 
477  selected = menu("prompt", default, pickX,
478                 { {key:"a", text:"option a"}, {key:"b", text:"option b"}, ... } ) */
479 static int
nhl_menu(lua_State * L)480 nhl_menu(lua_State *L)
481 {
482     static const char *const pickX[] = {"none", "one", "any"}; /* PICK_x */
483     int argc = lua_gettop(L);
484     const char *prompt;
485     const char *defval = "";
486     int pick = PICK_ONE, pick_cnt;
487     winid tmpwin;
488     anything any;
489     menu_item *picks = (menu_item *) 0;
490 
491     if (argc < 2 || argc > 4) {
492         nhl_error(L, "Wrong args");
493         return 0;
494     }
495 
496     prompt = luaL_checkstring(L, 1);
497     if (lua_isstring(L, 2))
498         defval = luaL_checkstring(L, 2);
499     if (lua_isstring(L, 3))
500         pick = luaL_checkoption(L, 3, "one", pickX);
501     luaL_checktype(L, argc, LUA_TTABLE);
502 
503     tmpwin = create_nhwindow(NHW_MENU);
504     start_menu(tmpwin, MENU_BEHAVE_STANDARD);
505 
506     lua_pushnil(L); /* first key */
507     while (lua_next(L, argc) != 0) {
508         const char *str = "";
509         const char *key = "";
510 
511         /* key @ index -2, value @ index -1 */
512         if (lua_istable(L, -1)) {
513             lua_pushliteral(L, "key");
514             lua_gettable(L, -2);
515             key = lua_tostring(L, -1);
516             lua_pop(L, 1);
517 
518             lua_pushliteral(L, "text");
519             lua_gettable(L, -2);
520             str = lua_tostring(L, -1);
521             lua_pop(L, 1);
522 
523             /* TODO: glyph, attr, accel, group accel (all optional) */
524         } else if (lua_isstring(L, -1)) {
525             str = luaL_checkstring(L, -1);
526             key = luaL_checkstring(L, -2);
527         }
528 
529         any = cg.zeroany;
530         if (*key)
531             any.a_char = key[0];
532         add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE, str,
533                  (*defval && *key && defval[0] == key[0])
534                     ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
535 
536         lua_pop(L, 1); /* removes 'value'; keeps 'key' for next iteration */
537     }
538 
539     end_menu(tmpwin, prompt);
540     pick_cnt = select_menu(tmpwin, pick, &picks);
541     destroy_nhwindow(tmpwin);
542 
543     if (pick_cnt > 0) {
544         char buf[2];
545         buf[0] = picks[0].item.a_char;
546 
547         if (pick == PICK_ONE && pick_cnt > 1
548             && *defval && defval[0] == picks[0].item.a_char)
549             buf[0] = picks[1].item.a_char;
550 
551         buf[1] = '\0';
552         lua_pushstring(L, buf);
553         /* TODO: pick any */
554     } else {
555         char buf[2];
556         buf[0] = defval[0];
557         buf[1] = '\0';
558         lua_pushstring(L, buf);
559     }
560 
561     return 1;
562 }
563 
564 /* makeplural("zorkmid") */
565 static int
nhl_makeplural(lua_State * L)566 nhl_makeplural(lua_State *L)
567 {
568     int argc = lua_gettop(L);
569 
570     if (argc == 1)
571         lua_pushstring(L, makeplural(luaL_checkstring(L, 1)));
572     else
573         nhl_error(L, "Wrong args");
574 
575     return 1;
576 }
577 
578 /* makesingular("zorkmids") */
579 static int
nhl_makesingular(lua_State * L)580 nhl_makesingular(lua_State *L)
581 {
582     int argc = lua_gettop(L);
583 
584     if (argc == 1)
585         lua_pushstring(L, makesingular(luaL_checkstring(L, 1)));
586     else
587         nhl_error(L, "Wrong args");
588 
589     return 1;
590 }
591 
592 /* s_suffix("foo") */
593 static int
nhl_s_suffix(lua_State * L)594 nhl_s_suffix(lua_State *L)
595 {
596     int argc = lua_gettop(L);
597 
598     if (argc == 1)
599         lua_pushstring(L, s_suffix(luaL_checkstring(L, 1)));
600     else
601         nhl_error(L, "Wrong args");
602 
603     return 1;
604 }
605 
606 /* ing_suffix("foo") */
607 static int
nhl_ing_suffix(lua_State * L)608 nhl_ing_suffix(lua_State *L)
609 {
610     int argc = lua_gettop(L);
611 
612     if (argc == 1)
613         lua_pushstring(L, ing_suffix(luaL_checkstring(L, 1)));
614     else
615         nhl_error(L, "Wrong args");
616 
617     return 1;
618 }
619 
620 /* an("foo") */
621 static int
nhl_an(lua_State * L)622 nhl_an(lua_State *L)
623 {
624     int argc = lua_gettop(L);
625 
626     if (argc == 1)
627         lua_pushstring(L, an(luaL_checkstring(L, 1)));
628     else
629         nhl_error(L, "Wrong args");
630 
631     return 1;
632 }
633 
634 /* rn2(10) */
635 static int
nhl_rn2(lua_State * L)636 nhl_rn2(lua_State *L)
637 {
638     int argc = lua_gettop(L);
639 
640     if (argc == 1)
641         lua_pushinteger(L, rn2((int) luaL_checkinteger(L, 1)));
642     else
643         nhl_error(L, "Wrong args");
644 
645     return 1;
646 }
647 
648 /* random(10);  -- is the same as rn2(10); */
649 /* random(5,8); -- same as 5 + rn2(8); */
650 static int
nhl_random(lua_State * L)651 nhl_random(lua_State *L)
652 {
653     int argc = lua_gettop(L);
654 
655     if (argc == 1)
656         lua_pushinteger(L, rn2((int) luaL_checkinteger(L, 1)));
657     else if (argc == 2)
658         lua_pushinteger(L, luaL_checkinteger(L, 1) + rn2((int) luaL_checkinteger(L, 2)));
659     else
660         nhl_error(L, "Wrong args");
661 
662     return 1;
663 }
664 
665 /* level_difficulty() */
666 static int
nhl_level_difficulty(lua_State * L)667 nhl_level_difficulty(lua_State *L)
668 {
669     int argc = lua_gettop(L);
670     if (argc == 0) {
671         lua_pushinteger(L, level_difficulty());
672     }
673     else {
674         nhl_error(L, "level_difficulty should not have any args");
675     }
676     return 1;
677 }
678 
679 /* mon_difficulty("water troll") => mons[PM_WATER_TROLL].difficulty */
680 static int
nhl_mon_difficulty(lua_State * L)681 nhl_mon_difficulty(lua_State *L)
682 {
683     int argc = lua_gettop(L);
684     if (argc == 1) {
685         const char *species = luaL_checkstring(L, 1);
686         int mnum = find_montype(L, species, (int *) 0);
687         if (mnum != NON_PM) {
688             lua_pushinteger(L, mons[mnum].difficulty);
689         }
690         else {
691             nhl_error(L, "Unknown monster for difficulty lookup");
692         }
693     }
694     else {
695         nhl_error(L, "mon_difficulty takes only one arg");
696     }
697     return 1;
698 }
699 
700 /* get mandatory integer value from table */
701 int
get_table_int(lua_State * L,const char * name)702 get_table_int(lua_State *L, const char *name)
703 {
704     int ret;
705 
706     lua_getfield(L, -1, name);
707     ret = (int) luaL_checkinteger(L, -1);
708     lua_pop(L, 1);
709     return ret;
710 }
711 
712 /* get optional integer value from table */
713 int
get_table_int_opt(lua_State * L,const char * name,int defval)714 get_table_int_opt(lua_State *L, const char *name, int defval)
715 {
716     int ret = defval;
717 
718     lua_getfield(L, -1, name);
719     if (!lua_isnil(L, -1)) {
720         ret = (int) luaL_checkinteger(L, -1);
721     }
722     lua_pop(L, 1);
723     return ret;
724 }
725 
726 char *
get_table_str(lua_State * L,const char * name)727 get_table_str(lua_State *L, const char *name)
728 {
729     char *ret;
730 
731     lua_getfield(L, -1, name);
732     ret = dupstr(luaL_checkstring(L, -1));
733     lua_pop(L, 1);
734     return ret;
735 }
736 
737 /* get optional string value from table.
738    return value must be freed by caller. */
739 char *
get_table_str_opt(lua_State * L,const char * name,char * defval)740 get_table_str_opt(lua_State *L, const char *name, char *defval)
741 {
742     const char *ret;
743 
744     lua_getfield(L, -1, name);
745     ret = luaL_optstring(L, -1, defval);
746     if (ret) {
747         lua_pop(L, 1);
748         return dupstr(ret);
749     }
750     lua_pop(L, 1);
751     return NULL;
752 }
753 
754 int
get_table_boolean(lua_State * L,const char * name)755 get_table_boolean(lua_State *L, const char *name)
756 {
757     static const char *const boolstr[] = {
758         "true", "false", "yes", "no", NULL
759     };
760     /* static const int boolstr2i[] = { TRUE, FALSE, TRUE, FALSE, -1 }; */
761     int ltyp;
762     int ret = -1;
763 
764     lua_getfield(L, -1, name);
765     ltyp = lua_type(L, -1);
766     if (ltyp == LUA_TSTRING) {
767         ret = luaL_checkoption(L, -1, NULL, boolstr);
768         /* nhUse(boolstr2i[0]); */
769     } else if (ltyp == LUA_TBOOLEAN) {
770         ret = lua_toboolean(L, -1);
771     } else if (ltyp == LUA_TNUMBER) {
772         ret = (int) luaL_checkinteger(L, -1);
773         if ( ret < 0 || ret > 1)
774             ret = -1;
775     }
776     lua_pop(L, 1);
777     if (ret == -1)
778         nhl_error(L, "Expected a boolean");
779     return ret;
780 }
781 
782 int
get_table_boolean_opt(lua_State * L,const char * name,int defval)783 get_table_boolean_opt(lua_State *L, const char *name, int defval)
784 {
785     int ret = defval;
786 
787     lua_getfield(L, -1, name);
788     if (lua_type(L, -1) != LUA_TNIL) {
789         lua_pop(L, 1);
790         return get_table_boolean(L, name);
791     }
792     lua_pop(L, 1);
793     return ret;
794 }
795 
796 /* opts[] is a null-terminated list */
797 int
get_table_option(lua_State * L,const char * name,const char * defval,const char * const opts[])798 get_table_option(lua_State *L,
799                  const char *name,
800                  const char *defval,
801                  const char *const opts[])
802 {
803     int ret;
804 
805     lua_getfield(L, -1, name);
806     ret = luaL_checkoption(L, -1, defval, opts);
807     lua_pop(L, 1);
808     return ret;
809 }
810 
811 /*
812   test( { x = 123, y = 456 } );
813 */
814 static int
nhl_test(lua_State * L)815 nhl_test(lua_State *L)
816 {
817     int x, y;
818     char *name, Player[] = "Player";
819 
820     /* discard any extra arguments passed in */
821     lua_settop(L, 1);
822 
823     luaL_checktype(L, 1, LUA_TTABLE);
824 
825     x = get_table_int(L, "x");
826     y = get_table_int(L, "y");
827     name = get_table_str_opt(L, "name", Player);
828 
829     pline("TEST:{ x=%i, y=%i, name=\"%s\" }", x,y, name);
830 
831     free(name);
832 
833     return 1;
834 }
835 
836 static const struct luaL_Reg nhl_functions[] = {
837     {"test", nhl_test},
838 
839     {"getmap", nhl_getmap},
840 #if 0
841     {"setmap", nhl_setmap},
842 #endif
843     {"gettrap", nhl_gettrap},
844     {"deltrap", nhl_deltrap},
845 
846     {"pline", nhl_pline},
847     {"verbalize", nhl_verbalize},
848     {"menu", nhl_menu},
849     {"getlin", nhl_getlin},
850 
851     {"makeplural", nhl_makeplural},
852     {"makesingular", nhl_makesingular},
853     {"s_suffix", nhl_s_suffix},
854     {"ing_suffix", nhl_ing_suffix},
855     {"an", nhl_an},
856     {"rn2", nhl_rn2},
857     {"random", nhl_random},
858     {"level_difficulty", nhl_level_difficulty},
859     {"mon_difficulty", nhl_mon_difficulty},
860     {"parse_config", nhl_parse_config},
861     {"get_config", nhl_get_config},
862     {"get_config_errors", l_get_config_errors},
863     {NULL, NULL}
864 };
865 
866 static const struct {
867     const char *name;
868     long value;
869 } nhl_consts[] = {
870     { "COLNO",  COLNO },
871     { "ROWNO",  ROWNO },
872     { NULL, 0 },
873 };
874 
875 /* register and init the constants table */
876 static void
init_nhc_data(lua_State * L)877 init_nhc_data(lua_State *L)
878 {
879     int i;
880 
881     lua_newtable(L);
882 
883     for (i = 0; nhl_consts[i].name; i++) {
884         lua_pushstring(L, nhl_consts[i].name);
885         lua_pushinteger(L, nhl_consts[i].value);
886         lua_rawset(L, -3);
887     }
888 
889     lua_setglobal(L, "nhc");
890 }
891 
892 static int
nhl_push_anything(lua_State * L,int anytype,void * src)893 nhl_push_anything(lua_State *L, int anytype, void *src)
894 {
895     anything any = cg.zeroany;
896 
897     switch (anytype) {
898     case ANY_INT: any.a_int = *(int *) src;
899         lua_pushinteger(L, any.a_int);
900         break;
901     case ANY_UCHAR: any.a_uchar = *(uchar *) src;
902         lua_pushinteger(L, any.a_uchar);
903         break;
904     case ANY_SCHAR: any.a_schar = *(schar *) src;
905         lua_pushinteger(L, any.a_schar);
906         break;
907     }
908     return 1;
909 }
910 
911 static int
nhl_meta_u_index(lua_State * L)912 nhl_meta_u_index(lua_State *L)
913 {
914     static const struct {
915         const char *name;
916         void *ptr;
917         int type;
918     } ustruct[] = {
919         { "ux", &(u.ux), ANY_UCHAR },
920         { "uy", &(u.uy), ANY_UCHAR },
921         { "dx", &(u.dx), ANY_SCHAR },
922         { "dy", &(u.dy), ANY_SCHAR },
923         { "dz", &(u.dz), ANY_SCHAR },
924         { "tx", &(u.tx), ANY_UCHAR },
925         { "ty", &(u.ty), ANY_UCHAR },
926         { "ulevel", &(u.ulevel), ANY_INT },
927         { "ulevelmax", &(u.ulevelmax), ANY_INT },
928         { "uhunger", &(u.uhunger), ANY_INT },
929         { "nv_range", &(u.nv_range), ANY_INT },
930         { "xray_range", &(u.xray_range), ANY_INT },
931         { "umonster", &(u.umonster), ANY_INT },
932         { "umonnum", &(u.umonnum), ANY_INT },
933         { "mh", &(u.mh), ANY_INT },
934         { "mhmax", &(u.mhmax), ANY_INT },
935         { "mtimedone", &(u.mtimedone), ANY_INT },
936         { "dlevel", &(u.uz.dlevel), ANY_SCHAR }, /* actually xchar */
937         { "dnum", &(u.uz.dnum), ANY_SCHAR }, /* actually xchar */
938         { "uluck", &(u.uluck), ANY_SCHAR },
939         { "uhp", &(u.uhp), ANY_INT },
940         { "uhpmax", &(u.uhpmax), ANY_INT },
941         { "uen", &(u.uen), ANY_INT },
942         { "uenmax", &(u.uenmax), ANY_INT },
943     };
944     const char *tkey = luaL_checkstring(L, 2);
945     int i;
946 
947     /* FIXME: doesn't really work, eg. negative values for u.dx */
948     for (i = 0; i < SIZE(ustruct); i++)
949         if (!strcmp(tkey, ustruct[i].name)) {
950             return nhl_push_anything(L, ustruct[i].type, ustruct[i].ptr);
951         }
952 
953     if (!strcmp(tkey, "inventory")) {
954         nhl_push_obj(L, g.invent);
955         return 1;
956     } else if (!strcmp(tkey, "role")) {
957         lua_pushstring(L, g.urole.name.m);
958         return 1;
959     }
960 
961     nhl_error(L, "Unknown u table index");
962     return 0;
963 }
964 
965 static int
nhl_meta_u_newindex(lua_State * L)966 nhl_meta_u_newindex(lua_State *L)
967 {
968     nhl_error(L, "Cannot set u table values");
969     return 0;
970 }
971 
972 static int
nhl_u_clear_inventory(lua_State * L UNUSED)973 nhl_u_clear_inventory(lua_State *L UNUSED)
974 {
975     while (g.invent)
976         useupall(g.invent);
977     return 0;
978 }
979 
980 /* Put object into player's inventory */
981 /* u.giveobj(obj.new("rock")); */
982 static int
nhl_u_giveobj(lua_State * L)983 nhl_u_giveobj(lua_State *L)
984 {
985     return nhl_obj_u_giveobj(L);
986 }
987 
988 static const struct luaL_Reg nhl_u_functions[] = {
989     { "clear_inventory", nhl_u_clear_inventory },
990     { "giveobj", nhl_u_giveobj },
991     { NULL, NULL }
992 };
993 
994 static void
init_u_data(lua_State * L)995 init_u_data(lua_State *L)
996 {
997     lua_newtable(L);
998     luaL_setfuncs(L, nhl_u_functions, 0);
999     lua_newtable(L);
1000     lua_pushcfunction(L, nhl_meta_u_index);
1001     lua_setfield(L, -2, "__index");
1002     lua_pushcfunction(L, nhl_meta_u_newindex);
1003     lua_setfield(L, -2, "__newindex");
1004     lua_setmetatable(L, -2);
1005     lua_setglobal(L, "u");
1006 }
1007 
1008 static int
nhl_set_package_path(lua_State * L,const char * path)1009 nhl_set_package_path(lua_State *L, const char *path)
1010 {
1011     lua_getglobal(L, "package");
1012     lua_pushstring(L, path);
1013     lua_setfield(L, -2, "path");
1014     lua_pop(L, 1);
1015     return 0;
1016 }
1017 
1018 static int
traceback_handler(lua_State * L)1019 traceback_handler(lua_State *L)
1020 {
1021     luaL_traceback(L, L, lua_tostring(L, 1), 0);
1022     /* TODO: call impossible() if fuzzing? */
1023     return 1;
1024 }
1025 
1026 /* read lua code/data from a dlb module or an external file
1027    into a string buffer and feed that to lua */
1028 boolean
nhl_loadlua(lua_State * L,const char * fname)1029 nhl_loadlua(lua_State *L, const char *fname)
1030 {
1031 #define LOADCHUNKSIZE (1L << 13) /* 8K */
1032     boolean ret = TRUE;
1033     dlb *fh;
1034     char *buf = (char *) 0, *bufin, *bufout, *p, *nl, *altfname;
1035     long buflen, ct, cnt;
1036     int llret;
1037 
1038     altfname = (char *) alloc(strlen(fname) + 3); /* 3: '('...')\0' */
1039     /* don't know whether 'fname' is inside a dlb container;
1040        if we did, we could choose between "nhdat(<fname>)" and "<fname>"
1041        but since we don't, compromise */
1042     Sprintf(altfname, "(%s)", fname);
1043     fh = dlb_fopen(fname, "r");
1044     if (!fh) {
1045         impossible("nhl_loadlua: Error loading %s", altfname);
1046         ret = FALSE;
1047         goto give_up;
1048     }
1049 
1050     dlb_fseek(fh, 0L, SEEK_END);
1051     buflen = dlb_ftell(fh);
1052     dlb_fseek(fh, 0L, SEEK_SET);
1053 
1054     /* extra +1: room to add final '\n' if missing */
1055     buf = bufout = (char *) alloc(buflen + 1 + 1);
1056     buf[0] = '\0';
1057     bufin = bufout = buf;
1058 
1059     ct = 0L;
1060     while (buflen > 0 || ct) {
1061         /*
1062          * Semi-arbitrarily limit reads to 8K at a time.  That's big
1063          * enough to cover the majority of our Lua files in one bite
1064          * but small enough to fully exercise the partial record
1065          * handling (when processing the castle's level description).
1066          *
1067          * [For an external file (non-DLB), VMS may only be able to
1068          * read at most 32K-1 at a time depending on the file format
1069          * in use, and fseek(SEEK_END) only yields an upper bound on
1070          * the actual amount of data in that situation.]
1071          */
1072         if ((cnt = dlb_fread(bufin, 1, min(buflen, LOADCHUNKSIZE), fh)) < 0L)
1073             break;
1074         buflen -= cnt; /* set up for next iteration, if any */
1075         if (cnt == 0L) {
1076             *bufin = '\n'; /* very last line is unterminated? */
1077             cnt = 1;
1078         }
1079         bufin[cnt] = '\0'; /* fread() doesn't do this */
1080 
1081         /* in case partial line was leftover from previous fread */
1082         bufin -= ct, cnt += ct, ct = 0;
1083 
1084         while (cnt > 0) {
1085             if ((nl = index(bufin, '\n')) != 0) {
1086                 /* normal case, newline is present */
1087                 ct = (long) (nl - bufin + 1L); /* +1: keep the newline */
1088                 for (p = bufin; p <= nl; ++p)
1089                     *bufout++ = *bufin++;
1090                 if (*bufin == '\r')
1091                     ++bufin, ++ct;
1092                 /* update for next loop iteration */
1093                 cnt -= ct;
1094                 ct = 0;
1095             } else if (strlen(bufin) < LOADCHUNKSIZE) {
1096                 /* no newline => partial record; move unprocessed chars
1097                    to front of input buffer (bufin portion of buf[]) */
1098                 ct = cnt = (long) (eos(bufin) - bufin);
1099                 for (p = bufout; cnt > 0; --cnt)
1100                     *p++ = *bufin++;
1101                 *p = '\0';
1102                 bufin = p; /* next fread() populates buf[] starting here */
1103                 /* cnt==0 so inner loop will terminate */
1104             } else {
1105                 /* LOADCHUNKSIZE portion of buffer already completely full */
1106                 impossible("(%s) line too long", altfname);
1107                 goto give_up;
1108             }
1109         }
1110     }
1111     *bufout = '\0';
1112     (void) dlb_fclose(fh);
1113 
1114     llret = luaL_loadbuffer(L, buf, strlen(buf), altfname);
1115     if (llret != LUA_OK) {
1116         impossible("luaL_loadbuffer: Error loading %s: %s",
1117                    altfname, lua_tostring(L, -1));
1118         ret = FALSE;
1119         goto give_up;
1120     } else {
1121         lua_pushcfunction(L, traceback_handler);
1122         lua_insert(L, 1);
1123         if (lua_pcall(L, 0, LUA_MULTRET, -2)) {
1124             impossible("Lua error: %s", lua_tostring(L, -1));
1125             ret = FALSE;
1126             goto give_up;
1127         }
1128     }
1129 
1130  give_up:
1131     if (altfname)
1132         free((genericptr_t) altfname);
1133     if (buf)
1134         free((genericptr_t) buf);
1135     return ret;
1136 }
1137 
1138 lua_State *
nhl_init(void)1139 nhl_init(void)
1140 {
1141     lua_State *L = luaL_newstate();
1142 
1143     iflags.in_lua = TRUE;
1144     luaL_openlibs(L);
1145     nhl_set_package_path(L, "./?.lua");
1146 
1147     /* register nh -table, and functions for it */
1148     lua_newtable(L);
1149     luaL_setfuncs(L, nhl_functions, 0);
1150     lua_setglobal(L, "nh");
1151 
1152     /* init nhc -table */
1153     init_nhc_data(L);
1154 
1155     /* init u -table */
1156     init_u_data(L);
1157 
1158     l_selection_register(L);
1159     l_register_des(L);
1160 
1161     l_obj_register(L);
1162 
1163     if (!nhl_loadlua(L, "nhlib.lua")) {
1164         nhl_done(L);
1165         return (lua_State *) 0;
1166     }
1167 
1168     return L;
1169 }
1170 
1171 void
nhl_done(lua_State * L)1172 nhl_done(lua_State *L)
1173 {
1174     lua_close(L);
1175     iflags.in_lua = FALSE;
1176 }
1177 
1178 boolean
load_lua(const char * name)1179 load_lua(const char *name)
1180 {
1181     boolean ret = TRUE;
1182     lua_State *L = nhl_init();
1183 
1184     if (!L) {
1185         ret = FALSE;
1186         goto give_up;
1187     }
1188 
1189     if (!nhl_loadlua(L, name)) {
1190         ret = FALSE;
1191         goto give_up;
1192     }
1193 
1194  give_up:
1195     nhl_done(L);
1196 
1197     return ret;
1198 }
1199 
1200 DISABLE_WARNING_CONDEXPR_IS_CONSTANT
1201 
1202 const char *
get_lua_version(void)1203 get_lua_version(void)
1204 {
1205     size_t len = (size_t) 0;
1206     const char *vs = (const char *) 0;
1207     lua_State *L;
1208 
1209     if (g.lua_ver[0] == 0) {
1210         L = nhl_init();
1211 
1212         if (L) {
1213             lua_getglobal(L, "_VERSION");
1214             if (lua_isstring(L, -1))
1215                 vs = lua_tolstring (L, -1, &len);
1216             if (vs && len < sizeof g.lua_ver) {
1217                 if (!strncmpi(vs, "Lua", 3)) {
1218                     vs += 3;
1219                     if (*vs == '-' || *vs == ' ')
1220                         vs += 1;
1221                 }
1222                 Strcpy(g.lua_ver, vs);
1223             }
1224         }
1225         lua_close(L);
1226 #ifdef LUA_COPYRIGHT
1227         if (sizeof LUA_COPYRIGHT <= sizeof g.lua_copyright)
1228             Strcpy(g.lua_copyright, LUA_COPYRIGHT);
1229 #endif
1230     }
1231     return (const char *) g.lua_ver;
1232 }
1233 
1234 RESTORE_WARNINGS
1235 
1236 
1237 
1238