1 /* NetHack 3.6 options.c $NHDT-Date: 1578996303 2020/01/14 10:05:03 $ $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.396 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Michael Allison, 2008. */
4 /* NetHack may be freely redistributed. See license for details. */
5
6 #ifdef OPTION_LISTS_ONLY /* (AMIGA) external program for opt lists */
7 #include "config.h"
8 #include "objclass.h"
9 #include "flag.h"
10 NEARDATA struct flag flags; /* provide linkage */
11 #ifdef SYSFLAGS
12 NEARDATA struct sysflag sysflags; /* provide linkage */
13 #endif
14 NEARDATA struct instance_flags iflags; /* provide linkage */
15 #define static
16 #else
17 #include "hack.h"
18 #include "tcap.h"
19 #include <ctype.h>
20 #endif
21
22 #define BACKWARD_COMPAT
23
24 #ifdef DEFAULT_WC_TILED_MAP
25 #define PREFER_TILED TRUE
26 #else
27 #define PREFER_TILED FALSE
28 #endif
29
30 #ifdef CURSES_GRAPHICS
31 extern int curses_read_attrs(const char *attrs);
32 extern char *curses_fmt_attrs(char *);
33 #endif
34
35 enum window_option_types {
36 MESSAGE_OPTION = 1,
37 STATUS_OPTION,
38 MAP_OPTION,
39 MENU_OPTION,
40 TEXT_OPTION
41 };
42
43 #define PILE_LIMIT_DFLT 5
44
45 static char empty_optstr[] = { '\0' };
46
47 /*
48 * NOTE: If you add (or delete) an option, please update the short
49 * options help (option_help()), the long options help (dat/opthelp),
50 * and the current options setting display function (doset()),
51 * and also the Guidebooks.
52 *
53 * The order matters. If an option is a an initial substring of another
54 * option (e.g. time and timed_delay) the shorter one must come first.
55 */
56
57 static struct Bool_Opt {
58 const char *name;
59 boolean *addr, initvalue;
60 int optflags;
61 } boolopt[] = {
62 { "acoustics", &flags.acoustics, TRUE, SET_IN_GAME },
63 #if defined(SYSFLAGS) && defined(AMIGA)
64 /* Amiga altmeta causes Alt+key to be converted into Meta+key by
65 low level nethack code; on by default, can be toggled off if
66 Alt+key is needed for some ASCII chars on non-ASCII keyboard */
67 { "altmeta", &sysflags.altmeta, TRUE, DISP_IN_GAME },
68 #else
69 #ifdef ALTMETA
70 /* non-Amiga altmeta causes nethack's top level command loop to treat
71 two character sequence "ESC c" as M-c, for terminals or emulators
72 which send "ESC c" when Alt+c is pressed; off by default, enabling
73 this can potentially make trouble if user types ESC when nethack
74 is honoring this conversion request (primarily after starting a
75 count prefix prior to a command and then deciding to cancel it) */
76 { "altmeta", &iflags.altmeta, FALSE, SET_IN_GAME },
77 #else
78 { "altmeta", (boolean *) 0, TRUE, DISP_IN_GAME },
79 #endif
80 #endif
81 { "ascii_map", &iflags.wc_ascii_map, !PREFER_TILED, SET_IN_GAME }, /*WC*/
82 #if defined(SYSFLAGS) && defined(MFLOPPY)
83 { "asksavedisk", &sysflags.asksavedisk, FALSE, SET_IN_GAME },
84 #else
85 { "asksavedisk", (boolean *) 0, FALSE, SET_IN_FILE },
86 #endif
87 { "autodescribe", &iflags.autodescribe, TRUE, SET_IN_GAME },
88 { "autodig", &flags.autodig, FALSE, SET_IN_GAME },
89 { "autoopen", &flags.autoopen, TRUE, SET_IN_GAME },
90 { "autopickup", &flags.pickup, TRUE, SET_IN_GAME },
91 { "autoquiver", &flags.autoquiver, FALSE, SET_IN_GAME },
92 #if defined(MICRO) && !defined(AMIGA)
93 { "BIOS", &iflags.BIOS, FALSE, SET_IN_FILE },
94 #else
95 { "BIOS", (boolean *) 0, FALSE, SET_IN_FILE },
96 #endif
97 { "blind", &u.uroleplay.blind, FALSE, DISP_IN_GAME },
98 { "bones", &flags.bones, TRUE, SET_IN_FILE },
99 #ifdef INSURANCE
100 { "checkpoint", &flags.ins_chkpt, TRUE, SET_IN_GAME },
101 #else
102 { "checkpoint", (boolean *) 0, FALSE, SET_IN_FILE },
103 #endif
104 #ifdef MFLOPPY
105 { "checkspace", &iflags.checkspace, TRUE, SET_IN_GAME },
106 #else
107 { "checkspace", (boolean *) 0, FALSE, SET_IN_FILE },
108 #endif
109 { "clicklook", &iflags.clicklook, FALSE, SET_IN_GAME },
110 { "cmdassist", &iflags.cmdassist, TRUE, SET_IN_GAME },
111 #if defined(MICRO) || defined(WIN32) || defined(CURSES_GRAPHICS)
112 { "color", &iflags.wc_color, TRUE, SET_IN_GAME }, /* on/off: use WC or not */
113 #else /* systems that support multiple terminals, many monochrome */
114 { "color", &iflags.wc_color, FALSE, SET_IN_GAME },
115 #endif
116 { "confirm", &flags.confirm, TRUE, SET_IN_GAME },
117 { "dark_room", &flags.dark_room, TRUE, SET_IN_GAME },
118 { "eight_bit_tty", &iflags.wc_eight_bit_input, FALSE, SET_IN_GAME }, /*WC*/
119 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS) || defined(X11_GRAPHICS)
120 { "extmenu", &iflags.extmenu, FALSE, SET_IN_GAME },
121 #else
122 { "extmenu", (boolean *) 0, FALSE, SET_IN_FILE },
123 #endif
124 #ifdef OPT_DISPMAP
125 { "fast_map", &flags.fast_map, TRUE, SET_IN_GAME },
126 #else
127 { "fast_map", (boolean *) 0, TRUE, SET_IN_FILE },
128 #endif
129 { "female", &flags.female, FALSE, DISP_IN_GAME },
130 { "fixinv", &flags.invlet_constant, TRUE, SET_IN_GAME },
131 #if defined(SYSFLAGS) && defined(AMIFLUSH)
132 { "flush", &sysflags.amiflush, FALSE, SET_IN_GAME },
133 #else
134 { "flush", (boolean *) 0, FALSE, SET_IN_FILE },
135 #endif
136 { "force_invmenu", &iflags.force_invmenu, FALSE, SET_IN_GAME },
137 { "fullscreen", &iflags.wc2_fullscreen, FALSE, SET_IN_FILE }, /*WC2*/
138 { "goldX", &iflags.goldX, FALSE, SET_IN_GAME },
139 { "guicolor", &iflags.wc2_guicolor, TRUE, SET_IN_GAME}, /*WC2*/
140 { "help", &flags.help, TRUE, SET_IN_GAME },
141 { "herecmd_menu", &iflags.herecmd_menu, FALSE, SET_IN_GAME },
142 { "hilite_pet", &iflags.wc_hilite_pet, FALSE, SET_IN_GAME }, /*WC*/
143 { "hilite_pile", &iflags.hilite_pile, FALSE, SET_IN_GAME },
144 { "hitpointbar", &iflags.wc2_hitpointbar, FALSE, SET_IN_GAME }, /*WC2*/
145 #ifndef MAC
146 { "ignintr", &flags.ignintr, FALSE, SET_IN_GAME },
147 #else
148 { "ignintr", (boolean *) 0, FALSE, SET_IN_FILE },
149 #endif
150 { "implicit_uncursed", &iflags.implicit_uncursed, TRUE, SET_IN_GAME },
151 { "large_font", &iflags.obsolete, FALSE, SET_IN_FILE }, /* OBSOLETE */
152 { "legacy", &flags.legacy, TRUE, DISP_IN_GAME },
153 { "lit_corridor", &flags.lit_corridor, FALSE, SET_IN_GAME },
154 { "lootabc", &flags.lootabc, FALSE, SET_IN_GAME },
155 #ifdef MAIL
156 { "mail", &flags.biff, TRUE, SET_IN_GAME },
157 #else
158 { "mail", (boolean *) 0, TRUE, SET_IN_FILE },
159 #endif
160 { "mention_walls", &iflags.mention_walls, FALSE, SET_IN_GAME },
161 { "menucolors", &iflags.use_menu_color, FALSE, SET_IN_GAME },
162 /* for menu debugging only*/
163 { "menu_tab_sep", &iflags.menu_tab_sep, FALSE, SET_IN_WIZGAME },
164 { "menu_objsyms", &iflags.menu_head_objsym, FALSE, SET_IN_GAME },
165 #ifdef TTY_GRAPHICS
166 { "menu_overlay", &iflags.menu_overlay, TRUE, SET_IN_GAME },
167 #else
168 { "menu_overlay", (boolean *) 0, FALSE, SET_IN_FILE },
169 #endif
170 { "monpolycontrol", &iflags.mon_polycontrol, FALSE, SET_IN_WIZGAME },
171 #ifdef NEWS
172 { "news", &iflags.news, TRUE, DISP_IN_GAME },
173 #else
174 { "news", (boolean *) 0, FALSE, SET_IN_FILE },
175 #endif
176 { "nudist", &u.uroleplay.nudist, FALSE, DISP_IN_GAME },
177 { "null", &flags.null, TRUE, SET_IN_GAME },
178 #if defined(SYSFLAGS) && defined(MAC)
179 { "page_wait", &sysflags.page_wait, TRUE, SET_IN_GAME },
180 #else
181 { "page_wait", (boolean *) 0, FALSE, SET_IN_FILE },
182 #endif
183 /* moved perm_invent from flags to iflags and out of save file in 3.6.2 */
184 { "perm_invent", &iflags.perm_invent, FALSE, SET_IN_GAME },
185 { "pickup_thrown", &flags.pickup_thrown, TRUE, SET_IN_GAME },
186 { "popup_dialog", &iflags.wc_popup_dialog, FALSE, SET_IN_GAME }, /*WC*/
187 { "preload_tiles", &iflags.wc_preload_tiles, TRUE, DISP_IN_GAME }, /*WC*/
188 { "pushweapon", &flags.pushweapon, FALSE, SET_IN_GAME },
189 #if defined(MICRO) && !defined(AMIGA)
190 { "rawio", &iflags.rawio, FALSE, DISP_IN_GAME },
191 #else
192 { "rawio", (boolean *) 0, FALSE, SET_IN_FILE },
193 #endif
194 { "rest_on_space", &flags.rest_on_space, FALSE, SET_IN_GAME },
195 #ifdef RLECOMP
196 { "rlecomp", &iflags.rlecomp,
197 #if defined(COMPRESS) || defined(ZLIB_COMP)
198 FALSE,
199 #else
200 TRUE,
201 #endif
202 DISP_IN_GAME },
203 #endif
204 { "safe_pet", &flags.safe_dog, TRUE, SET_IN_GAME },
205 { "sanity_check", &iflags.sanity_check, FALSE, SET_IN_WIZGAME },
206 { "selectsaved", &iflags.wc2_selectsaved, TRUE, DISP_IN_GAME }, /*WC*/
207 { "showexp", &flags.showexp, FALSE, SET_IN_GAME },
208 { "showrace", &flags.showrace, FALSE, SET_IN_GAME },
209 #ifdef SCORE_ON_BOTL
210 { "showscore", &flags.showscore, FALSE, SET_IN_GAME },
211 #else
212 { "showscore", (boolean *) 0, FALSE, SET_IN_FILE },
213 #endif
214 { "silent", &flags.silent, TRUE, SET_IN_GAME },
215 { "softkeyboard", &iflags.wc2_softkeyboard, FALSE, SET_IN_FILE }, /*WC2*/
216 { "sortpack", &flags.sortpack, TRUE, SET_IN_GAME },
217 { "sparkle", &flags.sparkle, TRUE, SET_IN_GAME },
218 { "splash_screen", &iflags.wc_splash_screen, TRUE, DISP_IN_GAME }, /*WC*/
219 { "standout", &flags.standout, FALSE, SET_IN_GAME },
220 { "status_updates", &iflags.status_updates, TRUE, DISP_IN_GAME },
221 { "tiled_map", &iflags.wc_tiled_map, PREFER_TILED, DISP_IN_GAME }, /*WC*/
222 { "time", &flags.time, FALSE, SET_IN_GAME },
223 #ifdef TIMED_DELAY
224 { "timed_delay", &flags.nap, TRUE, SET_IN_GAME },
225 #else
226 { "timed_delay", (boolean *) 0, FALSE, SET_IN_GAME },
227 #endif
228 { "tombstone", &flags.tombstone, TRUE, SET_IN_GAME },
229 { "toptenwin", &iflags.toptenwin, FALSE, SET_IN_GAME },
230 { "travel", &flags.travelcmd, TRUE, SET_IN_GAME },
231 #ifdef DEBUG
232 { "travel_debug", &iflags.trav_debug, FALSE, SET_IN_WIZGAME }, /*hack.c*/
233 #endif
234 { "use_darkgray", &iflags.wc2_darkgray, TRUE, SET_IN_FILE }, /*WC2*/
235 #ifdef WIN32
236 { "use_inverse", &iflags.wc_inverse, TRUE, SET_IN_GAME }, /*WC*/
237 #else
238 { "use_inverse", &iflags.wc_inverse, FALSE, SET_IN_GAME }, /*WC*/
239 #endif
240 { "verbose", &flags.verbose, TRUE, SET_IN_GAME },
241 #ifdef TTY_TILES_ESCCODES
242 { "vt_tiledata", &iflags.vt_tiledata, FALSE, SET_IN_FILE },
243 #else
244 { "vt_tiledata", (boolean *) 0, FALSE, SET_IN_FILE },
245 #endif
246 { "whatis_menu", &iflags.getloc_usemenu, FALSE, SET_IN_GAME },
247 { "whatis_moveskip", &iflags.getloc_moveskip, FALSE, SET_IN_GAME },
248 { "wizweight", &iflags.wizweight, FALSE, SET_IN_WIZGAME },
249 { "wraptext", &iflags.wc2_wraptext, FALSE, SET_IN_GAME }, /*WC2*/
250 #ifdef ZEROCOMP
251 { "zerocomp", &iflags.zerocomp,
252 #if defined(COMPRESS) || defined(ZLIB_COMP)
253 FALSE,
254 #else
255 TRUE,
256 #endif
257 DISP_IN_GAME },
258 #endif
259 { (char *) 0, (boolean *) 0, FALSE, 0 }
260 };
261
262 /* compound options, for option_help() and external programs like Amiga
263 * frontend */
264 static struct Comp_Opt {
265 const char *name, *descr;
266 int size; /* for frontends and such allocating space --
267 * usually allowed size of data in game, but
268 * occasionally maximum reasonable size for
269 * typing when game maintains information in
270 * a different format */
271 int optflags;
272 } compopt[] = {
273 { "align", "your starting alignment (lawful, neutral, or chaotic)", 8,
274 DISP_IN_GAME },
275 { "align_message", "message window alignment", 20, DISP_IN_GAME }, /*WC*/
276 { "align_status", "status window alignment", 20, DISP_IN_GAME }, /*WC*/
277 { "altkeyhandler", "alternate key handler", 20, SET_IN_GAME },
278 #ifdef BACKWARD_COMPAT
279 { "boulder", "deprecated (use S_boulder in sym file instead)", 1,
280 SET_IN_GAME },
281 #endif
282 { "catname", "the name of your (first) cat (e.g., catname:Tabby)",
283 PL_PSIZ, DISP_IN_GAME },
284 { "disclose", "the kinds of information to disclose at end of game",
285 sizeof flags.end_disclose * 2, SET_IN_GAME },
286 { "dogname", "the name of your (first) dog (e.g., dogname:Fang)", PL_PSIZ,
287 DISP_IN_GAME },
288 { "dungeon", "the symbols to use in drawing the dungeon map",
289 MAXDCHARS + 1, SET_IN_FILE },
290 { "effects", "the symbols to use in drawing special effects",
291 MAXECHARS + 1, SET_IN_FILE },
292 { "font_map", "the font to use in the map window", 40,
293 DISP_IN_GAME }, /*WC*/
294 { "font_menu", "the font to use in menus", 40, DISP_IN_GAME }, /*WC*/
295 { "font_message", "the font to use in the message window", 40,
296 DISP_IN_GAME }, /*WC*/
297 { "font_size_map", "the size of the map font", 20, DISP_IN_GAME }, /*WC*/
298 { "font_size_menu", "the size of the menu font", 20,
299 DISP_IN_GAME }, /*WC*/
300 { "font_size_message", "the size of the message font", 20,
301 DISP_IN_GAME }, /*WC*/
302 { "font_size_status", "the size of the status font", 20,
303 DISP_IN_GAME }, /*WC*/
304 { "font_size_text", "the size of the text font", 20,
305 DISP_IN_GAME }, /*WC*/
306 { "font_status", "the font to use in status window", 40,
307 DISP_IN_GAME }, /*WC*/
308 { "font_text", "the font to use in text windows", 40,
309 DISP_IN_GAME }, /*WC*/
310 { "fruit", "the name of a fruit you enjoy eating", PL_FSIZ, SET_IN_GAME },
311 { "gender", "your starting gender (male or female)", 8, DISP_IN_GAME },
312 { "horsename", "the name of your (first) horse (e.g., horsename:Silver)",
313 PL_PSIZ, DISP_IN_GAME },
314 { "map_mode", "map display mode under Windows", 20, DISP_IN_GAME }, /*WC*/
315 { "menustyle", "user interface for object selection", MENUTYPELEN,
316 SET_IN_GAME },
317 { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE },
318 { "menu_deselect_page", "deselect all items on this page of a menu", 4,
319 SET_IN_FILE },
320 { "menu_first_page", "jump to the first page in a menu", 4, SET_IN_FILE },
321 { "menu_headings", "text attribute for menu headings", 9, SET_IN_GAME },
322 { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE },
323 { "menu_invert_page", "invert all items on this page of a menu", 4,
324 SET_IN_FILE },
325 { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE },
326 { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE },
327 { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE },
328 { "menu_search", "search for a menu item", 4, SET_IN_FILE },
329 { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE },
330 { "menu_select_page", "select all items on this page of a menu", 4,
331 SET_IN_FILE },
332 { "monsters", "the symbols to use for monsters", MAXMCLASSES,
333 SET_IN_FILE },
334 { "msghistory", "number of top line messages to save", 5, DISP_IN_GAME },
335 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
336 { "msg_window", "the type of message window required", 1, SET_IN_GAME },
337 #else
338 { "msg_window", "the type of message window required", 1, SET_IN_FILE },
339 #endif
340 { "name", "your character's name (e.g., name:Merlin-W)", PL_NSIZ,
341 DISP_IN_GAME },
342 { "mouse_support", "game receives click info from mouse", 0, SET_IN_GAME },
343 { "number_pad", "use the number pad for movement", 1, SET_IN_GAME },
344 { "objects", "the symbols to use for objects", MAXOCLASSES, SET_IN_FILE },
345 { "packorder", "the inventory order of the items in your pack",
346 MAXOCLASSES, SET_IN_GAME },
347 #ifdef CHANGE_COLOR
348 { "palette",
349 #ifndef WIN32
350 "palette (00c/880/-fff is blue/yellow/reverse white)", 15, SET_IN_GAME
351 #else
352 "palette (adjust an RGB color in palette (color-R-G-B)", 15, SET_IN_FILE
353 #endif
354 },
355 #if defined(MAC)
356 { "hicolor", "same as palette, only order is reversed", 15, SET_IN_FILE },
357 #endif
358 #endif
359 { "paranoid_confirmation", "extra prompting in certain situations", 28,
360 SET_IN_GAME },
361 { "petattr", "attributes for highlighting pets", 88, SET_IN_GAME },
362 { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME },
363 { "pickup_burden", "maximum burden picked up before prompt", 20,
364 SET_IN_GAME },
365 { "pickup_types", "types of objects to pick up automatically",
366 MAXOCLASSES, SET_IN_GAME },
367 { "pile_limit", "threshold for \"there are many objects here\"", 24,
368 SET_IN_GAME },
369 { "playmode", "normal play, non-scoring explore mode, or debug mode", 8,
370 DISP_IN_GAME },
371 { "player_selection", "choose character via dialog or prompts", 12,
372 DISP_IN_GAME },
373 { "race", "your starting race (e.g., Human, Elf)", PL_CSIZ,
374 DISP_IN_GAME },
375 { "role", "your starting role (e.g., Barbarian, Valkyrie)", PL_CSIZ,
376 DISP_IN_GAME },
377 { "runmode", "display frequency when `running' or `travelling'",
378 sizeof "teleport", SET_IN_GAME },
379 { "scores", "the parts of the score list you wish to see", 32,
380 SET_IN_GAME },
381 { "scroll_amount", "amount to scroll map when scroll_margin is reached",
382 20, DISP_IN_GAME }, /*WC*/
383 { "scroll_margin", "scroll map when this far from the edge", 20,
384 DISP_IN_GAME }, /*WC*/
385 { "sortloot", "sort object selection lists by description", 4,
386 SET_IN_GAME },
387 #ifdef MSDOS
388 { "soundcard", "type of sound card to use", 20, SET_IN_FILE },
389 #endif
390 { "statushilites",
391 #ifdef STATUS_HILITES
392 "0=no status highlighting, N=show highlights for N turns",
393 20, SET_IN_GAME
394 #else
395 "highlight control", 20, SET_IN_FILE
396 #endif
397 },
398 { "statuslines",
399 #ifdef CURSES_GRAPHICS
400 "2 or 3 lines for horizontal (bottom or top) status display",
401 20, SET_IN_GAME
402 #else
403 "2 or 3 lines for status display",
404 20, SET_IN_FILE
405 #endif
406 }, /*WC2*/
407 { "symset", "load a set of display symbols from the symbols file", 70,
408 SET_IN_GAME },
409 { "roguesymset",
410 "load a set of rogue display symbols from the symbols file", 70,
411 SET_IN_GAME },
412 #ifdef WIN32
413 { "subkeyvalue", "override keystroke value", 7, SET_IN_FILE },
414 #endif
415 { "suppress_alert", "suppress alerts about version-specific features", 8,
416 SET_IN_GAME },
417 /* term_cols,term_rows -> WC2_TERM_SIZE (6: room to format 1..32767) */
418 { "term_cols", "number of columns", 6, SET_IN_FILE }, /*WC2*/
419 { "term_rows", "number of rows", 6, SET_IN_FILE }, /*WC2*/
420 { "tile_width", "width of tiles", 20, DISP_IN_GAME }, /*WC*/
421 { "tile_height", "height of tiles", 20, DISP_IN_GAME }, /*WC*/
422 { "tile_file", "name of tile file", 70, DISP_IN_GAME }, /*WC*/
423 { "traps", "the symbols to use in drawing traps", MAXTCHARS + 1,
424 SET_IN_FILE },
425 { "vary_msgcount", "show more old messages at a time", 20,
426 DISP_IN_GAME }, /*WC*/
427 #ifdef MSDOS
428 { "video", "method of video updating", 20, SET_IN_FILE },
429 #endif
430 #ifdef VIDEOSHADES
431 { "videocolors", "color mappings for internal screen routines", 40,
432 DISP_IN_GAME },
433 { "videoshades", "gray shades to map to black/gray/white", 32,
434 DISP_IN_GAME },
435 #endif
436 { "whatis_coord", "show coordinates when auto-describing cursor position",
437 1, SET_IN_GAME },
438 { "whatis_filter",
439 "filter coordinate locations when targeting next or previous",
440 1, SET_IN_GAME },
441 { "windowborders", "0 (off), 1 (on), 2 (auto)", 9, SET_IN_GAME }, /*WC2*/
442 { "windowcolors", "the foreground/background colors of windows", /*WC*/
443 80, DISP_IN_GAME },
444 { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME },
445 #ifdef WINCHAIN
446 { "windowchain", "window processor to use", WINTYPELEN, SET_IN_SYS },
447 #endif
448 #ifdef BACKWARD_COMPAT
449 { "DECgraphics", "load DECGraphics display symbols", 70, SET_IN_FILE },
450 { "IBMgraphics", "load IBMGraphics display symbols", 70, SET_IN_FILE },
451 #ifdef CURSES_GRAPHICS
452 { "cursesgraphics", "load curses display symbols", 70, SET_IN_FILE },
453 #endif
454 #ifdef MAC_GRAPHICS_ENV
455 { "Macgraphics", "load MACGraphics display symbols", 70, SET_IN_FILE },
456 #endif
457 #endif
458 { (char *) 0, (char *) 0, 0, 0 }
459 };
460
461 #ifdef OPTION_LISTS_ONLY
462 #undef static
463
464 #else /* use rest of file */
465
466 extern char configfile[]; /* for messages */
467
468 extern struct symparse loadsyms[];
469 static boolean need_redraw; /* for doset() */
470
471 #if defined(TOS) && defined(TEXTCOLOR)
472 extern boolean colors_changed; /* in tos.c */
473 #endif
474
475 #ifdef VIDEOSHADES
476 extern char *shade[3]; /* in sys/msdos/video.c */
477 extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
478 #endif
479
480 static char def_inv_order[MAXOCLASSES] = {
481 COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
482 SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
483 TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
484 };
485
486 /*
487 * Default menu manipulation command accelerators. These may _not_ be:
488 *
489 * + a number - reserved for counts
490 * + an upper or lower case US ASCII letter - used for accelerators
491 * + ESC - reserved for escaping the menu
492 * + NULL, CR or LF - reserved for commiting the selection(s). NULL
493 * is kind of odd, but the tty's xwaitforspace() will return it if
494 * someone hits a <ret>.
495 * + a default object class symbol - used for object class accelerators
496 *
497 * Standard letters (for now) are:
498 *
499 * < back 1 page
500 * > forward 1 page
501 * ^ first page
502 * | last page
503 * : search
504 *
505 * page all
506 * , select .
507 * \ deselect -
508 * ~ invert @
509 *
510 * The command name list is duplicated in the compopt array.
511 */
512 typedef struct {
513 const char *name;
514 char cmd;
515 const char *desc;
516 } menu_cmd_t;
517
518 static const menu_cmd_t default_menu_cmd_info[] = {
519 { "menu_first_page", MENU_FIRST_PAGE, "Go to first page" },
520 { "menu_last_page", MENU_LAST_PAGE, "Go to last page" },
521 { "menu_next_page", MENU_NEXT_PAGE, "Go to next page" },
522 { "menu_previous_page", MENU_PREVIOUS_PAGE, "Go to previous page" },
523 { "menu_select_all", MENU_SELECT_ALL, "Select all items" },
524 { "menu_deselect_all", MENU_UNSELECT_ALL, "Unselect all items" },
525 { "menu_invert_all", MENU_INVERT_ALL, "Invert selection" },
526 { "menu_select_page", MENU_SELECT_PAGE, "Select items in current page" },
527 { "menu_deselect_page", MENU_UNSELECT_PAGE,
528 "Unselect items in current page" },
529 { "menu_invert_page", MENU_INVERT_PAGE, "Invert current page selection" },
530 { "menu_search", MENU_SEARCH, "Search and toggle matching items" },
531 };
532
533 /*
534 * Allow the user to map incoming characters to various menu commands.
535 * The accelerator list must be a valid C string.
536 */
537 #define MAX_MENU_MAPPED_CMDS 32 /* some number */
538 char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS + 1]; /* exported */
539 static char mapped_menu_op[MAX_MENU_MAPPED_CMDS + 1];
540 static short n_menu_mapped = 0;
541
542 static boolean initial, from_file;
543
544 STATIC_DCL void FDECL(nmcpy, (char *, const char *, int));
545 STATIC_DCL void FDECL(escapes, (const char *, char *));
546 STATIC_DCL void FDECL(rejectoption, (const char *));
547 STATIC_DCL char *FDECL(string_for_opt, (char *, BOOLEAN_P));
548 STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *, BOOLEAN_P));
549 STATIC_DCL void FDECL(bad_negation, (const char *, BOOLEAN_P));
550 STATIC_DCL int FDECL(change_inv_order, (char *));
551 STATIC_DCL boolean FDECL(warning_opts, (char *, const char *));
552 STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *));
553 STATIC_DCL boolean FDECL(duplicate_opt_detection, (const char *, int));
554 STATIC_DCL void FDECL(complain_about_duplicate, (const char *, int));
555
556 STATIC_DCL const char *FDECL(attr2attrname, (int));
557 STATIC_DCL const char * FDECL(msgtype2name, (int));
558 STATIC_DCL int NDECL(query_msgtype);
559 STATIC_DCL boolean FDECL(msgtype_add, (int, char *));
560 STATIC_DCL void FDECL(free_one_msgtype, (int));
561 STATIC_DCL int NDECL(msgtype_count);
562 STATIC_DCL boolean FDECL(test_regex_pattern, (const char *, const char *));
563 STATIC_DCL boolean FDECL(add_menu_coloring_parsed, (char *, int, int));
564 STATIC_DCL void FDECL(free_one_menu_coloring, (int));
565 STATIC_DCL int NDECL(count_menucolors);
566 STATIC_DCL boolean FDECL(parse_role_opts, (BOOLEAN_P, const char *,
567 char *, char **));
568
569 STATIC_DCL void FDECL(doset_add_menu, (winid, const char *, int));
570 STATIC_DCL void FDECL(opts_add_others, (winid, const char *, int,
571 char *, int));
572 STATIC_DCL int FDECL(handle_add_list_remove, (const char *, int));
573 STATIC_DCL boolean FDECL(special_handling, (const char *,
574 BOOLEAN_P, BOOLEAN_P));
575 STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *));
576 STATIC_DCL void FDECL(remove_autopickup_exception,
577 (struct autopickup_exception *));
578
579 STATIC_DCL boolean FDECL(is_wc_option, (const char *));
580 STATIC_DCL boolean FDECL(wc_supported, (const char *));
581 STATIC_DCL boolean FDECL(is_wc2_option, (const char *));
582 STATIC_DCL boolean FDECL(wc2_supported, (const char *));
583 STATIC_DCL void FDECL(wc_set_font_name, (int, char *));
584 STATIC_DCL int FDECL(wc_set_window_colors, (char *));
585
586 void
reglyph_darkroom()587 reglyph_darkroom()
588 {
589 xchar x, y;
590
591 for (x = 0; x < COLNO; x++)
592 for (y = 0; y < ROWNO; y++) {
593 struct rm *lev = &levl[x][y];
594
595 if (!flags.dark_room || !iflags.use_color
596 || Is_rogue_level(&u.uz)) {
597 if (lev->glyph == cmap_to_glyph(S_darkroom))
598 lev->glyph = lev->waslit ? cmap_to_glyph(S_room)
599 : cmap_to_glyph(S_stone);
600 } else {
601 if (lev->glyph == cmap_to_glyph(S_room) && lev->seenv
602 && lev->waslit && !cansee(x, y))
603 lev->glyph = cmap_to_glyph(S_darkroom);
604 else if (lev->glyph == cmap_to_glyph(S_stone)
605 && lev->typ == ROOM && lev->seenv && !cansee(x, y))
606 lev->glyph = cmap_to_glyph(S_darkroom);
607 }
608 }
609 if (flags.dark_room && iflags.use_color)
610 showsyms[S_darkroom] = showsyms[S_room];
611 else
612 showsyms[S_darkroom] = showsyms[S_stone];
613 }
614
615 /* check whether a user-supplied option string is a proper leading
616 substring of a particular option name; option string might have
617 a colon or equals sign and arbitrary value appended to it */
618 boolean
match_optname(user_string,opt_name,min_length,val_allowed)619 match_optname(user_string, opt_name, min_length, val_allowed)
620 const char *user_string, *opt_name;
621 int min_length;
622 boolean val_allowed;
623 {
624 int len = (int) strlen(user_string);
625
626 if (val_allowed) {
627 const char *p = index(user_string, ':'),
628 *q = index(user_string, '=');
629
630 if (!p || (q && q < p))
631 p = q;
632 if (p) {
633 /* 'user_string' hasn't necessarily been through mungspaces()
634 so might have tabs or consecutive spaces */
635 while (p > user_string && isspace((uchar) *(p - 1)))
636 p--;
637 len = (int) (p - user_string);
638 }
639 }
640
641 return (boolean) (len >= min_length
642 && !strncmpi(opt_name, user_string, len));
643 }
644
645 /* most environment variables will eventually be printed in an error
646 * message if they don't work, and most error message paths go through
647 * BUFSZ buffers, which could be overflowed by a maliciously long
648 * environment variable. If a variable can legitimately be long, or
649 * if it's put in a smaller buffer, the responsible code will have to
650 * bounds-check itself.
651 */
652 char *
nh_getenv(ev)653 nh_getenv(ev)
654 const char *ev;
655 {
656 char *getev = getenv(ev);
657
658 if (getev && strlen(getev) <= (BUFSZ / 2))
659 return getev;
660 else
661 return (char *) 0;
662 }
663
664 /* process options, possibly including SYSCF */
665 void
initoptions()666 initoptions()
667 {
668 initoptions_init();
669 #ifdef SYSCF
670 /* someday there may be other SYSCF alternatives besides text file */
671 #ifdef SYSCF_FILE
672 /* If SYSCF_FILE is specified, it _must_ exist... */
673 assure_syscf_file();
674 config_error_init(TRUE, SYSCF_FILE, FALSE);
675
676 /* ... and _must_ parse correctly. */
677 if (!read_config_file(SYSCF_FILE, SET_IN_SYS)) {
678 if (config_error_done() && !iflags.initoptions_noterminate)
679 nh_terminate(EXIT_FAILURE);
680 }
681 config_error_done();
682 /*
683 * TODO [maybe]: parse the sysopt entries which are space-separated
684 * lists of usernames into arrays with one name per element.
685 */
686 #endif
687 #endif /* SYSCF */
688 initoptions_finish();
689 }
690
691 void
initoptions_init()692 initoptions_init()
693 {
694 #if (defined(UNIX) || defined(VMS)) && defined(TTY_GRAPHICS)
695 char *opts;
696 #endif
697 int i;
698
699 /* set up the command parsing */
700 reset_commands(TRUE); /* init */
701
702 /* initialize the random number generator(s) */
703 init_random(rn2);
704 init_random(rn2_on_display_rng);
705
706 /* for detection of configfile options specified multiple times */
707 iflags.opt_booldup = iflags.opt_compdup = (int *) 0;
708
709 for (i = 0; boolopt[i].name; i++) {
710 if (boolopt[i].addr)
711 *(boolopt[i].addr) = boolopt[i].initvalue;
712 }
713 #if defined(COMPRESS) || defined(ZLIB_COMP)
714 set_savepref("externalcomp");
715 set_restpref("externalcomp");
716 #ifdef RLECOMP
717 set_savepref("!rlecomp");
718 set_restpref("!rlecomp");
719 #endif
720 #else
721 #ifdef ZEROCOMP
722 set_savepref("zerocomp");
723 set_restpref("zerocomp");
724 #endif
725 #ifdef RLECOMP
726 set_savepref("rlecomp");
727 set_restpref("rlecomp");
728 #endif
729 #endif
730 #ifdef SYSFLAGS
731 Strcpy(sysflags.sysflagsid, "sysflags");
732 sysflags.sysflagsid[9] = (char) sizeof (struct sysflag);
733 #endif
734 flags.end_own = FALSE;
735 flags.end_top = 3;
736 flags.end_around = 2;
737 flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */
738 flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */
739 flags.runmode = RUN_LEAP;
740 iflags.msg_history = 20;
741 /* msg_window has conflicting defaults for multi-interface binary */
742 #ifdef TTY_GRAPHICS
743 iflags.prevmsg_window = 's';
744 #else
745 #ifdef CURSES_GRAPHICS
746 iflags.prevmsg_window = 'r';
747 #endif
748 #endif
749 iflags.menu_headings = ATR_INVERSE;
750 iflags.getpos_coords = GPCOORDS_NONE;
751
752 /* hero's role, race, &c haven't been chosen yet */
753 flags.initrole = flags.initrace = flags.initgend = flags.initalign
754 = ROLE_NONE;
755
756 init_ov_primary_symbols();
757 init_ov_rogue_symbols();
758 /* Set the default monster and object class symbols. */
759 init_symbols();
760 for (i = 0; i < WARNCOUNT; i++)
761 warnsyms[i] = def_warnsyms[i].sym;
762
763 /* for "special achievement" tracking (see obj.h,
764 create_object(sp_lev.c), addinv_core1(invent.c) */
765 iflags.mines_prize_type = LUCKSTONE;
766 iflags.soko_prize_type1 = BAG_OF_HOLDING;
767 iflags.soko_prize_type2 = AMULET_OF_REFLECTION;
768
769 /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
770 (void) memcpy((genericptr_t) flags.inv_order,
771 (genericptr_t) def_inv_order, sizeof flags.inv_order);
772 flags.pickup_types[0] = '\0';
773 flags.pickup_burden = MOD_ENCUMBER;
774 flags.sortloot = 'l'; /* sort only loot by default */
775
776 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
777 flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
778 switch_symbols(FALSE); /* set default characters */
779 init_rogue_symbols();
780 #if defined(UNIX) && defined(TTY_GRAPHICS)
781 /*
782 * Set defaults for some options depending on what we can
783 * detect about the environment's capabilities.
784 * This has to be done after the global initialization above
785 * and before reading user-specific initialization via
786 * config file/environment variable below.
787 */
788 /* this detects the IBM-compatible console on most 386 boxes */
789 if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
790 if (!symset[PRIMARY].explicitly)
791 load_symset("IBMGraphics", PRIMARY);
792 if (!symset[ROGUESET].explicitly)
793 load_symset("RogueIBM", ROGUESET);
794 switch_symbols(TRUE);
795 #ifdef TEXTCOLOR
796 iflags.use_color = TRUE;
797 #endif
798 }
799 #endif /* UNIX && TTY_GRAPHICS */
800 #if defined(UNIX) || defined(VMS)
801 #ifdef TTY_GRAPHICS
802 /* detect whether a "vt" terminal can handle alternate charsets */
803 if ((opts = nh_getenv("TERM"))
804 /* [could also check "xterm" which emulates vtXXX by default] */
805 && !strncmpi(opts, "vt", 2)
806 && AS && AE && index(AS, '\016') && index(AE, '\017')) {
807 if (!symset[PRIMARY].explicitly)
808 load_symset("DECGraphics", PRIMARY);
809 switch_symbols(TRUE);
810 }
811 #endif
812 #endif /* UNIX || VMS */
813
814 #if defined(MSDOS) || defined(WIN32)
815 /* Use IBM defaults. Can be overridden via config file */
816 if (!symset[PRIMARY].explicitly)
817 load_symset("IBMGraphics_2", PRIMARY);
818 if (!symset[ROGUESET].explicitly)
819 load_symset("RogueEpyx", ROGUESET);
820 #endif
821 #ifdef MAC_GRAPHICS_ENV
822 if (!symset[PRIMARY].explicitly)
823 load_symset("MACGraphics", PRIMARY);
824 switch_symbols(TRUE);
825 #endif /* MAC_GRAPHICS_ENV */
826 flags.menu_style = MENU_FULL;
827
828 iflags.wc_align_message = ALIGN_TOP;
829 iflags.wc_align_status = ALIGN_BOTTOM;
830 /* used by tty and curses */
831 iflags.wc2_statuslines = 2;
832 /* only used by curses */
833 iflags.wc2_windowborders = 2; /* 'Auto' */
834
835 /* since this is done before init_objects(), do partial init here */
836 objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
837 nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
838 }
839
840 void
initoptions_finish()841 initoptions_finish()
842 {
843 nhsym sym = 0;
844 #ifndef MAC
845 char *opts = getenv("NETHACKOPTIONS");
846
847 if (!opts)
848 opts = getenv("HACKOPTIONS");
849 if (opts) {
850 if (*opts == '/' || *opts == '\\' || *opts == '@') {
851 if (*opts == '@')
852 opts++; /* @filename */
853 /* looks like a filename */
854 if (strlen(opts) < BUFSZ / 2) {
855 config_error_init(TRUE, opts, CONFIG_ERROR_SECURE);
856 read_config_file(opts, SET_IN_FILE);
857 config_error_done();
858 }
859 } else {
860 config_error_init(TRUE, (char *) 0, FALSE);
861 read_config_file((char *) 0, SET_IN_FILE);
862 config_error_done();
863 /* let the total length of options be long;
864 * parseoptions() will check each individually
865 */
866 config_error_init(FALSE, "NETHACKOPTIONS", FALSE);
867 (void) parseoptions(opts, TRUE, FALSE);
868 config_error_done();
869 }
870 } else
871 #endif /* !MAC */
872 /*else*/ {
873 config_error_init(TRUE, (char *) 0, FALSE);
874 read_config_file((char *) 0, SET_IN_FILE);
875 config_error_done();
876 }
877
878 (void) fruitadd(pl_fruit, (struct fruit *) 0);
879 /*
880 * Remove "slime mold" from list of object names. This will
881 * prevent it from being wished unless it's actually present
882 * as a named (or default) fruit. Wishing for "fruit" will
883 * result in the player's preferred fruit [better than "\033"].
884 */
885 obj_descr[SLIME_MOLD].oc_name = "fruit";
886
887 sym = get_othersym(SYM_BOULDER,
888 Is_rogue_level(&u.uz) ? ROGUESET : PRIMARY);
889 if (sym)
890 showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
891 reglyph_darkroom();
892
893 #ifdef STATUS_HILITES
894 /*
895 * A multi-interface binary might only support status highlighting
896 * for some of the interfaces; check whether we asked for it but are
897 * using one which doesn't.
898 *
899 * Option processing can take place before a user-decided WindowPort
900 * is even initialized, so check for that too.
901 */
902 if (!WINDOWPORT("safe-startup")) {
903 if (iflags.hilite_delta && !wc2_supported("statushilites")) {
904 raw_printf("Status highlighting not supported for %s interface.",
905 windowprocs.name);
906 iflags.hilite_delta = 0;
907 }
908 }
909 #endif
910 return;
911 }
912
913 /* copy up to maxlen-1 characters; 'dest' must be able to hold maxlen;
914 treat comma as alternate end of 'src' */
915 STATIC_OVL void
nmcpy(dest,src,maxlen)916 nmcpy(dest, src, maxlen)
917 char *dest;
918 const char *src;
919 int maxlen;
920 {
921 int count;
922
923 for (count = 1; count < maxlen; count++) {
924 if (*src == ',' || *src == '\0')
925 break; /*exit on \0 terminator*/
926 *dest++ = *src++;
927 }
928 *dest = '\0';
929 }
930
931 /*
932 * escapes(): escape expansion for showsyms. C-style escapes understood
933 * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal).
934 * (Note: unlike in C, leading digit 0 is not used to indicate octal;
935 * the letter o (either upper or lower case) is used for that.
936 * The ^-prefix for control characters is also understood, and \[mM]
937 * has the effect of 'meta'-ing the value which follows (so that the
938 * alternate character set will be enabled).
939 *
940 * X normal key X
941 * ^X control-X
942 * \mX meta-X
943 *
944 * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
945 * prior to terminating '\0' would pull that '\0' into the output and then
946 * keep processing past it, potentially overflowing the output buffer.
947 * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
948 * output and stop there; trailing \M will fall through to \<other> and
949 * yield 'M', then stop. Any \X or \O followed by something other than
950 * an appropriate digit will also fall through to \<other> and yield 'X'
951 * or 'O', plus stop if the non-digit is end-of-string.
952 */
953 STATIC_OVL void
escapes(cp,tp)954 escapes(cp, tp)
955 const char *cp; /* might be 'tp', updating in place */
956 char *tp; /* result is never longer than 'cp' */
957 {
958 static NEARDATA const char oct[] = "01234567", dec[] = "0123456789",
959 hex[] = "00112233445566778899aAbBcCdDeEfF";
960 const char *dp;
961 int cval, meta, dcount;
962
963 while (*cp) {
964 /* \M has to be followed by something to do meta conversion,
965 otherwise it will just be \M which ultimately yields 'M' */
966 meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
967 if (meta)
968 cp += 2;
969
970 cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
971 if ((*cp != '\\' && *cp != '^') || !cp[1]) {
972 /* simple character, or nothing left for \ or ^ to escape */
973 cval = *cp++;
974 } else if (*cp == '^') { /* expand control-character syntax */
975 cval = (*++cp & 0x1f);
976 ++cp;
977
978 /* remaining cases are all for backslash; we know cp[1] is not \0 */
979 } else if (index(dec, cp[1])) {
980 ++cp; /* move past backslash to first digit */
981 do {
982 cval = (cval * 10) + (*cp - '0');
983 } while (*++cp && index(dec, *cp) && ++dcount < 3);
984 } else if ((cp[1] == 'o' || cp[1] == 'O') && cp[2]
985 && index(oct, cp[2])) {
986 cp += 2; /* move past backslash and 'O' */
987 do {
988 cval = (cval * 8) + (*cp - '0');
989 } while (*++cp && index(oct, *cp) && ++dcount < 3);
990 } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2]
991 && (dp = index(hex, cp[2])) != 0) {
992 cp += 2; /* move past backslash and 'X' */
993 do {
994 cval = (cval * 16) + ((int) (dp - hex) / 2);
995 } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
996 } else { /* C-style character escapes */
997 switch (*++cp) {
998 case '\\':
999 cval = '\\';
1000 break;
1001 case 'n':
1002 cval = '\n';
1003 break;
1004 case 't':
1005 cval = '\t';
1006 break;
1007 case 'b':
1008 cval = '\b';
1009 break;
1010 case 'r':
1011 cval = '\r';
1012 break;
1013 default:
1014 cval = *cp;
1015 }
1016 ++cp;
1017 }
1018
1019 if (meta)
1020 cval |= 0x80;
1021 *tp++ = (char) cval;
1022 }
1023 *tp = '\0';
1024 }
1025
1026 STATIC_OVL void
rejectoption(optname)1027 rejectoption(optname)
1028 const char *optname;
1029 {
1030 #ifdef MICRO
1031 pline("\"%s\" settable only from %s.", optname, configfile);
1032 #else
1033 pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
1034 configfile);
1035 #endif
1036 }
1037
1038 /*
1039
1040 # errors:
1041 OPTIONS=aaaaaaaaaa[ more than 247 (255 - 8 for 'OPTIONS=') total ]aaaaaaaaaa
1042 OPTIONS
1043 OPTIONS=
1044 MSGTYPE=stop"You swap places with "
1045 MSGTYPE=st.op "You swap places with "
1046 MSGTYPE=stop "You swap places with \"
1047 MENUCOLOR=" blessed "green&none
1048 MENUCOLOR=" holy " = green&reverse
1049 MENUCOLOR=" cursed " = red&uline
1050 MENUCOLOR=" unholy " = reed
1051 OPTIONS=!legacy:true,fooo
1052 OPTIONS=align:!pin
1053 OPTIONS=gender
1054
1055 */
1056
1057 STATIC_OVL char *
string_for_opt(opts,val_optional)1058 string_for_opt(opts, val_optional)
1059 char *opts;
1060 boolean val_optional;
1061 {
1062 char *colon, *equals;
1063
1064 colon = index(opts, ':');
1065 equals = index(opts, '=');
1066 if (!colon || (equals && equals < colon))
1067 colon = equals;
1068
1069 if (!colon || !*++colon) {
1070 if (!val_optional)
1071 config_error_add("Missing parameter for '%s'", opts);
1072 return empty_optstr;
1073 }
1074 return colon;
1075 }
1076
1077 STATIC_OVL char *
string_for_env_opt(optname,opts,val_optional)1078 string_for_env_opt(optname, opts, val_optional)
1079 const char *optname;
1080 char *opts;
1081 boolean val_optional;
1082 {
1083 if (!initial) {
1084 rejectoption(optname);
1085 return empty_optstr;
1086 }
1087 return string_for_opt(opts, val_optional);
1088 }
1089
1090 STATIC_OVL void
bad_negation(optname,with_parameter)1091 bad_negation(optname, with_parameter)
1092 const char *optname;
1093 boolean with_parameter;
1094 {
1095 pline_The("%s option may not %sbe negated.", optname,
1096 with_parameter ? "both have a value and " : "");
1097 }
1098
1099 /*
1100 * Change the inventory order, using the given string as the new order.
1101 * Missing characters in the new order are filled in at the end from
1102 * the current inv_order, except for gold, which is forced to be first
1103 * if not explicitly present.
1104 *
1105 * This routine returns 1 unless there is a duplicate or bad char in
1106 * the string.
1107 */
1108 STATIC_OVL int
change_inv_order(op)1109 change_inv_order(op)
1110 char *op;
1111 {
1112 int oc_sym, num;
1113 char *sp, buf[QBUFSZ];
1114 int retval = 1;
1115
1116 num = 0;
1117 if (!index(op, GOLD_SYM))
1118 buf[num++] = COIN_CLASS;
1119
1120 for (sp = op; *sp; sp++) {
1121 boolean fail = FALSE;
1122 oc_sym = def_char_to_objclass(*sp);
1123 /* reject bad or duplicate entries */
1124 if (oc_sym == MAXOCLASSES) { /* not an object class char */
1125 config_error_add("Not an object class '%c'", *sp);
1126 retval = 0;
1127 fail = TRUE;
1128 } else if (!index(flags.inv_order, oc_sym)) {
1129 /* VENOM_CLASS, RANDOM_CLASS, and ILLOBJ_CLASS are excluded
1130 because they aren't in def_inv_order[] so don't make it
1131 into flags.inv_order, hence always fail this index() test */
1132 config_error_add("Object class '%c' not allowed", *sp);
1133 retval = 0;
1134 fail = TRUE;
1135 } else if (index(sp + 1, *sp)) {
1136 config_error_add("Duplicate object class '%c'", *sp);
1137 retval = 0;
1138 fail = TRUE;
1139 }
1140 /* retain good ones */
1141 if (!fail)
1142 buf[num++] = (char) oc_sym;
1143 }
1144 buf[num] = '\0';
1145
1146 /* fill in any omitted classes, using previous ordering */
1147 for (sp = flags.inv_order; *sp; sp++)
1148 if (!index(buf, *sp))
1149 (void) strkitten(&buf[num++], *sp);
1150 buf[MAXOCLASSES - 1] = '\0';
1151
1152 Strcpy(flags.inv_order, buf);
1153 return retval;
1154 }
1155
1156 STATIC_OVL boolean
warning_opts(opts,optype)1157 warning_opts(opts, optype)
1158 register char *opts;
1159 const char *optype;
1160 {
1161 uchar translate[WARNCOUNT];
1162 int length, i;
1163
1164 if ((opts = string_for_env_opt(optype, opts, FALSE)) == empty_optstr)
1165 return FALSE;
1166 escapes(opts, opts);
1167
1168 length = (int) strlen(opts);
1169 /* match the form obtained from PC configuration files */
1170 for (i = 0; i < WARNCOUNT; i++)
1171 translate[i] = (i >= length) ? 0
1172 : opts[i] ? (uchar) opts[i]
1173 : def_warnsyms[i].sym;
1174 assign_warnings(translate);
1175 return TRUE;
1176 }
1177
1178 void
assign_warnings(graph_chars)1179 assign_warnings(graph_chars)
1180 register uchar *graph_chars;
1181 {
1182 int i;
1183
1184 for (i = 0; i < WARNCOUNT; i++)
1185 if (graph_chars[i])
1186 warnsyms[i] = graph_chars[i];
1187 }
1188
1189 STATIC_OVL int
feature_alert_opts(op,optn)1190 feature_alert_opts(op, optn)
1191 char *op;
1192 const char *optn;
1193 {
1194 char buf[BUFSZ];
1195 unsigned long fnv = get_feature_notice_ver(op); /* version.c */
1196
1197 if (fnv == 0L)
1198 return 0;
1199 if (fnv > get_current_feature_ver()) {
1200 if (!initial) {
1201 You_cant("disable new feature alerts for future versions.");
1202 } else {
1203 config_error_add(
1204 "%s=%s Invalid reference to a future version ignored",
1205 optn, op);
1206 }
1207 return 0;
1208 }
1209
1210 flags.suppress_alert = fnv;
1211 if (!initial) {
1212 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
1213 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
1214 pline(
1215 "Feature change alerts disabled for NetHack %s features and prior.",
1216 buf);
1217 }
1218 return 1;
1219 }
1220
1221 void
set_duplicate_opt_detection(on_or_off)1222 set_duplicate_opt_detection(on_or_off)
1223 int on_or_off;
1224 {
1225 int k, *optptr;
1226
1227 if (on_or_off != 0) {
1228 /*-- ON --*/
1229 if (iflags.opt_booldup)
1230 impossible("iflags.opt_booldup already on (memory leak)");
1231 iflags.opt_booldup = (int *) alloc(SIZE(boolopt) * sizeof (int));
1232 optptr = iflags.opt_booldup;
1233 for (k = 0; k < SIZE(boolopt); ++k)
1234 *optptr++ = 0;
1235
1236 if (iflags.opt_compdup)
1237 impossible("iflags.opt_compdup already on (memory leak)");
1238 iflags.opt_compdup = (int *) alloc(SIZE(compopt) * sizeof (int));
1239 optptr = iflags.opt_compdup;
1240 for (k = 0; k < SIZE(compopt); ++k)
1241 *optptr++ = 0;
1242 } else {
1243 /*-- OFF --*/
1244 if (iflags.opt_booldup)
1245 free((genericptr_t) iflags.opt_booldup);
1246 iflags.opt_booldup = (int *) 0;
1247 if (iflags.opt_compdup)
1248 free((genericptr_t) iflags.opt_compdup);
1249 iflags.opt_compdup = (int *) 0;
1250 }
1251 }
1252
1253 STATIC_OVL boolean
duplicate_opt_detection(opts,iscompound)1254 duplicate_opt_detection(opts, iscompound)
1255 const char *opts;
1256 int iscompound; /* 0 == boolean option, 1 == compound */
1257 {
1258 int i, *optptr;
1259
1260 if (!iscompound && iflags.opt_booldup && initial && from_file) {
1261 for (i = 0; boolopt[i].name; i++) {
1262 if (match_optname(opts, boolopt[i].name, 3, FALSE)) {
1263 optptr = iflags.opt_booldup + i;
1264 *optptr += 1;
1265 if (*optptr > 1)
1266 return TRUE;
1267 else
1268 return FALSE;
1269 }
1270 }
1271 } else if (iscompound && iflags.opt_compdup && initial && from_file) {
1272 for (i = 0; compopt[i].name; i++) {
1273 if (match_optname(opts, compopt[i].name, strlen(compopt[i].name),
1274 TRUE)) {
1275 optptr = iflags.opt_compdup + i;
1276 *optptr += 1;
1277 if (*optptr > 1)
1278 return TRUE;
1279 else
1280 return FALSE;
1281 }
1282 }
1283 }
1284 return FALSE;
1285 }
1286
1287 STATIC_OVL void
complain_about_duplicate(opts,iscompound)1288 complain_about_duplicate(opts, iscompound)
1289 const char *opts;
1290 int iscompound; /* 0 == boolean option, 1 == compound */
1291 {
1292 #ifdef MAC
1293 /* the Mac has trouble dealing with the output of messages while
1294 * processing the config file. That should get fixed one day.
1295 * For now just return.
1296 */
1297 #else /* !MAC */
1298 config_error_add("%s option specified multiple times: %s",
1299 iscompound ? "compound" : "boolean", opts);
1300 #endif /* ?MAC */
1301 return;
1302 }
1303
1304 /* paranoia[] - used by parseoptions() and special_handling() */
1305 STATIC_VAR const struct paranoia_opts {
1306 int flagmask; /* which paranoid option */
1307 const char *argname; /* primary name */
1308 int argMinLen; /* minimum number of letters to match */
1309 const char *synonym; /* alternate name (optional) */
1310 int synMinLen;
1311 const char *explain; /* for interactive menu */
1312 } paranoia[] = {
1313 /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack"
1314 takes precedence and "all" isn't present in the interactive menu,
1315 and "d"ie vs "d"eath, synonyms for each other so doesn't matter;
1316 (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia"
1317 is just a synonym for "Confirm"); "b"ones vs "br"eak-wand, the
1318 latter requires at least two letters; "e"at vs "ex"plore,
1319 "cont"inue eating vs "C"onfirm; "wand"-break vs "Were"-change,
1320 both require at least two letters during config processing and use
1321 case-senstivity for 'O's interactive menu */
1322 { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2,
1323 "for \"yes\" confirmations, require \"no\" to reject" },
1324 { PARANOID_QUIT, "quit", 1, "explore", 2,
1325 "yes vs y to quit or to enter explore mode" },
1326 { PARANOID_DIE, "die", 1, "death", 2,
1327 "yes vs y to die (explore mode or debug mode)" },
1328 { PARANOID_BONES, "bones", 1, 0, 0,
1329 "yes vs y to save bones data when dying in debug mode" },
1330 { PARANOID_HIT, "attack", 1, "hit", 1,
1331 "yes vs y to attack a peaceful monster" },
1332 { PARANOID_BREAKWAND, "wand-break", 2, "break-wand", 2,
1333 "yes vs y to break a wand via (a)pply" },
1334 { PARANOID_EATING, "eat", 1, "continue", 4,
1335 "yes vs y to continue eating after first bite when satiated" },
1336 { PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0,
1337 "yes vs y to change form when lycanthropy is controllable" },
1338 { PARANOID_PRAY, "pray", 1, 0, 0,
1339 "y to pray (supersedes old \"prayconfirm\" option)" },
1340 { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1,
1341 "always pick from inventory for Remove and Takeoff" },
1342 /* for config file parsing; interactive menu skips these */
1343 { 0, "none", 4, 0, 0, 0 }, /* require full word match */
1344 { ~0, "all", 3, 0, 0, 0 }, /* ditto */
1345 };
1346
1347 extern struct menucoloring *menu_colorings;
1348
1349 static const struct {
1350 const char *name;
1351 const int color;
1352 } colornames[] = {
1353 { "black", CLR_BLACK },
1354 { "red", CLR_RED },
1355 { "green", CLR_GREEN },
1356 { "brown", CLR_BROWN },
1357 { "blue", CLR_BLUE },
1358 { "magenta", CLR_MAGENTA },
1359 { "cyan", CLR_CYAN },
1360 { "gray", CLR_GRAY },
1361 { "orange", CLR_ORANGE },
1362 { "light green", CLR_BRIGHT_GREEN },
1363 { "yellow", CLR_YELLOW },
1364 { "light blue", CLR_BRIGHT_BLUE },
1365 { "light magenta", CLR_BRIGHT_MAGENTA },
1366 { "light cyan", CLR_BRIGHT_CYAN },
1367 { "white", CLR_WHITE },
1368 { "no color", NO_COLOR },
1369 { NULL, CLR_BLACK }, /* everything after this is an alias */
1370 { "transparent", NO_COLOR },
1371 { "purple", CLR_MAGENTA },
1372 { "light purple", CLR_BRIGHT_MAGENTA },
1373 { "bright purple", CLR_BRIGHT_MAGENTA },
1374 { "grey", CLR_GRAY },
1375 { "bright red", CLR_ORANGE },
1376 { "bright green", CLR_BRIGHT_GREEN },
1377 { "bright blue", CLR_BRIGHT_BLUE },
1378 { "bright magenta", CLR_BRIGHT_MAGENTA },
1379 { "bright cyan", CLR_BRIGHT_CYAN }
1380 };
1381
1382 static const struct {
1383 const char *name;
1384 const int attr;
1385 } attrnames[] = {
1386 { "none", ATR_NONE },
1387 { "bold", ATR_BOLD },
1388 { "dim", ATR_DIM },
1389 { "underline", ATR_ULINE },
1390 { "blink", ATR_BLINK },
1391 { "inverse", ATR_INVERSE },
1392 { NULL, ATR_NONE }, /* everything after this is an alias */
1393 { "normal", ATR_NONE },
1394 { "uline", ATR_ULINE },
1395 { "reverse", ATR_INVERSE },
1396 };
1397
1398 const char *
clr2colorname(clr)1399 clr2colorname(clr)
1400 int clr;
1401 {
1402 int i;
1403
1404 for (i = 0; i < SIZE(colornames); i++)
1405 if (colornames[i].name && colornames[i].color == clr)
1406 return colornames[i].name;
1407 return (char *) 0;
1408 }
1409
1410 int
match_str2clr(str)1411 match_str2clr(str)
1412 char *str;
1413 {
1414 int i, c = CLR_MAX;
1415
1416 /* allow "lightblue", "light blue", and "light-blue" to match "light blue"
1417 (also junk like "_l i-gh_t---b l u e" but we won't worry about that);
1418 also copes with trailing space; caller has removed any leading space */
1419 for (i = 0; i < SIZE(colornames); i++)
1420 if (colornames[i].name
1421 && fuzzymatch(str, colornames[i].name, " -_", TRUE)) {
1422 c = colornames[i].color;
1423 break;
1424 }
1425 if (i == SIZE(colornames) && digit(*str))
1426 c = atoi(str);
1427
1428 if (c < 0 || c >= CLR_MAX) {
1429 config_error_add("Unknown color '%.60s'", str);
1430 c = CLR_MAX; /* "none of the above" */
1431 }
1432
1433 return c;
1434 }
1435
1436 STATIC_OVL const char *
attr2attrname(attr)1437 attr2attrname(attr)
1438 int attr;
1439 {
1440 int i;
1441
1442 for (i = 0; i < SIZE(attrnames); i++)
1443 if (attrnames[i].attr == attr)
1444 return attrnames[i].name;
1445 return (char *) 0;
1446 }
1447
1448 int
match_str2attr(str,complain)1449 match_str2attr(str, complain)
1450 const char *str;
1451 boolean complain;
1452 {
1453 int i, a = -1;
1454
1455 for (i = 0; i < SIZE(attrnames); i++)
1456 if (attrnames[i].name
1457 && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) {
1458 a = attrnames[i].attr;
1459 break;
1460 }
1461
1462 if (a == -1 && complain)
1463 config_error_add("Unknown text attribute '%.50s'", str);
1464
1465 return a;
1466 }
1467
1468 int
query_color(prompt)1469 query_color(prompt)
1470 const char *prompt;
1471 {
1472 winid tmpwin;
1473 anything any;
1474 int i, pick_cnt;
1475 menu_item *picks = (menu_item *) 0;
1476
1477 tmpwin = create_nhwindow(NHW_MENU);
1478 start_menu(tmpwin);
1479 any = zeroany;
1480 for (i = 0; i < SIZE(colornames); i++) {
1481 if (!colornames[i].name)
1482 break;
1483 any.a_int = i + 1;
1484 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, colornames[i].name,
1485 (colornames[i].color == NO_COLOR) ? MENU_SELECTED
1486 : MENU_UNSELECTED);
1487 }
1488 end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color");
1489 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1490 destroy_nhwindow(tmpwin);
1491 if (pick_cnt > 0) {
1492 i = colornames[picks[0].item.a_int - 1].color;
1493 /* pick_cnt==2: explicitly picked something other than the
1494 preselected entry */
1495 if (pick_cnt == 2 && i == NO_COLOR)
1496 i = colornames[picks[1].item.a_int - 1].color;
1497 free((genericptr_t) picks);
1498 return i;
1499 } else if (pick_cnt == 0) {
1500 /* pick_cnt==0: explicitly picking preselected entry toggled it off */
1501 return NO_COLOR;
1502 }
1503 return -1;
1504 }
1505
1506 /* ask about highlighting attribute; for menu headers and menu
1507 coloring patterns, only one attribute at a time is allowed;
1508 for status highlighting, multiple attributes are allowed [overkill;
1509 life would be much simpler if that were restricted to one also...] */
1510 int
query_attr(prompt)1511 query_attr(prompt)
1512 const char *prompt;
1513 {
1514 winid tmpwin;
1515 anything any;
1516 int i, pick_cnt;
1517 menu_item *picks = (menu_item *) 0;
1518 boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6));
1519 int default_attr = ATR_NONE;
1520
1521 if (prompt && strstri(prompt, "menu headings"))
1522 default_attr = iflags.menu_headings;
1523 tmpwin = create_nhwindow(NHW_MENU);
1524 start_menu(tmpwin);
1525 any = zeroany;
1526 for (i = 0; i < SIZE(attrnames); i++) {
1527 if (!attrnames[i].name)
1528 break;
1529 any.a_int = i + 1;
1530 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, attrnames[i].attr,
1531 attrnames[i].name,
1532 (attrnames[i].attr == default_attr) ? MENU_SELECTED
1533 : MENU_UNSELECTED);
1534 }
1535 end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute");
1536 pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks);
1537 destroy_nhwindow(tmpwin);
1538 if (pick_cnt > 0) {
1539 int j, k = 0;
1540
1541 if (allow_many) {
1542 /* PICK_ANY, with one preselected entry (ATR_NONE) which
1543 should be excluded if any other choices were picked */
1544 for (i = 0; i < pick_cnt; ++i) {
1545 j = picks[i].item.a_int - 1;
1546 if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) {
1547 switch (attrnames[j].attr) {
1548 case ATR_DIM:
1549 k |= HL_DIM;
1550 break;
1551 case ATR_BLINK:
1552 k |= HL_BLINK;
1553 break;
1554 case ATR_ULINE:
1555 k |= HL_ULINE;
1556 break;
1557 case ATR_INVERSE:
1558 k |= HL_INVERSE;
1559 break;
1560 case ATR_BOLD:
1561 k |= HL_BOLD;
1562 break;
1563 case ATR_NONE:
1564 k = HL_NONE;
1565 break;
1566 }
1567 }
1568 }
1569 } else {
1570 /* PICK_ONE, but might get 0 or 2 due to preselected entry */
1571 j = picks[0].item.a_int - 1;
1572 /* pick_cnt==2: explicitly picked something other than the
1573 preselected entry */
1574 if (pick_cnt == 2 && attrnames[j].attr == default_attr)
1575 j = picks[1].item.a_int - 1;
1576 k = attrnames[j].attr;
1577 }
1578 free((genericptr_t) picks);
1579 return k;
1580 } else if (pick_cnt == 0 && !allow_many) {
1581 /* PICK_ONE, preselected entry explicitly chosen */
1582 return default_attr;
1583 }
1584 /* either ESC to explicitly cancel (pick_cnt==-1) or
1585 PICK_ANY with preselected entry toggled off and nothing chosen */
1586 return -1;
1587 }
1588
1589 static const struct {
1590 const char *name;
1591 xchar msgtyp;
1592 const char *descr;
1593 } msgtype_names[] = {
1594 { "show", MSGTYP_NORMAL, "Show message normally" },
1595 { "hide", MSGTYP_NOSHOW, "Hide message" },
1596 { "noshow", MSGTYP_NOSHOW, NULL },
1597 { "stop", MSGTYP_STOP, "Prompt for more after the message" },
1598 { "more", MSGTYP_STOP, NULL },
1599 { "norep", MSGTYP_NOREP, "Do not repeat the message" }
1600 };
1601
1602 STATIC_OVL const char *
msgtype2name(typ)1603 msgtype2name(typ)
1604 int typ;
1605 {
1606 int i;
1607
1608 for (i = 0; i < SIZE(msgtype_names); i++)
1609 if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
1610 return msgtype_names[i].name;
1611 return (char *) 0;
1612 }
1613
1614 STATIC_OVL int
query_msgtype()1615 query_msgtype()
1616 {
1617 winid tmpwin;
1618 anything any;
1619 int i, pick_cnt;
1620 menu_item *picks = (menu_item *) 0;
1621
1622 tmpwin = create_nhwindow(NHW_MENU);
1623 start_menu(tmpwin);
1624 any = zeroany;
1625 for (i = 0; i < SIZE(msgtype_names); i++)
1626 if (msgtype_names[i].descr) {
1627 any.a_int = msgtype_names[i].msgtyp + 1;
1628 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1629 msgtype_names[i].descr, MENU_UNSELECTED);
1630 }
1631 end_menu(tmpwin, "How to show the message");
1632 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
1633 destroy_nhwindow(tmpwin);
1634 if (pick_cnt > 0) {
1635 i = picks->item.a_int - 1;
1636 free((genericptr_t) picks);
1637 return i;
1638 }
1639 return -1;
1640 }
1641
1642 STATIC_OVL boolean
msgtype_add(typ,pattern)1643 msgtype_add(typ, pattern)
1644 int typ;
1645 char *pattern;
1646 {
1647 struct plinemsg_type *tmp = (struct plinemsg_type *) alloc(sizeof *tmp);
1648
1649 tmp->msgtype = typ;
1650 tmp->regex = regex_init();
1651 if (!regex_compile(pattern, tmp->regex)) {
1652 static const char *re_error = "MSGTYPE regex error";
1653
1654 config_error_add("%s: %s", re_error, regex_error_desc(tmp->regex));
1655 regex_free(tmp->regex);
1656 free((genericptr_t) tmp);
1657 return FALSE;
1658 }
1659 tmp->pattern = dupstr(pattern);
1660 tmp->next = plinemsg_types;
1661 plinemsg_types = tmp;
1662 return TRUE;
1663 }
1664
1665 void
msgtype_free()1666 msgtype_free()
1667 {
1668 struct plinemsg_type *tmp, *tmp2 = 0;
1669
1670 for (tmp = plinemsg_types; tmp; tmp = tmp2) {
1671 tmp2 = tmp->next;
1672 free((genericptr_t) tmp->pattern);
1673 regex_free(tmp->regex);
1674 free((genericptr_t) tmp);
1675 }
1676 plinemsg_types = (struct plinemsg_type *) 0;
1677 }
1678
1679 STATIC_OVL void
free_one_msgtype(idx)1680 free_one_msgtype(idx)
1681 int idx; /* 0 .. */
1682 {
1683 struct plinemsg_type *tmp = plinemsg_types;
1684 struct plinemsg_type *prev = NULL;
1685
1686 while (tmp) {
1687 if (idx == 0) {
1688 struct plinemsg_type *next = tmp->next;
1689
1690 regex_free(tmp->regex);
1691 free((genericptr_t) tmp->pattern);
1692 free((genericptr_t) tmp);
1693 if (prev)
1694 prev->next = next;
1695 else
1696 plinemsg_types = next;
1697 return;
1698 }
1699 idx--;
1700 prev = tmp;
1701 tmp = tmp->next;
1702 }
1703 }
1704
1705 int
msgtype_type(msg,norepeat)1706 msgtype_type(msg, norepeat)
1707 const char *msg;
1708 boolean norepeat; /* called from Norep(via pline) */
1709 {
1710 struct plinemsg_type *tmp = plinemsg_types;
1711
1712 while (tmp) {
1713 /* we don't exclude entries with negative msgtype values
1714 because then the msg might end up matching a later pattern */
1715 if (regex_match(msg, tmp->regex))
1716 return tmp->msgtype;
1717 tmp = tmp->next;
1718 }
1719 return norepeat ? MSGTYP_NOREP : MSGTYP_NORMAL;
1720 }
1721
1722 /* negate one or more types of messages so that their type handling will
1723 be disabled or re-enabled; MSGTYPE_NORMAL (value 0) is not affected */
1724 void
hide_unhide_msgtypes(hide,hide_mask)1725 hide_unhide_msgtypes(hide, hide_mask)
1726 boolean hide;
1727 int hide_mask;
1728 {
1729 struct plinemsg_type *tmp;
1730 int mt;
1731
1732 /* negative msgtype value won't be recognized by pline, so does nothing */
1733 for (tmp = plinemsg_types; tmp; tmp = tmp->next) {
1734 mt = tmp->msgtype;
1735 if (!hide)
1736 mt = -mt; /* unhide: negate negative, yielding positive */
1737 if (mt > 0 && ((1 << mt) & hide_mask))
1738 tmp->msgtype = -tmp->msgtype;
1739 }
1740 }
1741
1742 STATIC_OVL int
msgtype_count(VOID_ARGS)1743 msgtype_count(VOID_ARGS)
1744 {
1745 int c = 0;
1746 struct plinemsg_type *tmp = plinemsg_types;
1747
1748 while (tmp) {
1749 c++;
1750 tmp = tmp->next;
1751 }
1752 return c;
1753 }
1754
1755 boolean
msgtype_parse_add(str)1756 msgtype_parse_add(str)
1757 char *str;
1758 {
1759 char pattern[256];
1760 char msgtype[11];
1761
1762 if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
1763 int typ = -1;
1764 int i;
1765
1766 for (i = 0; i < SIZE(msgtype_names); i++)
1767 if (!strncmpi(msgtype_names[i].name, msgtype, strlen(msgtype))) {
1768 typ = msgtype_names[i].msgtyp;
1769 break;
1770 }
1771 if (typ != -1)
1772 return msgtype_add(typ, pattern);
1773 else
1774 config_error_add("Unknown message type '%s'", msgtype);
1775 } else {
1776 config_error_add("Malformed MSGTYPE");
1777 }
1778 return FALSE;
1779 }
1780
1781 STATIC_OVL boolean
test_regex_pattern(str,errmsg)1782 test_regex_pattern(str, errmsg)
1783 const char *str;
1784 const char *errmsg;
1785 {
1786 static const char re_error[] = "Regex error";
1787 struct nhregex *match;
1788 boolean retval = TRUE;
1789
1790 if (!str)
1791 return FALSE;
1792
1793 match = regex_init();
1794 if (!match) {
1795 config_error_add("NHregex error");
1796 return FALSE;
1797 }
1798
1799 if (!regex_compile(str, match)) {
1800 config_error_add("%s: %s", errmsg ? errmsg : re_error,
1801 regex_error_desc(match));
1802 retval = FALSE;
1803 }
1804 regex_free(match);
1805 return retval;
1806 }
1807
1808 STATIC_OVL boolean
add_menu_coloring_parsed(str,c,a)1809 add_menu_coloring_parsed(str, c, a)
1810 char *str;
1811 int c, a;
1812 {
1813 static const char re_error[] = "Menucolor regex error";
1814 struct menucoloring *tmp;
1815
1816 if (!str)
1817 return FALSE;
1818 tmp = (struct menucoloring *) alloc(sizeof *tmp);
1819 tmp->match = regex_init();
1820 if (!regex_compile(str, tmp->match)) {
1821 config_error_add("%s: %s", re_error, regex_error_desc(tmp->match));
1822 regex_free(tmp->match);
1823 free(tmp);
1824 return FALSE;
1825 } else {
1826 tmp->next = menu_colorings;
1827 tmp->origstr = dupstr(str);
1828 tmp->color = c;
1829 tmp->attr = a;
1830 menu_colorings = tmp;
1831 return TRUE;
1832 }
1833 }
1834
1835 /* parse '"regex_string"=color&attr' and add it to menucoloring */
1836 boolean
add_menu_coloring(tmpstr)1837 add_menu_coloring(tmpstr)
1838 char *tmpstr; /* never Null but could be empty */
1839 {
1840 int c = NO_COLOR, a = ATR_NONE;
1841 char *tmps, *cs, *amp;
1842 char str[BUFSZ];
1843
1844 (void) strncpy(str, tmpstr, sizeof str - 1);
1845 str[sizeof str - 1] = '\0';
1846
1847 if ((cs = index(str, '=')) == 0) {
1848 config_error_add("Malformed MENUCOLOR");
1849 return FALSE;
1850 }
1851
1852 tmps = cs + 1; /* advance past '=' */
1853 mungspaces(tmps);
1854 if ((amp = index(tmps, '&')) != 0)
1855 *amp = '\0';
1856
1857 c = match_str2clr(tmps);
1858 if (c >= CLR_MAX)
1859 return FALSE;
1860
1861 if (amp) {
1862 tmps = amp + 1; /* advance past '&' */
1863 a = match_str2attr(tmps, TRUE);
1864 if (a == -1)
1865 return FALSE;
1866 }
1867
1868 /* the regexp portion here has not been condensed by mungspaces() */
1869 *cs = '\0';
1870 tmps = str;
1871 if (*tmps == '"' || *tmps == '\'') {
1872 cs--;
1873 while (isspace((uchar) *cs))
1874 cs--;
1875 if (*cs == *tmps) {
1876 *cs = '\0';
1877 tmps++;
1878 }
1879 }
1880 return add_menu_coloring_parsed(tmps, c, a);
1881 }
1882
1883 boolean
get_menu_coloring(str,color,attr)1884 get_menu_coloring(str, color, attr)
1885 const char *str;
1886 int *color, *attr;
1887 {
1888 struct menucoloring *tmpmc;
1889
1890 if (iflags.use_menu_color)
1891 for (tmpmc = menu_colorings; tmpmc; tmpmc = tmpmc->next)
1892 if (regex_match(str, tmpmc->match)) {
1893 *color = tmpmc->color;
1894 *attr = tmpmc->attr;
1895 return TRUE;
1896 }
1897 return FALSE;
1898 }
1899
1900 void
free_menu_coloring()1901 free_menu_coloring()
1902 {
1903 struct menucoloring *tmp, *tmp2;
1904
1905 for (tmp = menu_colorings; tmp; tmp = tmp2) {
1906 tmp2 = tmp->next;
1907 regex_free(tmp->match);
1908 free((genericptr_t) tmp->origstr);
1909 free((genericptr_t) tmp);
1910 }
1911 }
1912
1913 STATIC_OVL void
free_one_menu_coloring(idx)1914 free_one_menu_coloring(idx)
1915 int idx; /* 0 .. */
1916 {
1917 struct menucoloring *tmp = menu_colorings;
1918 struct menucoloring *prev = NULL;
1919
1920 while (tmp) {
1921 if (idx == 0) {
1922 struct menucoloring *next = tmp->next;
1923
1924 regex_free(tmp->match);
1925 free((genericptr_t) tmp->origstr);
1926 free((genericptr_t) tmp);
1927 if (prev)
1928 prev->next = next;
1929 else
1930 menu_colorings = next;
1931 return;
1932 }
1933 idx--;
1934 prev = tmp;
1935 tmp = tmp->next;
1936 }
1937 }
1938
1939 STATIC_OVL int
count_menucolors(VOID_ARGS)1940 count_menucolors(VOID_ARGS)
1941 {
1942 struct menucoloring *tmp;
1943 int count = 0;
1944
1945 for (tmp = menu_colorings; tmp; tmp = tmp->next)
1946 count++;
1947 return count;
1948 }
1949
1950 STATIC_OVL boolean
parse_role_opts(negated,fullname,opts,opp)1951 parse_role_opts(negated, fullname, opts, opp)
1952 boolean negated;
1953 const char *fullname;
1954 char *opts;
1955 char **opp;
1956 {
1957 char *op = *opp;
1958
1959 if (negated) {
1960 bad_negation(fullname, FALSE);
1961 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
1962 != empty_optstr) {
1963 boolean val_negated = FALSE;
1964
1965 while ((*op == '!') || !strncmpi(op, "no", 2)) {
1966 if (*op == '!')
1967 op++;
1968 else
1969 op += 2;
1970 val_negated = !val_negated;
1971 }
1972 if (val_negated) {
1973 if (!setrolefilter(op)) {
1974 config_error_add("Unknown negated parameter '%s'", op);
1975 return FALSE;
1976 }
1977 } else {
1978 if (duplicate_opt_detection(opts, 1))
1979 complain_about_duplicate(opts, 1);
1980 *opp = op;
1981 return TRUE;
1982 }
1983 }
1984 return FALSE;
1985 }
1986
1987 /* Check if character c is illegal as a menu command key */
1988 boolean
illegal_menu_cmd_key(c)1989 illegal_menu_cmd_key(c)
1990 char c;
1991 {
1992 if (c == 0 || c == '\r' || c == '\n' || c == '\033'
1993 || c == ' ' || digit(c) || (letter(c) && c != '@')) {
1994 config_error_add("Reserved menu command key '%s'", visctrl(c));
1995 return TRUE;
1996 } else { /* reject default object class symbols */
1997 int j;
1998 for (j = 1; j < MAXOCLASSES; j++)
1999 if (c == def_oc_syms[j].sym) {
2000 config_error_add("Menu command key '%s' is an object class",
2001 visctrl(c));
2002 return TRUE;
2003 }
2004 }
2005 return FALSE;
2006 }
2007
2008 boolean
parseoptions(opts,tinitial,tfrom_file)2009 parseoptions(opts, tinitial, tfrom_file)
2010 register char *opts;
2011 boolean tinitial, tfrom_file;
2012 {
2013 char *op;
2014 unsigned num;
2015 boolean negated, duplicate;
2016 int i;
2017 const char *fullname;
2018 boolean retval = TRUE;
2019
2020 initial = tinitial;
2021 from_file = tfrom_file;
2022 if ((op = index(opts, ',')) != 0) {
2023 *op++ = 0;
2024 if (!parseoptions(op, initial, from_file))
2025 retval = FALSE;
2026 }
2027 if (strlen(opts) > BUFSZ / 2) {
2028 config_error_add("Option too long, max length is %i characters",
2029 (BUFSZ / 2));
2030 return FALSE;
2031 }
2032
2033 /* strip leading and trailing white space */
2034 while (isspace((uchar) *opts))
2035 opts++;
2036 op = eos(opts);
2037 while (--op >= opts && isspace((uchar) *op))
2038 *op = '\0';
2039
2040 if (!*opts) {
2041 config_error_add("Empty statement");
2042 return FALSE;
2043 }
2044 negated = FALSE;
2045 while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
2046 if (*opts == '!')
2047 opts++;
2048 else
2049 opts += 2;
2050 negated = !negated;
2051 }
2052
2053 /* variant spelling */
2054
2055 if (match_optname(opts, "colour", 5, FALSE))
2056 Strcpy(opts, "color"); /* fortunately this isn't longer */
2057
2058 /* special boolean options */
2059
2060 if (match_optname(opts, "female", 3, FALSE)) {
2061 if (duplicate_opt_detection(opts, 0))
2062 complain_about_duplicate(opts, 0);
2063 if (!initial && flags.female == negated) {
2064 config_error_add("That is not anatomically possible.");
2065 return FALSE;
2066 } else
2067 flags.initgend = flags.female = !negated;
2068 return retval;
2069 }
2070
2071 if (match_optname(opts, "male", 4, FALSE)) {
2072 if (duplicate_opt_detection(opts, 0))
2073 complain_about_duplicate(opts, 0);
2074 if (!initial && flags.female != negated) {
2075 config_error_add("That is not anatomically possible.");
2076 return FALSE;
2077 } else
2078 flags.initgend = flags.female = negated;
2079 return retval;
2080 }
2081
2082 #if defined(MICRO) && !defined(AMIGA)
2083 /* included for compatibility with old NetHack.cnf files */
2084 if (match_optname(opts, "IBM_", 4, FALSE)) {
2085 iflags.BIOS = !negated;
2086 return retval;
2087 }
2088 #endif /* MICRO */
2089
2090 /* compound options */
2091
2092 /* This first batch can be duplicated if their values are negated */
2093
2094 /* align:string */
2095 fullname = "align";
2096 if (match_optname(opts, fullname, sizeof "align" - 1, TRUE)) {
2097 if (parse_role_opts(negated, fullname, opts, &op)) {
2098 if ((flags.initalign = str2align(op)) == ROLE_NONE) {
2099 config_error_add("Unknown %s '%s'", fullname, op);
2100 return FALSE;
2101 }
2102 } else
2103 return FALSE;
2104 return retval;
2105 }
2106
2107 /* role:string or character:string */
2108 fullname = "role";
2109 if (match_optname(opts, fullname, 4, TRUE)
2110 || match_optname(opts, (fullname = "character"), 4, TRUE)) {
2111 if (parse_role_opts(negated, fullname, opts, &op)) {
2112 if ((flags.initrole = str2role(op)) == ROLE_NONE) {
2113 config_error_add("Unknown %s '%s'", fullname, op);
2114 return FALSE;
2115 } else /* Backwards compatibility */
2116 nmcpy(pl_character, op, PL_NSIZ);
2117 } else
2118 return FALSE;
2119 return retval;
2120 }
2121
2122 /* race:string */
2123 fullname = "race";
2124 if (match_optname(opts, fullname, 4, TRUE)) {
2125 if (parse_role_opts(negated, fullname, opts, &op)) {
2126 if ((flags.initrace = str2race(op)) == ROLE_NONE) {
2127 config_error_add("Unknown %s '%s'", fullname, op);
2128 return FALSE;
2129 } else /* Backwards compatibility */
2130 pl_race = *op;
2131 } else
2132 return FALSE;
2133 return retval;
2134 }
2135
2136 /* gender:string */
2137 fullname = "gender";
2138 if (match_optname(opts, fullname, 4, TRUE)) {
2139 if (parse_role_opts(negated, fullname, opts, &op)) {
2140 if ((flags.initgend = str2gend(op)) == ROLE_NONE) {
2141 config_error_add("Unknown %s '%s'", fullname, op);
2142 return FALSE;
2143 } else
2144 flags.female = flags.initgend;
2145 } else
2146 return FALSE;
2147 return retval;
2148 }
2149
2150 /* We always check for duplicates on the remaining compound options,
2151 although individual option processing can choose to complain or not */
2152
2153 duplicate = duplicate_opt_detection(opts, 1); /* 1: check compounds */
2154
2155 fullname = "pettype";
2156 if (match_optname(opts, fullname, 3, TRUE)) {
2157 if (duplicate)
2158 complain_about_duplicate(opts, 1);
2159 if ((op = string_for_env_opt(fullname, opts, negated))
2160 != empty_optstr) {
2161 if (negated) {
2162 bad_negation(fullname, TRUE);
2163 return FALSE;
2164 } else
2165 switch (lowc(*op)) {
2166 case 'd': /* dog */
2167 preferred_pet = 'd';
2168 break;
2169 case 'c': /* cat */
2170 case 'f': /* feline */
2171 preferred_pet = 'c';
2172 break;
2173 case 'h': /* horse */
2174 case 'q': /* quadruped */
2175 /* avoids giving "unrecognized type of pet" but
2176 pet_type(dog.c) won't actually honor this */
2177 preferred_pet = 'h';
2178 break;
2179 case 'n': /* no pet */
2180 preferred_pet = 'n';
2181 break;
2182 case '*': /* random */
2183 preferred_pet = '\0';
2184 break;
2185 default:
2186 config_error_add("Unrecognized pet type '%s'.", op);
2187 return FALSE;
2188 break;
2189 }
2190 } else if (negated)
2191 preferred_pet = 'n';
2192 return retval;
2193 }
2194
2195 fullname = "catname";
2196 if (match_optname(opts, fullname, 3, TRUE)) {
2197 if (duplicate)
2198 complain_about_duplicate(opts, 1);
2199 if (negated) {
2200 bad_negation(fullname, FALSE);
2201 return FALSE;
2202 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2203 != empty_optstr) {
2204 nmcpy(catname, op, PL_PSIZ);
2205 } else
2206 return FALSE;
2207 sanitize_name(catname);
2208 return retval;
2209 }
2210
2211 fullname = "dogname";
2212 if (match_optname(opts, fullname, 3, TRUE)) {
2213 if (duplicate)
2214 complain_about_duplicate(opts, 1);
2215 if (negated) {
2216 bad_negation(fullname, FALSE);
2217 return FALSE;
2218 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2219 != empty_optstr) {
2220 nmcpy(dogname, op, PL_PSIZ);
2221 } else
2222 return FALSE;
2223 sanitize_name(dogname);
2224 return retval;
2225 }
2226
2227 fullname = "horsename";
2228 if (match_optname(opts, fullname, 5, TRUE)) {
2229 if (duplicate)
2230 complain_about_duplicate(opts, 1);
2231 if (negated) {
2232 bad_negation(fullname, FALSE);
2233 return FALSE;
2234 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2235 != empty_optstr) {
2236 nmcpy(horsename, op, PL_PSIZ);
2237 } else
2238 return FALSE;
2239 sanitize_name(horsename);
2240 return retval;
2241 }
2242
2243 fullname = "mouse_support";
2244 if (match_optname(opts, fullname, 13, TRUE)) {
2245 boolean compat = (strlen(opts) <= 13);
2246
2247 if (duplicate)
2248 complain_about_duplicate(opts, 1);
2249 op = string_for_opt(opts, (compat || !initial));
2250 if (op == empty_optstr) {
2251 if (compat || negated || initial) {
2252 /* for backwards compatibility, "mouse_support" without a
2253 value is a synonym for mouse_support:1 */
2254 iflags.wc_mouse_support = !negated;
2255 }
2256 } else if (negated) {
2257 bad_negation(fullname, TRUE);
2258 return FALSE;
2259 } else {
2260 int mode = atoi(op);
2261
2262 if (mode < 0 || mode > 2 || (mode == 0 && *op != '0')) {
2263 config_error_add("Illegal %s parameter '%s'", fullname, op);
2264 return FALSE;
2265 } else { /* mode >= 0 */
2266 iflags.wc_mouse_support = mode;
2267 }
2268 }
2269 return retval;
2270 }
2271
2272 fullname = "number_pad";
2273 if (match_optname(opts, fullname, 10, TRUE)) {
2274 boolean compat = (strlen(opts) <= 10);
2275
2276 if (duplicate)
2277 complain_about_duplicate(opts, 1);
2278 op = string_for_opt(opts, (compat || !initial));
2279 if (op == empty_optstr) {
2280 if (compat || negated || initial) {
2281 /* for backwards compatibility, "number_pad" without a
2282 value is a synonym for number_pad:1 */
2283 iflags.num_pad = !negated;
2284 iflags.num_pad_mode = 0;
2285 }
2286 } else if (negated) {
2287 bad_negation(fullname, TRUE);
2288 return FALSE;
2289 } else {
2290 int mode = atoi(op);
2291
2292 if (mode < -1 || mode > 4 || (mode == 0 && *op != '0')) {
2293 config_error_add("Illegal %s parameter '%s'", fullname, op);
2294 return FALSE;
2295 } else if (mode <= 0) {
2296 iflags.num_pad = FALSE;
2297 /* German keyboard; y and z keys swapped */
2298 iflags.num_pad_mode = (mode < 0); /* 0 or 1 */
2299 } else { /* mode > 0 */
2300 iflags.num_pad = TRUE;
2301 iflags.num_pad_mode = 0;
2302 /* PC Hack / MSDOS compatibility */
2303 if (mode == 2 || mode == 4)
2304 iflags.num_pad_mode |= 1;
2305 /* phone keypad layout */
2306 if (mode == 3 || mode == 4)
2307 iflags.num_pad_mode |= 2;
2308 }
2309 }
2310 reset_commands(FALSE);
2311 number_pad(iflags.num_pad ? 1 : 0);
2312 return retval;
2313 }
2314
2315 fullname = "roguesymset";
2316 if (match_optname(opts, fullname, 7, TRUE)) {
2317 if (duplicate)
2318 complain_about_duplicate(opts, 1);
2319 if (negated) {
2320 bad_negation(fullname, FALSE);
2321 return FALSE;
2322 } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
2323 symset[ROGUESET].name = dupstr(op);
2324 if (!read_sym_file(ROGUESET)) {
2325 clear_symsetentry(ROGUESET, TRUE);
2326 config_error_add(
2327 "Unable to load symbol set \"%s\" from \"%s\"",
2328 op, SYMBOLS);
2329 return FALSE;
2330 } else {
2331 if (!initial && Is_rogue_level(&u.uz))
2332 assign_graphics(ROGUESET);
2333 need_redraw = TRUE;
2334 }
2335 } else
2336 return FALSE;
2337 return retval;
2338 }
2339
2340 fullname = "symset";
2341 if (match_optname(opts, fullname, 6, TRUE)) {
2342 if (duplicate)
2343 complain_about_duplicate(opts, 1);
2344 if (negated) {
2345 bad_negation(fullname, FALSE);
2346 return FALSE;
2347 } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
2348 symset[PRIMARY].name = dupstr(op);
2349 if (!read_sym_file(PRIMARY)) {
2350 clear_symsetentry(PRIMARY, TRUE);
2351 config_error_add(
2352 "Unable to load symbol set \"%s\" from \"%s\"",
2353 op, SYMBOLS);
2354 return FALSE;
2355 } else {
2356 switch_symbols(symset[PRIMARY].name != (char *) 0);
2357 need_redraw = TRUE;
2358 }
2359 } else
2360 return FALSE;
2361 return retval;
2362 }
2363
2364 fullname = "runmode";
2365 if (match_optname(opts, fullname, 4, TRUE)) {
2366 if (duplicate)
2367 complain_about_duplicate(opts, 1);
2368 if (negated) {
2369 flags.runmode = RUN_TPORT;
2370 } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
2371 if (!strncmpi(op, "teleport", strlen(op)))
2372 flags.runmode = RUN_TPORT;
2373 else if (!strncmpi(op, "run", strlen(op)))
2374 flags.runmode = RUN_LEAP;
2375 else if (!strncmpi(op, "walk", strlen(op)))
2376 flags.runmode = RUN_STEP;
2377 else if (!strncmpi(op, "crawl", strlen(op)))
2378 flags.runmode = RUN_CRAWL;
2379 else {
2380 config_error_add("Unknown %s parameter '%s'", fullname, op);
2381 return FALSE;
2382 }
2383 } else
2384 return FALSE;
2385 return retval;
2386 }
2387
2388 /* menucolor:"regex_string"=color */
2389 fullname = "menucolor";
2390 if (match_optname(opts, fullname, 9, TRUE)) {
2391 if (negated) {
2392 bad_negation(fullname, FALSE);
2393 return FALSE;
2394 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2395 != empty_optstr) {
2396 if (!add_menu_coloring(op))
2397 return FALSE;
2398 } else
2399 return FALSE;
2400 return retval;
2401 }
2402
2403 fullname = "msghistory";
2404 if (match_optname(opts, fullname, 3, TRUE)) {
2405 if (duplicate)
2406 complain_about_duplicate(opts, 1);
2407 op = string_for_env_opt(fullname, opts, negated);
2408 if ((negated && op == empty_optstr)
2409 || (!negated && op != empty_optstr)) {
2410 iflags.msg_history = negated ? 0 : atoi(op);
2411 } else if (negated) {
2412 bad_negation(fullname, TRUE);
2413 return FALSE;
2414 }
2415 return retval;
2416 }
2417
2418 fullname = "msg_window";
2419 /* msg_window:single, combo, full or reversed */
2420 if (match_optname(opts, fullname, 4, TRUE)) {
2421 /* allow option to be silently ignored by non-tty ports */
2422 #ifdef TTY_GRAPHICS
2423 int tmp;
2424
2425 if (duplicate)
2426 complain_about_duplicate(opts, 1);
2427 if ((op = string_for_opt(opts, TRUE)) == empty_optstr) {
2428 tmp = negated ? 's' : 'f';
2429 } else {
2430 if (negated) {
2431 bad_negation(fullname, TRUE);
2432 return FALSE;
2433 }
2434 tmp = lowc(*op);
2435 }
2436 switch (tmp) {
2437 case 's': /* single message history cycle (default if negated) */
2438 iflags.prevmsg_window = 's';
2439 break;
2440 case 'c': /* combination: two singles, then full page */
2441 iflags.prevmsg_window = 'c';
2442 break;
2443 case 'f': /* full page (default if specified without argument) */
2444 iflags.prevmsg_window = 'f';
2445 break;
2446 case 'r': /* full page (reversed) */
2447 iflags.prevmsg_window = 'r';
2448 break;
2449 default:
2450 config_error_add("Unknown %s parameter '%s'", fullname, op);
2451 retval = FALSE;
2452 }
2453 #endif
2454 return retval;
2455 }
2456
2457 /* WINCAP
2458 * setting font options */
2459 fullname = "font";
2460 if (!strncmpi(opts, fullname, 4)) {
2461 int opttype = -1;
2462 char *fontopts = opts + 4;
2463
2464 if (!strncmpi(fontopts, "map", 3) || !strncmpi(fontopts, "_map", 4))
2465 opttype = MAP_OPTION;
2466 else if (!strncmpi(fontopts, "message", 7)
2467 || !strncmpi(fontopts, "_message", 8))
2468 opttype = MESSAGE_OPTION;
2469 else if (!strncmpi(fontopts, "text", 4)
2470 || !strncmpi(fontopts, "_text", 5))
2471 opttype = TEXT_OPTION;
2472 else if (!strncmpi(fontopts, "menu", 4)
2473 || !strncmpi(fontopts, "_menu", 5))
2474 opttype = MENU_OPTION;
2475 else if (!strncmpi(fontopts, "status", 6)
2476 || !strncmpi(fontopts, "_status", 7))
2477 opttype = STATUS_OPTION;
2478 else if (!strncmpi(fontopts, "_size", 5)) {
2479 if (!strncmpi(fontopts, "_size_map", 8))
2480 opttype = MAP_OPTION;
2481 else if (!strncmpi(fontopts, "_size_message", 12))
2482 opttype = MESSAGE_OPTION;
2483 else if (!strncmpi(fontopts, "_size_text", 9))
2484 opttype = TEXT_OPTION;
2485 else if (!strncmpi(fontopts, "_size_menu", 9))
2486 opttype = MENU_OPTION;
2487 else if (!strncmpi(fontopts, "_size_status", 11))
2488 opttype = STATUS_OPTION;
2489 else {
2490 config_error_add("Unknown %s parameter '%s'", fullname, opts);
2491 return FALSE;
2492 }
2493 if (duplicate)
2494 complain_about_duplicate(opts, 1);
2495 if (opttype > 0 && !negated
2496 && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
2497 switch (opttype) {
2498 case MAP_OPTION:
2499 iflags.wc_fontsiz_map = atoi(op);
2500 break;
2501 case MESSAGE_OPTION:
2502 iflags.wc_fontsiz_message = atoi(op);
2503 break;
2504 case TEXT_OPTION:
2505 iflags.wc_fontsiz_text = atoi(op);
2506 break;
2507 case MENU_OPTION:
2508 iflags.wc_fontsiz_menu = atoi(op);
2509 break;
2510 case STATUS_OPTION:
2511 iflags.wc_fontsiz_status = atoi(op);
2512 break;
2513 }
2514 }
2515 return retval;
2516 } else {
2517 config_error_add("Unknown %s parameter '%s'", fullname, opts);
2518 return FALSE;
2519 }
2520 if (opttype > 0
2521 && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
2522 wc_set_font_name(opttype, op);
2523 #ifdef MAC
2524 set_font_name(opttype, op);
2525 #endif
2526 return retval;
2527 } else if (negated) {
2528 bad_negation(fullname, TRUE);
2529 return FALSE;
2530 }
2531 return retval;
2532 }
2533
2534 #ifdef CHANGE_COLOR
2535 if (match_optname(opts, "palette", 3, TRUE)
2536 #ifdef MAC
2537 || match_optname(opts, "hicolor", 3, TRUE)
2538 #endif
2539 ) {
2540 int color_number, color_incr;
2541
2542 #ifndef WIN32
2543 if (duplicate)
2544 complain_about_duplicate(opts, 1);
2545 #endif
2546 #ifdef MAC
2547 if (match_optname(opts, "hicolor", 3, TRUE)) {
2548 if (negated) {
2549 bad_negation("hicolor", FALSE);
2550 return FALSE;
2551 }
2552 color_number = CLR_MAX + 4; /* HARDCODED inverse number */
2553 color_incr = -1;
2554 } else
2555 #endif
2556 {
2557 if (negated) {
2558 bad_negation("palette", FALSE);
2559 return FALSE;
2560 }
2561 color_number = 0;
2562 color_incr = 1;
2563 }
2564 #ifdef WIN32
2565 op = string_for_opt(opts, TRUE);
2566 if (op == empty_optstr || !alternative_palette(op)) {
2567 config_error_add("Error in palette parameter '%s'", op);
2568 return FALSE;
2569 }
2570 #else
2571 if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
2572 char *pt = op;
2573 int cnt, tmp, reverse;
2574 long rgb;
2575
2576 while (*pt && color_number >= 0) {
2577 cnt = 3;
2578 rgb = 0L;
2579 if (*pt == '-') {
2580 reverse = 1;
2581 pt++;
2582 } else {
2583 reverse = 0;
2584 }
2585 while (cnt-- > 0) {
2586 if (*pt && *pt != '/') {
2587 #ifdef AMIGA
2588 rgb <<= 4;
2589 #else
2590 rgb <<= 8;
2591 #endif
2592 tmp = *pt++;
2593 if (isalpha((uchar) tmp)) {
2594 tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */
2595 } else {
2596 tmp &= 0xf; /* Digits in ASCII too... */
2597 }
2598 #ifndef AMIGA
2599 /* Add an extra so we fill f -> ff and 0 -> 00 */
2600 rgb += tmp << 4;
2601 #endif
2602 rgb += tmp;
2603 }
2604 }
2605 if (*pt == '/')
2606 pt++;
2607 change_color(color_number, rgb, reverse);
2608 color_number += color_incr;
2609 }
2610 }
2611 #endif /* !WIN32 */
2612 if (!initial) {
2613 need_redraw = TRUE;
2614 }
2615 return retval;
2616 }
2617 #endif /* CHANGE_COLOR */
2618
2619 if (match_optname(opts, "fruit", 2, TRUE)) {
2620 struct fruit *forig = 0;
2621
2622 if (duplicate)
2623 complain_about_duplicate(opts, 1);
2624 op = string_for_opt(opts, negated || !initial);
2625 if (negated) {
2626 if (op != empty_optstr) {
2627 bad_negation("fruit", TRUE);
2628 return FALSE;
2629 }
2630 op = empty_optstr;
2631 goto goodfruit;
2632 }
2633 if (op == empty_optstr)
2634 return FALSE;
2635 /* strip leading/trailing spaces, condense internal ones (3.6.2) */
2636 mungspaces(op);
2637 if (!initial) {
2638 struct fruit *f;
2639 int fnum = 0;
2640
2641 /* count number of named fruits; if 'op' is found among them,
2642 then the count doesn't matter because we won't be adding it */
2643 f = fruit_from_name(op, FALSE, &fnum);
2644 if (!f) {
2645 if (!flags.made_fruit)
2646 forig = fruit_from_name(pl_fruit, FALSE, (int *) 0);
2647
2648 if (!forig && fnum >= 100) {
2649 config_error_add(
2650 "Doing that so many times isn't very fruitful.");
2651 return retval;
2652 }
2653 }
2654 }
2655 goodfruit:
2656 nmcpy(pl_fruit, op, PL_FSIZ);
2657 sanitize_name(pl_fruit);
2658 /* OBJ_NAME(objects[SLIME_MOLD]) won't work for this after
2659 initialization; it gets changed to generic "fruit" */
2660 if (!*pl_fruit)
2661 nmcpy(pl_fruit, "slime mold", PL_FSIZ);
2662 if (!initial) {
2663 /* if 'forig' is nonNull, we replace it rather than add
2664 a new fruit; it can only be nonNull if no fruits have
2665 been created since the previous name was put in place */
2666 (void) fruitadd(pl_fruit, forig);
2667 pline("Fruit is now \"%s\".", pl_fruit);
2668 }
2669 /* If initial, then initoptions is allowed to do it instead
2670 * of here (initoptions always has to do it even if there's
2671 * no fruit option at all. Also, we don't want people
2672 * setting multiple fruits in their options.)
2673 */
2674 return retval;
2675 }
2676
2677 fullname = "whatis_coord";
2678 if (match_optname(opts, fullname, 8, TRUE)) {
2679 if (duplicate)
2680 complain_about_duplicate(opts, 1);
2681 if (negated) {
2682 iflags.getpos_coords = GPCOORDS_NONE;
2683 return retval;
2684 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2685 != empty_optstr) {
2686 static char gpcoords[] = { GPCOORDS_NONE, GPCOORDS_COMPASS,
2687 GPCOORDS_COMFULL, GPCOORDS_MAP,
2688 GPCOORDS_SCREEN, '\0' };
2689 char c = lowc(*op);
2690
2691 if (c && index(gpcoords, c))
2692 iflags.getpos_coords = c;
2693 else {
2694 config_error_add("Unknown %s parameter '%s'", fullname, op);
2695 return FALSE;
2696 }
2697 } else
2698 return FALSE;
2699 return retval;
2700 }
2701
2702 fullname = "whatis_filter";
2703 if (match_optname(opts, fullname, 8, TRUE)) {
2704 if (duplicate)
2705 complain_about_duplicate(opts, 1);
2706 if (negated) {
2707 iflags.getloc_filter = GFILTER_NONE;
2708 return retval;
2709 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2710 != empty_optstr) {
2711 char c = lowc(*op);
2712
2713 switch (c) {
2714 case 'n':
2715 iflags.getloc_filter = GFILTER_NONE;
2716 break;
2717 case 'v':
2718 iflags.getloc_filter = GFILTER_VIEW;
2719 break;
2720 case 'a':
2721 iflags.getloc_filter = GFILTER_AREA;
2722 break;
2723 default: {
2724 config_error_add("Unknown %s parameter '%s'", fullname, op);
2725 return FALSE;
2726 }
2727 }
2728 } else
2729 return FALSE;
2730 return retval;
2731 }
2732
2733 fullname = "warnings";
2734 if (match_optname(opts, fullname, 5, TRUE)) {
2735 if (duplicate)
2736 complain_about_duplicate(opts, 1);
2737 if (negated) {
2738 bad_negation(fullname, FALSE);
2739 return FALSE;
2740 }
2741 return warning_opts(opts, fullname);
2742 }
2743
2744 /* boulder:symbol */
2745 fullname = "boulder";
2746 if (match_optname(opts, fullname, 7, TRUE)) {
2747 #ifdef BACKWARD_COMPAT
2748 int clash = 0;
2749
2750 if (duplicate)
2751 complain_about_duplicate(opts, 1);
2752 if (negated) {
2753 bad_negation(fullname, FALSE);
2754 return FALSE;
2755 }
2756 /* if ((opts = string_for_env_opt(fullname, opts, FALSE))
2757 == empty_optstr)
2758 */
2759 if ((opts = string_for_opt(opts, FALSE)) == empty_optstr)
2760 return FALSE;
2761 escapes(opts, opts);
2762 /* note: dummy monclass #0 has symbol value '\0'; we allow that--
2763 attempting to set bouldersym to '^@'/'\0' will reset to default */
2764 if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
2765 clash = opts[0] ? 1 : 0;
2766 else if (opts[0] >= '1' && opts[0] < WARNCOUNT + '0')
2767 clash = 2;
2768 if (clash) {
2769 /* symbol chosen matches a used monster or warning
2770 symbol which is not good - reject it */
2771 config_error_add(
2772 "Badoption - boulder symbol '%s' would conflict with a %s symbol",
2773 visctrl(opts[0]),
2774 (clash == 1) ? "monster" : "warning");
2775 } else {
2776 /*
2777 * Override the default boulder symbol.
2778 */
2779 ov_primary_syms[SYM_BOULDER + SYM_OFF_X] = (nhsym) opts[0];
2780 ov_rogue_syms[SYM_BOULDER + SYM_OFF_X] = (nhsym) opts[0];
2781 /* for 'initial', update of BOULDER symbol is done in
2782 initoptions_finish(), after all symset options
2783 have been processed */
2784 if (!initial) {
2785 nhsym sym = get_othersym(SYM_BOULDER,
2786 Is_rogue_level(&u.uz) ? ROGUESET : PRIMARY);
2787 if (sym)
2788 showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
2789 need_redraw = TRUE;
2790 }
2791 }
2792 return retval;
2793 #else
2794 config_error_add("'%s' no longer supported; use S_boulder:c instead",
2795 fullname);
2796 return FALSE;
2797 #endif
2798 }
2799
2800 /* name:string */
2801 fullname = "name";
2802 if (match_optname(opts, fullname, 4, TRUE)) {
2803 if (duplicate)
2804 complain_about_duplicate(opts, 1);
2805 if (negated) {
2806 bad_negation(fullname, FALSE);
2807 return FALSE;
2808 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2809 != empty_optstr) {
2810 nmcpy(plname, op, PL_NSIZ);
2811 } else
2812 return FALSE;
2813 return retval;
2814 }
2815
2816 /* altkeyhandler:string */
2817 fullname = "altkeyhandler";
2818 if (match_optname(opts, fullname, 4, TRUE)) {
2819 if (duplicate)
2820 complain_about_duplicate(opts, 1);
2821 if (negated) {
2822 bad_negation(fullname, FALSE);
2823 return FALSE;
2824 } else if ((op = string_for_opt(opts, negated)) != empty_optstr) {
2825 #if defined(WIN32) && defined(TTY_GRAPHICS)
2826 set_altkeyhandler(op);
2827 #endif
2828 } else
2829 return FALSE;
2830 return retval;
2831 }
2832
2833 /* WINCAP
2834 * align_status:[left|top|right|bottom] */
2835 fullname = "align_status";
2836 if (match_optname(opts, fullname, sizeof "align_status" - 1, TRUE)) {
2837 op = string_for_opt(opts, negated);
2838 if ((op != empty_optstr) && !negated) {
2839 if (!strncmpi(op, "left", sizeof "left" - 1))
2840 iflags.wc_align_status = ALIGN_LEFT;
2841 else if (!strncmpi(op, "top", sizeof "top" - 1))
2842 iflags.wc_align_status = ALIGN_TOP;
2843 else if (!strncmpi(op, "right", sizeof "right" - 1))
2844 iflags.wc_align_status = ALIGN_RIGHT;
2845 else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
2846 iflags.wc_align_status = ALIGN_BOTTOM;
2847 else {
2848 config_error_add("Unknown %s parameter '%s'", fullname, op);
2849 return FALSE;
2850 }
2851 } else if (negated) {
2852 bad_negation(fullname, TRUE);
2853 return FALSE;
2854 }
2855 return retval;
2856 }
2857
2858 /* WINCAP
2859 * align_message:[left|top|right|bottom] */
2860 fullname = "align_message";
2861 if (match_optname(opts, fullname, sizeof "align_message" - 1, TRUE)) {
2862 if (duplicate)
2863 complain_about_duplicate(opts, 1);
2864 op = string_for_opt(opts, negated);
2865 if ((op != empty_optstr) && !negated) {
2866 if (!strncmpi(op, "left", sizeof "left" - 1))
2867 iflags.wc_align_message = ALIGN_LEFT;
2868 else if (!strncmpi(op, "top", sizeof "top" - 1))
2869 iflags.wc_align_message = ALIGN_TOP;
2870 else if (!strncmpi(op, "right", sizeof "right" - 1))
2871 iflags.wc_align_message = ALIGN_RIGHT;
2872 else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
2873 iflags.wc_align_message = ALIGN_BOTTOM;
2874 else {
2875 config_error_add("Unknown %s parameter '%s'", fullname, op);
2876 return FALSE;
2877 }
2878 } else if (negated) {
2879 bad_negation(fullname, TRUE);
2880 return FALSE;
2881 }
2882 return retval;
2883 }
2884
2885 /* the order to list inventory */
2886 fullname = "packorder";
2887 if (match_optname(opts, fullname, 4, TRUE)) {
2888 if (duplicate)
2889 complain_about_duplicate(opts, 1);
2890 if (negated) {
2891 bad_negation(fullname, FALSE);
2892 return FALSE;
2893 } else if ((op = string_for_opt(opts, FALSE)) == empty_optstr)
2894 return FALSE;
2895
2896 if (!change_inv_order(op))
2897 return FALSE;
2898 return retval;
2899 }
2900
2901 /* user can change required response for some prompts (quit, die, hit),
2902 or add an extra prompt (pray, Remove) that isn't ordinarily there */
2903 fullname = "paranoid_confirmation";
2904 if (match_optname(opts, fullname, 8, TRUE)) {
2905 /* at present we don't complain about duplicates for this
2906 option, but we do throw away the old settings whenever
2907 we process a new one [clearing old flags is essential
2908 for handling default paranoid_confirm:pray sanely] */
2909 flags.paranoia_bits = 0; /* clear all */
2910 if (negated) {
2911 flags.paranoia_bits = 0; /* [now redundant...] */
2912 } else if ((op = string_for_opt(opts, TRUE)) != empty_optstr) {
2913 char *pp, buf[BUFSZ];
2914
2915 strncpy(buf, op, sizeof buf - 1);
2916 buf[sizeof buf - 1] = '\0';
2917 op = mungspaces(buf);
2918 for (;;) {
2919 /* We're looking to parse
2920 "paranoid_confirm:whichone wheretwo whothree"
2921 and "paranoid_confirm:" prefix has already
2922 been stripped off by the time we get here */
2923 pp = index(op, ' ');
2924 if (pp)
2925 *pp = '\0';
2926 /* we aren't matching option names but match_optname()
2927 does what we want once we've broken the space
2928 delimited aggregate into separate tokens */
2929 for (i = 0; i < SIZE(paranoia); ++i) {
2930 if (match_optname(op, paranoia[i].argname,
2931 paranoia[i].argMinLen, FALSE)
2932 || (paranoia[i].synonym
2933 && match_optname(op, paranoia[i].synonym,
2934 paranoia[i].synMinLen, FALSE))) {
2935 if (paranoia[i].flagmask)
2936 flags.paranoia_bits |= paranoia[i].flagmask;
2937 else /* 0 == "none", so clear all */
2938 flags.paranoia_bits = 0;
2939 break;
2940 }
2941 }
2942 if (i == SIZE(paranoia)) {
2943 /* didn't match anything, so arg is bad;
2944 any flags already set will stay set */
2945 config_error_add("Unknown %s parameter '%s'",
2946 fullname, op);
2947 return FALSE;
2948 }
2949 /* move on to next token */
2950 if (pp)
2951 op = pp + 1;
2952 else
2953 break; /* no next token */
2954 } /* for(;;) */
2955 } else
2956 return FALSE;
2957 return retval;
2958 }
2959
2960 /* accept deprecated boolean; superseded by paranoid_confirm:pray */
2961 fullname = "prayconfirm";
2962 if (match_optname(opts, fullname, 4, FALSE)) {
2963 if (negated)
2964 flags.paranoia_bits &= ~PARANOID_PRAY;
2965 else
2966 flags.paranoia_bits |= PARANOID_PRAY;
2967 return retval;
2968 }
2969
2970 /* maximum burden picked up before prompt (Warren Cheung) */
2971 fullname = "pickup_burden";
2972 if (match_optname(opts, fullname, 8, TRUE)) {
2973 if (duplicate)
2974 complain_about_duplicate(opts, 1);
2975 if (negated) {
2976 bad_negation(fullname, FALSE);
2977 return FALSE;
2978 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
2979 != empty_optstr) {
2980 switch (lowc(*op)) {
2981 case 'u': /* Unencumbered */
2982 flags.pickup_burden = UNENCUMBERED;
2983 break;
2984 case 'b': /* Burdened (slight encumbrance) */
2985 flags.pickup_burden = SLT_ENCUMBER;
2986 break;
2987 case 's': /* streSsed (moderate encumbrance) */
2988 flags.pickup_burden = MOD_ENCUMBER;
2989 break;
2990 case 'n': /* straiNed (heavy encumbrance) */
2991 flags.pickup_burden = HVY_ENCUMBER;
2992 break;
2993 case 'o': /* OverTaxed (extreme encumbrance) */
2994 case 't':
2995 flags.pickup_burden = EXT_ENCUMBER;
2996 break;
2997 case 'l': /* overLoaded */
2998 flags.pickup_burden = OVERLOADED;
2999 break;
3000 default:
3001 config_error_add("Unknown %s parameter '%s'", fullname, op);
3002 return FALSE;
3003 }
3004 } else
3005 return FALSE;
3006 return retval;
3007 }
3008
3009 /* types of objects to pick up automatically */
3010 fullname = "pickup_types";
3011 if (match_optname(opts, fullname, 8, TRUE)) {
3012 char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1],
3013 qbuf[QBUFSZ], abuf[BUFSZ];
3014 int oc_sym;
3015 boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
3016
3017 if (duplicate)
3018 complain_about_duplicate(opts, 1);
3019 oc_to_str(flags.pickup_types, tbuf);
3020 flags.pickup_types[0] = '\0'; /* all */
3021 op = string_for_opt(opts, (compat || !initial));
3022 if (op == empty_optstr) {
3023 if (compat || negated || initial) {
3024 /* for backwards compatibility, "pickup" without a
3025 value is a synonym for autopickup of all types
3026 (and during initialization, we can't prompt yet) */
3027 flags.pickup = !negated;
3028 return retval;
3029 }
3030 oc_to_str(flags.inv_order, ocl);
3031 use_menu = TRUE;
3032 if (flags.menu_style == MENU_TRADITIONAL
3033 || flags.menu_style == MENU_COMBINATION) {
3034 boolean wasspace;
3035
3036 use_menu = FALSE;
3037 Sprintf(qbuf, "New %s: [%s am] (%s)", fullname, ocl,
3038 *tbuf ? tbuf : "all");
3039 abuf[0] = '\0';
3040 getlin(qbuf, abuf);
3041 wasspace = (abuf[0] == ' '); /* before mungspaces */
3042 op = mungspaces(abuf);
3043 if (wasspace && !abuf[0])
3044 ; /* one or more spaces will remove old value */
3045 else if (!abuf[0] || abuf[0] == '\033')
3046 op = tbuf; /* restore */
3047 else if (abuf[0] == 'm')
3048 use_menu = TRUE;
3049 /* note: abuf[0]=='a' is already handled via clearing the
3050 the old value (above) as a default action */
3051 }
3052 if (use_menu) {
3053 if (wizard && !index(ocl, VENOM_SYM))
3054 strkitten(ocl, VENOM_SYM);
3055 (void) choose_classes_menu("Autopickup what?", 1, TRUE, ocl,
3056 tbuf);
3057 op = tbuf;
3058 }
3059 }
3060 if (negated) {
3061 bad_negation(fullname, TRUE);
3062 return FALSE;
3063 }
3064 while (*op == ' ')
3065 op++;
3066 if (*op != 'a' && *op != 'A') {
3067 num = 0;
3068 while (*op) {
3069 oc_sym = def_char_to_objclass(*op);
3070 /* make sure all are valid obj symbols occurring once */
3071 if (oc_sym != MAXOCLASSES
3072 && !index(flags.pickup_types, oc_sym)) {
3073 flags.pickup_types[num] = (char) oc_sym;
3074 flags.pickup_types[++num] = '\0';
3075 } else
3076 badopt = TRUE;
3077 op++;
3078 }
3079 if (badopt) {
3080 config_error_add("Unknown %s parameter '%s'", fullname, op);
3081 return FALSE;
3082 }
3083 }
3084 return retval;
3085 }
3086
3087 /* pile limit: when walking over objects, number which triggers
3088 "there are several/many objects here" instead of listing them */
3089 fullname = "pile_limit";
3090 if (match_optname(opts, fullname, 4, TRUE)) {
3091 if (duplicate)
3092 complain_about_duplicate(opts, 1);
3093 op = string_for_opt(opts, negated);
3094 if ((negated && op == empty_optstr)
3095 || (!negated && op != empty_optstr))
3096 flags.pile_limit = negated ? 0 : atoi(op);
3097 else if (negated) {
3098 bad_negation(fullname, TRUE);
3099 return FALSE;
3100 } else /* op == empty_optstr */
3101 flags.pile_limit = PILE_LIMIT_DFLT;
3102 /* sanity check */
3103 if (flags.pile_limit < 0)
3104 flags.pile_limit = PILE_LIMIT_DFLT;
3105 return retval;
3106 }
3107
3108 /* play mode: normal, explore/discovery, or debug/wizard */
3109 fullname = "playmode";
3110 if (match_optname(opts, fullname, 4, TRUE)) {
3111 if (duplicate)
3112 complain_about_duplicate(opts, 1);
3113 if (negated)
3114 bad_negation(fullname, FALSE);
3115 if (duplicate || negated)
3116 return FALSE;
3117 op = string_for_opt(opts, FALSE);
3118 if (op == empty_optstr)
3119 return FALSE;
3120 if (!strncmpi(op, "normal", 6) || !strcmpi(op, "play")) {
3121 wizard = discover = FALSE;
3122 } else if (!strncmpi(op, "explore", 6)
3123 || !strncmpi(op, "discovery", 6)) {
3124 wizard = FALSE, discover = TRUE;
3125 } else if (!strncmpi(op, "debug", 5) || !strncmpi(op, "wizard", 6)) {
3126 wizard = TRUE, discover = FALSE;
3127 } else {
3128 config_error_add("Invalid value for \"%s\":%s", fullname, op);
3129 return FALSE;
3130 }
3131 return retval;
3132 }
3133
3134 /* WINCAP
3135 * player_selection: dialog | prompt/prompts/prompting */
3136 fullname = "player_selection";
3137 if (match_optname(opts, fullname, sizeof "player_selection" - 1, TRUE)) {
3138 if (duplicate)
3139 complain_about_duplicate(opts, 1);
3140 op = string_for_opt(opts, negated);
3141 if (op != empty_optstr && !negated) {
3142 if (!strncmpi(op, "dialog", sizeof "dialog" - 1)) {
3143 iflags.wc_player_selection = VIA_DIALOG;
3144 } else if (!strncmpi(op, "prompt", sizeof "prompt" - 1)) {
3145 iflags.wc_player_selection = VIA_PROMPTS;
3146 } else {
3147 config_error_add("Unknown %s parameter '%s'", fullname, op);
3148 return FALSE;
3149 }
3150 } else if (negated) {
3151 bad_negation(fullname, TRUE);
3152 return FALSE;
3153 }
3154 return retval;
3155 }
3156
3157 /* things to disclose at end of game */
3158 fullname = "disclose";
3159 if (match_optname(opts, fullname, 7, TRUE)) {
3160 /*
3161 * The order that the end_disclose options are stored:
3162 * inventory, attribs, vanquished, genocided,
3163 * conduct, overview.
3164 * There is an array in flags:
3165 * end_disclose[NUM_DISCLOSURE_OPT];
3166 * with option settings for the each of the following:
3167 * iagvc [see disclosure_options in decl.c]:
3168 * Allowed setting values in that array are:
3169 * DISCLOSE_PROMPT_DEFAULT_YES ask with default answer yes
3170 * DISCLOSE_PROMPT_DEFAULT_NO ask with default answer no
3171 * DISCLOSE_YES_WITHOUT_PROMPT always disclose and don't ask
3172 * DISCLOSE_NO_WITHOUT_PROMPT never disclose and don't ask
3173 * DISCLOSE_PROMPT_DEFAULT_SPECIAL for 'vanquished' only...
3174 * DISCLOSE_SPECIAL_WITHOUT_PROMPT ...to set up sort order.
3175 *
3176 * Those setting values can be used in the option
3177 * string as a prefix to get the desired behaviour.
3178 *
3179 * For backward compatibility, no prefix is required,
3180 * and the presence of a i,a,g,v, or c without a prefix
3181 * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
3182 */
3183 int idx, prefix_val;
3184
3185 if (duplicate)
3186 complain_about_duplicate(opts, 1);
3187 op = string_for_opt(opts, TRUE);
3188 if (op != empty_optstr && negated) {
3189 bad_negation(fullname, TRUE);
3190 return FALSE;
3191 }
3192 /* "disclose" without a value means "all with prompting"
3193 and negated means "none without prompting" */
3194 if (op == empty_optstr
3195 || !strcmpi(op, "all") || !strcmpi(op, "none")) {
3196 if (op != empty_optstr && !strcmpi(op, "none"))
3197 negated = TRUE;
3198 for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
3199 flags.end_disclose[num] = negated
3200 ? DISCLOSE_NO_WITHOUT_PROMPT
3201 : DISCLOSE_PROMPT_DEFAULT_YES;
3202 return retval;
3203 }
3204
3205 num = 0;
3206 prefix_val = -1;
3207 while (*op && num < sizeof flags.end_disclose - 1) {
3208 static char valid_settings[] = {
3209 DISCLOSE_PROMPT_DEFAULT_YES, DISCLOSE_PROMPT_DEFAULT_NO,
3210 DISCLOSE_PROMPT_DEFAULT_SPECIAL,
3211 DISCLOSE_YES_WITHOUT_PROMPT, DISCLOSE_NO_WITHOUT_PROMPT,
3212 DISCLOSE_SPECIAL_WITHOUT_PROMPT, '\0'
3213 };
3214 register char c, *dop;
3215
3216 c = lowc(*op);
3217 if (c == 'k')
3218 c = 'v'; /* killed -> vanquished */
3219 if (c == 'd')
3220 c = 'o'; /* dungeon -> overview */
3221 dop = index(disclosure_options, c);
3222 if (dop) {
3223 idx = (int) (dop - disclosure_options);
3224 if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
3225 impossible("bad disclosure index %d %c", idx, c);
3226 continue;
3227 }
3228 if (prefix_val != -1) {
3229 if (*dop != 'v') {
3230 if (prefix_val == DISCLOSE_PROMPT_DEFAULT_SPECIAL)
3231 prefix_val = DISCLOSE_PROMPT_DEFAULT_YES;
3232 if (prefix_val == DISCLOSE_SPECIAL_WITHOUT_PROMPT)
3233 prefix_val = DISCLOSE_YES_WITHOUT_PROMPT;
3234 }
3235 flags.end_disclose[idx] = prefix_val;
3236 prefix_val = -1;
3237 } else
3238 flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
3239 } else if (index(valid_settings, c)) {
3240 prefix_val = c;
3241 } else if (c == ' ') {
3242 ; /* do nothing */
3243 } else {
3244 config_error_add("Unknown %s parameter '%c'", fullname, *op);
3245 return FALSE;
3246 }
3247 op++;
3248 }
3249 return retval;
3250 }
3251
3252 /* scores:5t[op] 5a[round] o[wn] */
3253 fullname = "scores";
3254 if (match_optname(opts, fullname, 4, TRUE)) {
3255 if (duplicate)
3256 complain_about_duplicate(opts, 1);
3257 if (negated) {
3258 bad_negation(fullname, FALSE);
3259 return FALSE;
3260 }
3261 if ((op = string_for_opt(opts, FALSE)) == empty_optstr)
3262 return FALSE;
3263
3264 while (*op) {
3265 int inum = 1;
3266
3267 if (digit(*op)) {
3268 inum = atoi(op);
3269 while (digit(*op))
3270 op++;
3271 } else if (*op == '!') {
3272 negated = !negated;
3273 op++;
3274 }
3275 while (*op == ' ')
3276 op++;
3277
3278 switch (*op) {
3279 case 't':
3280 case 'T':
3281 flags.end_top = inum;
3282 break;
3283 case 'a':
3284 case 'A':
3285 flags.end_around = inum;
3286 break;
3287 case 'o':
3288 case 'O':
3289 flags.end_own = !negated;
3290 break;
3291 default:
3292 config_error_add("Unknown %s parameter '%s'", fullname, op);
3293 return FALSE;
3294 }
3295 /* "3a" is sufficient but accept "3around" (or "3abracadabra") */
3296 while (letter(*op))
3297 op++;
3298 /* t, a, and o can be separated by space(s) or slash or both */
3299 while (*op == ' ')
3300 op++;
3301 if (*op == '/')
3302 op++;
3303 }
3304 return retval;
3305 }
3306
3307 fullname = "sortloot";
3308 if (match_optname(opts, fullname, 4, TRUE)) {
3309 op = string_for_env_opt(fullname, opts, FALSE);
3310 if (op != empty_optstr) {
3311 char c = lowc(*op);
3312
3313 switch (c) {
3314 case 'n': /* none */
3315 case 'l': /* loot (pickup) */
3316 case 'f': /* full (pickup + invent) */
3317 flags.sortloot = c;
3318 break;
3319 default:
3320 config_error_add("Unknown %s parameter '%s'", fullname, op);
3321 return FALSE;
3322 }
3323 } else
3324 return FALSE;
3325 return retval;
3326 }
3327
3328 fullname = "suppress_alert";
3329 if (match_optname(opts, fullname, 4, TRUE)) {
3330 if (duplicate)
3331 complain_about_duplicate(opts, 1);
3332 op = string_for_opt(opts, negated);
3333 if (negated) {
3334 bad_negation(fullname, FALSE);
3335 return FALSE;
3336 } else if (op != empty_optstr)
3337 (void) feature_alert_opts(op, fullname);
3338 return retval;
3339 }
3340
3341 #ifdef VIDEOSHADES
3342 /* videocolors:string */
3343 fullname = "videocolors";
3344 if (match_optname(opts, fullname, 6, TRUE)
3345 || match_optname(opts, "videocolours", 10, TRUE)) {
3346 if (duplicate)
3347 complain_about_duplicate(opts, 1);
3348 if (negated) {
3349 bad_negation(fullname, FALSE);
3350 return FALSE;
3351 } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
3352 == empty_optstr) {
3353 return FALSE;
3354 }
3355 if (!assign_videocolors(opts)) {
3356 config_error_add("Unknown error handling '%s'", fullname);
3357 return FALSE;
3358 }
3359 return retval;
3360 }
3361 /* videoshades:string */
3362 fullname = "videoshades";
3363 if (match_optname(opts, fullname, 6, TRUE)) {
3364 if (duplicate)
3365 complain_about_duplicate(opts, 1);
3366 if (negated) {
3367 bad_negation(fullname, FALSE);
3368 return FALSE;
3369 } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
3370 == empty_optstr) {
3371 return FALSE;
3372 }
3373 if (!assign_videoshades(opts)) {
3374 config_error_add("Unknown error handling '%s'", fullname);
3375 return FALSE;
3376 }
3377 return retval;
3378 }
3379 #endif /* VIDEOSHADES */
3380
3381 #ifdef MSDOS
3382 #ifdef NO_TERMS
3383 /* video:string -- must be after longer tests */
3384 fullname = "video";
3385 if (match_optname(opts, fullname, 5, TRUE)) {
3386 if (duplicate)
3387 complain_about_duplicate(opts, 1);
3388 if (negated) {
3389 bad_negation(fullname, FALSE);
3390 return FALSE;
3391 } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
3392 == empty_optstr) {
3393 return FALSE;
3394 }
3395 if (!assign_video(opts)) {
3396 config_error_add("Unknown error handling '%s'", fullname);
3397 return FALSE;
3398 }
3399 return retval;
3400 }
3401 #endif /* NO_TERMS */
3402 /* soundcard:string -- careful not to match boolean 'sound' */
3403 fullname = "soundcard";
3404 if (match_optname(opts, fullname, 6, TRUE)) {
3405 if (duplicate)
3406 complain_about_duplicate(opts, 1);
3407 if (negated) {
3408 bad_negation(fullname, FALSE);
3409 return FALSE;
3410 } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
3411 == empty_optstr) {
3412 return FALSE;
3413 }
3414 if (!assign_soundcard(opts)) {
3415 config_error_add("Unknown error handling '%s'", fullname);
3416 return FALSE;
3417 }
3418 return retval;
3419 }
3420 #endif /* MSDOS */
3421
3422 /* WINCAP
3423 *
3424 * map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12
3425 * |ascii8x12|ascii16x12|ascii12x16|ascii10x18|fit_to_screen
3426 * |ascii_fit_to_screen|tiles_fit_to_screen]
3427 */
3428 fullname = "map_mode";
3429 if (match_optname(opts, fullname, sizeof "map_mode" - 1, TRUE)) {
3430 if (duplicate)
3431 complain_about_duplicate(opts, 1);
3432 op = string_for_opt(opts, negated);
3433 if (op != empty_optstr && !negated) {
3434 if (!strcmpi(op, "tiles"))
3435 iflags.wc_map_mode = MAP_MODE_TILES;
3436 else if (!strncmpi(op, "ascii4x6", sizeof "ascii4x6" - 1))
3437 iflags.wc_map_mode = MAP_MODE_ASCII4x6;
3438 else if (!strncmpi(op, "ascii6x8", sizeof "ascii6x8" - 1))
3439 iflags.wc_map_mode = MAP_MODE_ASCII6x8;
3440 else if (!strncmpi(op, "ascii8x8", sizeof "ascii8x8" - 1))
3441 iflags.wc_map_mode = MAP_MODE_ASCII8x8;
3442 else if (!strncmpi(op, "ascii16x8", sizeof "ascii16x8" - 1))
3443 iflags.wc_map_mode = MAP_MODE_ASCII16x8;
3444 else if (!strncmpi(op, "ascii7x12", sizeof "ascii7x12" - 1))
3445 iflags.wc_map_mode = MAP_MODE_ASCII7x12;
3446 else if (!strncmpi(op, "ascii8x12", sizeof "ascii8x12" - 1))
3447 iflags.wc_map_mode = MAP_MODE_ASCII8x12;
3448 else if (!strncmpi(op, "ascii16x12", sizeof "ascii16x12" - 1))
3449 iflags.wc_map_mode = MAP_MODE_ASCII16x12;
3450 else if (!strncmpi(op, "ascii12x16", sizeof "ascii12x16" - 1))
3451 iflags.wc_map_mode = MAP_MODE_ASCII12x16;
3452 else if (!strncmpi(op, "ascii10x18", sizeof "ascii10x18" - 1))
3453 iflags.wc_map_mode = MAP_MODE_ASCII10x18;
3454 else if (!strncmpi(op, "fit_to_screen",
3455 sizeof "fit_to_screen" - 1))
3456 iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
3457 else if (!strncmpi(op, "ascii_fit_to_screen",
3458 sizeof "ascii_fit_to_screen" - 1))
3459 iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
3460 else if (!strncmpi(op, "tiles_fit_to_screen",
3461 sizeof "tiles_fit_to_screen" - 1))
3462 iflags.wc_map_mode = MAP_MODE_TILES_FIT_TO_SCREEN;
3463 else {
3464 config_error_add("Unknown %s parameter '%s'", fullname, op);
3465 return FALSE;
3466 }
3467 } else if (negated) {
3468 bad_negation(fullname, TRUE);
3469 return FALSE;
3470 }
3471 return retval;
3472 }
3473
3474 /* WINCAP
3475 * scroll_amount:nn */
3476 fullname = "scroll_amount";
3477 if (match_optname(opts, fullname, sizeof "scroll_amount" - 1, TRUE)) {
3478 if (duplicate)
3479 complain_about_duplicate(opts, 1);
3480 op = string_for_opt(opts, negated);
3481 if ((negated && op == empty_optstr)
3482 || (!negated && op != empty_optstr)) {
3483 iflags.wc_scroll_amount = negated ? 1 : atoi(op);
3484 } else if (negated) {
3485 bad_negation(fullname, TRUE);
3486 return FALSE;
3487 }
3488 return retval;
3489 }
3490
3491 /* WINCAP
3492 * scroll_margin:nn */
3493 fullname = "scroll_margin";
3494 if (match_optname(opts, fullname, sizeof "scroll_margin" - 1, TRUE)) {
3495 if (duplicate)
3496 complain_about_duplicate(opts, 1);
3497 op = string_for_opt(opts, negated);
3498 if ((negated && op == empty_optstr)
3499 || (!negated && op != empty_optstr)) {
3500 iflags.wc_scroll_margin = negated ? 5 : atoi(op);
3501 } else if (negated) {
3502 bad_negation(fullname, TRUE);
3503 return FALSE;
3504 }
3505 return retval;
3506 }
3507
3508 fullname = "subkeyvalue";
3509 if (match_optname(opts, fullname, 5, TRUE)) {
3510 /* no duplicate complaint here */
3511 if (negated) {
3512 bad_negation(fullname, FALSE);
3513 return FALSE;
3514 #if defined(WIN32)
3515 } else {
3516 op = string_for_opt(opts, 0);
3517 if (op == empty_optstr)
3518 return FALSE;
3519 #ifdef TTY_GRAPHICS
3520 map_subkeyvalue(op);
3521 #endif
3522 #endif
3523 }
3524 return retval;
3525 }
3526
3527 /* WINCAP
3528 * tile_width:nn */
3529 fullname = "tile_width";
3530 if (match_optname(opts, fullname, sizeof "tile_width" - 1, TRUE)) {
3531 if (duplicate)
3532 complain_about_duplicate(opts, 1);
3533 op = string_for_opt(opts, negated);
3534 if ((negated && op == empty_optstr)
3535 || (!negated && op != empty_optstr)) {
3536 iflags.wc_tile_width = negated ? 0 : atoi(op);
3537 } else if (negated) {
3538 bad_negation(fullname, TRUE);
3539 return FALSE;
3540 }
3541 return retval;
3542 }
3543 /* WINCAP
3544 * tile_file:name */
3545 fullname = "tile_file";
3546 if (match_optname(opts, fullname, sizeof "tile_file" - 1, TRUE)) {
3547 if (duplicate)
3548 complain_about_duplicate(opts, 1);
3549 if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
3550 if (iflags.wc_tile_file)
3551 free(iflags.wc_tile_file);
3552 iflags.wc_tile_file = dupstr(op);
3553 } else
3554 return FALSE;
3555 return retval;
3556 }
3557 /* WINCAP
3558 * tile_height:nn */
3559 fullname = "tile_height";
3560 if (match_optname(opts, fullname, sizeof "tile_height" - 1, TRUE)) {
3561 if (duplicate)
3562 complain_about_duplicate(opts, 1);
3563 op = string_for_opt(opts, negated);
3564 if ((negated && op == empty_optstr)
3565 || (!negated && op != empty_optstr)) {
3566 iflags.wc_tile_height = negated ? 0 : atoi(op);
3567 } else if (negated) {
3568 bad_negation(fullname, TRUE);
3569 return FALSE;
3570 }
3571 return retval;
3572 }
3573
3574 /* WINCAP
3575 * vary_msgcount:nn */
3576 fullname = "vary_msgcount";
3577 if (match_optname(opts, fullname, sizeof "vary_msgcount" - 1, TRUE)) {
3578 if (duplicate)
3579 complain_about_duplicate(opts, 1);
3580 op = string_for_opt(opts, negated);
3581 if ((negated && op == empty_optstr)
3582 || (!negated && op != empty_optstr)) {
3583 iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
3584 } else if (negated) {
3585 bad_negation(fullname, TRUE);
3586 return FALSE;
3587 }
3588 return retval;
3589 }
3590
3591 /*
3592 * windowtype: option to choose the interface for binaries built
3593 * with support for more than one interface (tty + X11, for instance).
3594 *
3595 * Ideally, 'windowtype' should be processed first, because it
3596 * causes the wc_ and wc2_ flags to be set up.
3597 * For user, making it be first in a config file is trivial, use
3598 * OPTIONS=windowtype:Foo
3599 * as the first non-comment line of the file.
3600 * Making it first in NETHACKOPTIONS requires it to be at the _end_
3601 * because comma-separated option strings are processed from right
3602 * to left.
3603 */
3604 fullname = "windowtype";
3605 if (match_optname(opts, fullname, 3, TRUE)) {
3606 if (iflags.windowtype_locked)
3607 return retval;
3608 if (duplicate)
3609 complain_about_duplicate(opts, 1);
3610 if (negated) {
3611 bad_negation(fullname, FALSE);
3612 return FALSE;
3613 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
3614 != empty_optstr) {
3615 if (!iflags.windowtype_deferred) {
3616 char buf[WINTYPELEN];
3617
3618 nmcpy(buf, op, WINTYPELEN);
3619 choose_windows(buf);
3620 } else {
3621 nmcpy(chosen_windowtype, op, WINTYPELEN);
3622 }
3623 } else
3624 return FALSE;
3625 return retval;
3626 }
3627
3628 #ifdef WINCHAIN
3629 fullname = "windowchain";
3630 if (match_optname(opts, fullname, 3, TRUE)) {
3631 if (negated) {
3632 bad_negation(fullname, FALSE);
3633 return FALSE;
3634 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
3635 != empty_optstr) {
3636 char buf[WINTYPELEN];
3637
3638 nmcpy(buf, op, WINTYPELEN);
3639 addto_windowchain(buf);
3640 } else
3641 return FALSE;
3642 return retval;
3643 }
3644 #endif
3645
3646 /* WINCAP
3647 * setting window colors
3648 * syntax: windowcolors=menu foregrnd/backgrnd text foregrnd/backgrnd
3649 */
3650 fullname = "windowcolors";
3651 if (match_optname(opts, fullname, 7, TRUE)) {
3652 if (duplicate)
3653 complain_about_duplicate(opts, 1);
3654 if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
3655 if (!wc_set_window_colors(op)) {
3656 config_error_add("Could not set %s '%s'", fullname, op);
3657 return FALSE;
3658 }
3659 } else if (negated) {
3660 bad_negation(fullname, TRUE);
3661 return FALSE;
3662 }
3663 return retval;
3664 }
3665
3666 #ifdef CURSES_GRAPHICS
3667 /* WINCAP2
3668 * term_cols:amount or term_rows:amount */
3669 fullname = "term_cols";
3670 if (match_optname(opts, fullname, 8, TRUE)
3671 /* alternate spelling */
3672 || match_optname(opts, "term_columns", 9, TRUE)
3673 /* different option but identical handlng */
3674 || (fullname = "term_rows", match_optname(opts, fullname, 8, TRUE))) {
3675 long ltmp;
3676
3677 if ((op = string_for_opt(opts, negated)) != empty_optstr) {
3678 ltmp = atol(op);
3679 if (negated) {
3680 bad_negation(fullname, FALSE);
3681 retval = FALSE;
3682
3683 /* just checks atol() sanity, not logical window size sanity */
3684 } else if (ltmp <= 0L || ltmp >= (long) LARGEST_INT) {
3685 config_error_add("Invalid %s: %ld", fullname, ltmp);
3686 retval = FALSE;
3687
3688 } else {
3689 if (!strcmp(fullname, "term_rows"))
3690 iflags.wc2_term_rows = (int) ltmp;
3691 else /* !strcmp(fullname, "term_cols") */
3692 iflags.wc2_term_cols = (int) ltmp;
3693 }
3694 }
3695 return retval;
3696 }
3697
3698 /* WINCAP2
3699 * petattr:string */
3700 fullname = "petattr";
3701 if (match_optname(opts, fullname, sizeof "petattr" - 1, TRUE)) {
3702 op = string_for_opt(opts, negated);
3703 if (op != empty_optstr && negated) {
3704 bad_negation(fullname, TRUE);
3705 retval = FALSE;
3706 } else if (op != empty_optstr) {
3707 #ifdef CURSES_GRAPHICS
3708 int itmp = curses_read_attrs(op);
3709
3710 if (itmp == -1) {
3711 config_error_add("Unknown %s parameter '%s'", fullname, opts);
3712 retval = FALSE;
3713 } else
3714 iflags.wc2_petattr = itmp;
3715 #else
3716 /* non-curses windowports will not use this flag anyway
3717 * but the above will not compile if we don't have curses.
3718 * Just set it to a sensible default: */
3719 iflags.wc2_petattr = ATR_INVERSE;
3720 #endif
3721 } else if (negated) {
3722 iflags.wc2_petattr = ATR_NONE;
3723 }
3724 if (retval) {
3725 iflags.hilite_pet = (iflags.wc2_petattr != ATR_NONE);
3726 if (!initial)
3727 need_redraw = TRUE;
3728 }
3729 return retval;
3730 }
3731
3732 /* WINCAP2
3733 * windowborders:n */
3734 fullname = "windowborders";
3735 if (match_optname(opts, fullname, 10, TRUE)) {
3736 op = string_for_opt(opts, negated);
3737 if (negated && op != empty_optstr) {
3738 bad_negation(fullname, TRUE);
3739 retval = FALSE;
3740 } else {
3741 int itmp;
3742
3743 if (negated)
3744 itmp = 0; /* Off */
3745 else if (op == empty_optstr)
3746 itmp = 1; /* On */
3747 else /* Value supplied; expect 0 (off), 1 (on), or 2 (auto) */
3748 itmp = atoi(op);
3749
3750 if (itmp < 0 || itmp > 2) {
3751 config_error_add("Invalid %s (should be 0, 1, or 2): %s",
3752 fullname, opts);
3753 retval = FALSE;
3754 } else {
3755 iflags.wc2_windowborders = itmp;
3756 }
3757 }
3758 return retval;
3759 }
3760 #endif /* CURSES_GRAPHICS */
3761
3762 /* WINCAP2
3763 * statuslines:n */
3764 fullname = "statuslines";
3765 if (match_optname(opts, fullname, 11, TRUE)) {
3766 int itmp = 0;
3767
3768 op = string_for_opt(opts, negated);
3769 if (negated) {
3770 bad_negation(fullname, TRUE);
3771 itmp = 2;
3772 retval = FALSE;
3773 } else if (op != empty_optstr) {
3774 itmp = atoi(op);
3775 }
3776 if (itmp < 2 || itmp > 3) {
3777 config_error_add("'%s' requires a value of 2 or 3", fullname);
3778 retval = FALSE;
3779 } else {
3780 iflags.wc2_statuslines = itmp;
3781 if (!initial)
3782 need_redraw = TRUE;
3783 }
3784 return retval;
3785 }
3786
3787 /* menustyle:traditional or combination or full or partial */
3788 fullname = "menustyle";
3789 if (match_optname(opts, fullname, 4, TRUE)) {
3790 int tmp;
3791 boolean val_required = (strlen(opts) > 5 && !negated);
3792
3793 if (duplicate)
3794 complain_about_duplicate(opts, 1);
3795 if ((op = string_for_opt(opts, !val_required)) == empty_optstr) {
3796 if (val_required)
3797 return FALSE; /* string_for_opt gave feedback */
3798 tmp = negated ? 'n' : 'f';
3799 } else {
3800 tmp = lowc(*op);
3801 }
3802 switch (tmp) {
3803 case 'n': /* none */
3804 case 't': /* traditional: prompt for class(es) by symbol,
3805 prompt for each item within class(es) one at a time */
3806 flags.menu_style = MENU_TRADITIONAL;
3807 break;
3808 case 'c': /* combination: prompt for class(es) by symbol,
3809 choose items within selected class(es) by menu */
3810 flags.menu_style = MENU_COMBINATION;
3811 break;
3812 case 'f': /* full: choose class(es) by first menu,
3813 choose items within selected class(es) by second menu */
3814 flags.menu_style = MENU_FULL;
3815 break;
3816 case 'p': /* partial: skip class filtering,
3817 choose items among all classes by menu */
3818 flags.menu_style = MENU_PARTIAL;
3819 break;
3820 default:
3821 config_error_add("Unknown %s parameter '%s'", fullname, op);
3822 return FALSE;
3823 }
3824 return retval;
3825 }
3826
3827 fullname = "menu_headings";
3828 if (match_optname(opts, fullname, 12, TRUE)) {
3829 int tmpattr;
3830
3831 if (duplicate)
3832 complain_about_duplicate(opts, 1);
3833 if (negated) {
3834 bad_negation(fullname, FALSE);
3835 return FALSE;
3836 } else if ((opts = string_for_env_opt(fullname, opts, FALSE))
3837 == empty_optstr) {
3838 return FALSE;
3839 }
3840 tmpattr = match_str2attr(opts, TRUE);
3841 if (tmpattr == -1)
3842 return FALSE;
3843 else
3844 iflags.menu_headings = tmpattr;
3845 return retval;
3846 }
3847
3848 /* check for menu command mapping */
3849 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
3850 fullname = default_menu_cmd_info[i].name;
3851 if (duplicate)
3852 complain_about_duplicate(opts, 1);
3853 if (match_optname(opts, fullname, (int) strlen(fullname), TRUE)) {
3854 if (negated) {
3855 bad_negation(fullname, FALSE);
3856 return FALSE;
3857 } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
3858 char c, op_buf[BUFSZ];
3859
3860 escapes(op, op_buf);
3861 c = *op_buf;
3862
3863 if (illegal_menu_cmd_key(c))
3864 return FALSE;
3865
3866 add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd);
3867 }
3868 return retval;
3869 }
3870 }
3871
3872 /* hilite fields in status prompt */
3873 fullname = "hilite_status";
3874 if (match_optname(opts, fullname, 13, TRUE)) {
3875 #ifdef STATUS_HILITES
3876 if (duplicate)
3877 complain_about_duplicate(opts, 1);
3878 op = string_for_opt(opts, TRUE);
3879 if (op != empty_optstr && negated) {
3880 clear_status_hilites();
3881 return retval;
3882 } else if (op == empty_optstr) {
3883 config_error_add("Value is mandatory for hilite_status");
3884 return FALSE;
3885 }
3886 if (!parse_status_hl1(op, tfrom_file))
3887 return FALSE;
3888 return retval;
3889 #else
3890 config_error_add("'%s' is not supported", fullname);
3891 return FALSE;
3892 #endif
3893 }
3894
3895 /* control over whether highlights should be displayed, and for how long */
3896 fullname = "statushilites";
3897 if (match_optname(opts, fullname, 9, TRUE)) {
3898 #ifdef STATUS_HILITES
3899 if (negated) {
3900 iflags.hilite_delta = 0L;
3901 } else {
3902 op = string_for_opt(opts, TRUE);
3903 iflags.hilite_delta = (op == empty_optstr || !*op) ? 3L : atol(op);
3904 if (iflags.hilite_delta < 0L)
3905 iflags.hilite_delta = 1L;
3906 }
3907 if (!tfrom_file)
3908 reset_status_hilites();
3909 return retval;
3910 #else
3911 config_error_add("'%s' is not supported", fullname);
3912 return FALSE;
3913 #endif
3914 }
3915
3916 fullname = "DECgraphics";
3917 if (match_optname(opts, fullname, 3, TRUE)) {
3918 #ifdef BACKWARD_COMPAT
3919 boolean badflag = FALSE;
3920
3921 if (duplicate)
3922 complain_about_duplicate(opts, 1);
3923 if (!negated) {
3924 /* There is no rogue level DECgraphics-specific set */
3925 if (symset[PRIMARY].name) {
3926 badflag = TRUE;
3927 } else {
3928 symset[PRIMARY].name = dupstr(fullname);
3929 if (!read_sym_file(PRIMARY)) {
3930 badflag = TRUE;
3931 clear_symsetentry(PRIMARY, TRUE);
3932 } else
3933 switch_symbols(TRUE);
3934 }
3935 if (badflag) {
3936 config_error_add("Failure to load symbol set %s.", fullname);
3937 return FALSE;
3938 }
3939 }
3940 return retval;
3941 #else
3942 config_error_add("'%s' no longer supported; use 'symset:%s' instead",
3943 fullname, fullname);
3944 return FALSE;
3945 #endif
3946 } /* "DECgraphics" */
3947
3948 fullname = "IBMgraphics";
3949 if (match_optname(opts, fullname, 3, TRUE)) {
3950 #ifdef BACKWARD_COMPAT
3951 const char *sym_name = fullname;
3952 boolean badflag = FALSE;
3953
3954 if (duplicate)
3955 complain_about_duplicate(opts, 1);
3956 if (!negated) {
3957 for (i = 0; i < NUM_GRAPHICS; ++i) {
3958 if (symset[i].name) {
3959 badflag = TRUE;
3960 } else {
3961 if (i == ROGUESET)
3962 sym_name = "RogueIBM";
3963 symset[i].name = dupstr(sym_name);
3964 if (!read_sym_file(i)) {
3965 badflag = TRUE;
3966 clear_symsetentry(i, TRUE);
3967 break;
3968 }
3969 }
3970 }
3971 if (badflag) {
3972 config_error_add("Failure to load symbol set %s.", sym_name);
3973 return FALSE;
3974 } else {
3975 switch_symbols(TRUE);
3976 if (!initial && Is_rogue_level(&u.uz))
3977 assign_graphics(ROGUESET);
3978 }
3979 }
3980 return retval;
3981 #else
3982 config_error_add("'%s' no longer supported; use 'symset:%s' instead",
3983 fullname, fullname);
3984 return FALSE;
3985 #endif
3986 } /* "IBMgraphics" */
3987
3988 fullname = "MACgraphics";
3989 if (match_optname(opts, fullname, 3, TRUE)) {
3990 #if defined(MAC_GRAPHICS_ENV) && defined(BACKWARD_COMPAT)
3991 boolean badflag = FALSE;
3992
3993 if (duplicate)
3994 complain_about_duplicate(opts, 1);
3995 if (!negated) {
3996 if (symset[PRIMARY].name) {
3997 badflag = TRUE;
3998 } else {
3999 symset[PRIMARY].name = dupstr(fullname);
4000 if (!read_sym_file(PRIMARY)) {
4001 badflag = TRUE;
4002 clear_symsetentry(PRIMARY, TRUE);
4003 }
4004 }
4005 if (badflag) {
4006 config_error_add("Failure to load symbol set %s.", fullname);
4007 return FALSE;
4008 } else {
4009 switch_symbols(TRUE);
4010 if (!initial && Is_rogue_level(&u.uz))
4011 assign_graphics(ROGUESET);
4012 }
4013 }
4014 return retval;
4015 #else /* !(MAC_GRAPHICS_ENV && BACKWARD_COMPAT) */
4016 config_error_add("'%s' %s; use 'symset:%s' instead",
4017 fullname,
4018 #ifdef MAC_GRAPHICS_ENV /* implies BACKWARD_COMPAT is not defined */
4019 "no longer supported",
4020 #else
4021 "is not supported",
4022 #endif
4023 fullname);
4024 return FALSE;
4025 #endif /* ?(MAC_GRAPHICS_ENV && BACKWARD_COMPAT) */
4026 } /* "MACgraphics" */
4027
4028 /*
4029 * OK, if we still haven't recognized the option, check the boolean
4030 * options list.
4031 */
4032 for (i = 0; boolopt[i].name; i++) {
4033 if (match_optname(opts, boolopt[i].name, 3, TRUE)) {
4034 /* options that don't exist */
4035 if (!boolopt[i].addr) {
4036 if (!initial && !negated)
4037 pline_The("\"%s\" option is not available.",
4038 boolopt[i].name);
4039 return retval;
4040 }
4041 /* options that must come from config file */
4042 if (!initial && (boolopt[i].optflags == SET_IN_FILE)) {
4043 rejectoption(boolopt[i].name);
4044 return retval;
4045 }
4046
4047 op = string_for_opt(opts, TRUE);
4048 if (op != empty_optstr) {
4049 if (negated) {
4050 config_error_add(
4051 "Negated boolean '%s' should not have a parameter",
4052 boolopt[i].name);
4053 return FALSE;
4054 }
4055 if (!strcmp(op, "true") || !strcmp(op, "yes")) {
4056 negated = FALSE;
4057 } else if (!strcmp(op, "false") || !strcmp(op, "no")) {
4058 negated = TRUE;
4059 } else {
4060 config_error_add("Illegal parameter for a boolean");
4061 return FALSE;
4062 }
4063 }
4064 if (iflags.debug_fuzzer && !initial) {
4065 /* don't randomly toggle this/these */
4066 if (boolopt[i].addr == &flags.silent)
4067 return TRUE;
4068 }
4069
4070 *(boolopt[i].addr) = !negated;
4071
4072 /* 0 means boolean opts */
4073 if (duplicate_opt_detection(boolopt[i].name, 0))
4074 complain_about_duplicate(boolopt[i].name, 0);
4075 #ifdef RLECOMP
4076 if (boolopt[i].addr == &iflags.rlecomp)
4077 set_savepref(iflags.rlecomp ? "rlecomp" : "!rlecomp");
4078 #endif
4079 #ifdef ZEROCOMP
4080 if (boolopt[i].addr == &iflags.zerocomp)
4081 set_savepref(iflags.zerocomp ? "zerocomp" : "externalcomp");
4082 #endif
4083 if (boolopt[i].addr == &iflags.wc_ascii_map) {
4084 /* toggling ascii_map; set tiled_map to its opposite;
4085 what does it mean to turn off ascii map if tiled map
4086 isn't supported? -- right now, we do nothing */
4087 iflags.wc_tiled_map = negated;
4088 } else if (boolopt[i].addr == &iflags.wc_tiled_map) {
4089 /* toggling tiled_map; set ascii_map to its opposite;
4090 as with ascii_map, what does it mean to turn off tiled
4091 map if ascii map isn't supported? */
4092 iflags.wc_ascii_map = negated;
4093 }
4094 /* only do processing below if setting with doset() */
4095 if (initial)
4096 return retval;
4097
4098 if (boolopt[i].addr == &flags.time
4099 #ifdef SCORE_ON_BOTL
4100 || boolopt[i].addr == &flags.showscore
4101 #endif
4102 || boolopt[i].addr == &flags.showexp) {
4103 if (VIA_WINDOWPORT())
4104 status_initialize(REASSESS_ONLY);
4105 context.botl = TRUE;
4106 } else if (boolopt[i].addr == &flags.invlet_constant
4107 || boolopt[i].addr == &flags.sortpack
4108 || boolopt[i].addr == &iflags.implicit_uncursed) {
4109 if (!flags.invlet_constant)
4110 reassign();
4111 update_inventory();
4112 } else if (boolopt[i].addr == &flags.lit_corridor
4113 || boolopt[i].addr == &flags.dark_room) {
4114 /*
4115 * All corridor squares seen via night vision or
4116 * candles & lamps change. Update them by calling
4117 * newsym() on them. Don't do this if we are
4118 * initializing the options --- the vision system
4119 * isn't set up yet.
4120 */
4121 vision_recalc(2); /* shut down vision */
4122 vision_full_recalc = 1; /* delayed recalc */
4123 if (iflags.use_color)
4124 need_redraw = TRUE; /* darkroom refresh */
4125 } else if (boolopt[i].addr == &flags.showrace
4126 || boolopt[i].addr == &iflags.use_inverse
4127 || boolopt[i].addr == &iflags.hilite_pile
4128 || boolopt[i].addr == &iflags.perm_invent
4129 #ifdef CURSES_GRAPHICS
4130 || boolopt[i].addr == &iflags.cursesgraphics
4131 #endif
4132 || boolopt[i].addr == &iflags.wc_ascii_map
4133 || boolopt[i].addr == &iflags.wc_tiled_map) {
4134 need_redraw = TRUE;
4135 } else if (boolopt[i].addr == &iflags.hilite_pet) {
4136 #ifdef CURSES_GRAPHICS
4137 if (WINDOWPORT("curses")) {
4138 /* if we're enabling hilite_pet and petattr isn't set,
4139 set it to Inverse; if we're disabling, leave petattr
4140 alone so that re-enabling will get current value back */
4141 if (iflags.hilite_pet && !iflags.wc2_petattr)
4142 iflags.wc2_petattr = curses_read_attrs("I");
4143 }
4144 #endif
4145 need_redraw = TRUE;
4146 } else if (boolopt[i].addr == &iflags.wc2_hitpointbar) {
4147 if (VIA_WINDOWPORT()) {
4148 /* [is reassessment really needed here?] */
4149 status_initialize(REASSESS_ONLY);
4150 need_redraw = TRUE;
4151 }
4152 #ifdef TEXTCOLOR
4153 } else if (boolopt[i].addr == &iflags.use_color) {
4154 need_redraw = TRUE;
4155 #ifdef TOS
4156 if (iflags.BIOS) {
4157 if (colors_changed)
4158 restore_colors();
4159 else
4160 set_colors();
4161 }
4162 #endif
4163 } else if (boolopt[i].addr == &iflags.use_menu_color
4164 || boolopt[i].addr == &iflags.wc2_guicolor) {
4165 update_inventory();
4166 #endif /* TEXTCOLOR */
4167 }
4168 return retval;
4169 }
4170 }
4171
4172 /* Is it a symbol? */
4173 if (strstr(opts, "S_") == opts && parsesymbols(opts, PRIMARY)) {
4174 switch_symbols(TRUE);
4175 check_gold_symbol();
4176 return retval;
4177 }
4178
4179 /* out of valid options */
4180 config_error_add("Unknown option '%s'", opts);
4181 return FALSE;
4182 }
4183
4184 /* parse key:command */
4185 boolean
parsebindings(bindings)4186 parsebindings(bindings)
4187 char* bindings;
4188 {
4189 char *bind;
4190 char key;
4191 int i;
4192 boolean ret = FALSE;
4193
4194 /* break off first binding from the rest; parse the rest */
4195 if ((bind = index(bindings, ',')) != 0) {
4196 *bind++ = 0;
4197 ret |= parsebindings(bind);
4198 }
4199
4200 /* parse a single binding: first split around : */
4201 if (! (bind = index(bindings, ':')))
4202 return FALSE; /* it's not a binding */
4203 *bind++ = 0;
4204
4205 /* read the key to be bound */
4206 key = txt2key(bindings);
4207 if (!key) {
4208 config_error_add("Unknown key binding key '%s'", bindings);
4209 return FALSE;
4210 }
4211
4212 bind = trimspaces(bind);
4213
4214 /* is it a special key? */
4215 if (bind_specialkey(key, bind))
4216 return TRUE;
4217
4218 /* is it a menu command? */
4219 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
4220 if (!strcmp(default_menu_cmd_info[i].name, bind)) {
4221 if (illegal_menu_cmd_key(key)) {
4222 config_error_add("Bad menu key %s:%s", visctrl(key), bind);
4223 return FALSE;
4224 } else
4225 add_menu_cmd_alias(key, default_menu_cmd_info[i].cmd);
4226 return TRUE;
4227 }
4228 }
4229
4230 /* extended command? */
4231 if (!bind_key(key, bind)) {
4232 config_error_add("Unknown key binding command '%s'", bind);
4233 return FALSE;
4234 }
4235 return TRUE;
4236 }
4237
4238 static NEARDATA const char *menutype[] = { "traditional", "combination",
4239 "full", "partial" };
4240
4241 static NEARDATA const char *burdentype[] = { "unencumbered", "burdened",
4242 "stressed", "strained",
4243 "overtaxed", "overloaded" };
4244
4245 static NEARDATA const char *runmodes[] = { "teleport", "run", "walk",
4246 "crawl" };
4247
4248 static NEARDATA const char *sortltype[] = { "none", "loot", "full" };
4249
4250 /*
4251 * Convert the given string of object classes to a string of default object
4252 * symbols.
4253 */
4254 void
oc_to_str(src,dest)4255 oc_to_str(src, dest)
4256 char *src, *dest;
4257 {
4258 int i;
4259
4260 while ((i = (int) *src++) != 0) {
4261 if (i < 0 || i >= MAXOCLASSES)
4262 impossible("oc_to_str: illegal object class %d", i);
4263 else
4264 *dest++ = def_oc_syms[i].sym;
4265 }
4266 *dest = '\0';
4267 }
4268
4269 /*
4270 * Add the given mapping to the menu command map list. Always keep the
4271 * maps valid C strings.
4272 */
4273 void
add_menu_cmd_alias(from_ch,to_ch)4274 add_menu_cmd_alias(from_ch, to_ch)
4275 char from_ch, to_ch;
4276 {
4277 if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS) {
4278 pline("out of menu map space.");
4279 } else {
4280 mapped_menu_cmds[n_menu_mapped] = from_ch;
4281 mapped_menu_op[n_menu_mapped] = to_ch;
4282 n_menu_mapped++;
4283 mapped_menu_cmds[n_menu_mapped] = '\0';
4284 mapped_menu_op[n_menu_mapped] = '\0';
4285 }
4286 }
4287
4288 char
get_menu_cmd_key(ch)4289 get_menu_cmd_key(ch)
4290 char ch;
4291 {
4292 char *found = index(mapped_menu_op, ch);
4293
4294 if (found) {
4295 int idx = (int) (found - mapped_menu_op);
4296
4297 ch = mapped_menu_cmds[idx];
4298 }
4299 return ch;
4300 }
4301
4302 /*
4303 * Map the given character to its corresponding menu command. If it
4304 * doesn't match anything, just return the original.
4305 */
4306 char
map_menu_cmd(ch)4307 map_menu_cmd(ch)
4308 char ch;
4309 {
4310 char *found = index(mapped_menu_cmds, ch);
4311
4312 if (found) {
4313 int idx = (int) (found - mapped_menu_cmds);
4314
4315 ch = mapped_menu_op[idx];
4316 }
4317 return ch;
4318 }
4319
4320 void
show_menu_controls(win,dolist)4321 show_menu_controls(win, dolist)
4322 winid win;
4323 boolean dolist;
4324 {
4325 char buf[BUFSZ];
4326
4327 putstr(win, 0, "Menu control keys:");
4328 if (dolist) {
4329 int i;
4330
4331 for (i = 0; i < SIZE(default_menu_cmd_info); i++) {
4332 Sprintf(buf, "%-8s %s",
4333 visctrl(get_menu_cmd_key(default_menu_cmd_info[i].cmd)),
4334 default_menu_cmd_info[i].desc);
4335 putstr(win, 0, buf);
4336 }
4337 } else {
4338 const char
4339 fmt3[] = " %-12s %-2s %-2s %s",
4340 fmt2[] = " %-12s %-2s %-2s",
4341 fmt1[] = " %10s %-2s %s",
4342 fmt0[] = " %14s %s";
4343
4344 putstr(win, 0, "");
4345 putstr(win, 0, "Selection: On page Full menu");
4346 Sprintf(buf, fmt2, "Select all",
4347 visctrl(get_menu_cmd_key(MENU_SELECT_PAGE)),
4348 visctrl(get_menu_cmd_key(MENU_SELECT_ALL)));
4349 putstr(win, 0, buf);
4350 Sprintf(buf, fmt2, "Deselect all",
4351 visctrl(get_menu_cmd_key(MENU_UNSELECT_PAGE)),
4352 visctrl(get_menu_cmd_key(MENU_UNSELECT_ALL)));
4353 putstr(win, 0, buf);
4354 Sprintf(buf, fmt2, "Invert all",
4355 visctrl(get_menu_cmd_key(MENU_INVERT_PAGE)),
4356 visctrl(get_menu_cmd_key(MENU_INVERT_ALL)));
4357 putstr(win, 0, buf);
4358 Sprintf(buf, fmt3, "Text match", "",
4359 visctrl(get_menu_cmd_key(MENU_SEARCH)),
4360 "Search and toggle matching entries");
4361 putstr(win, 0, buf);
4362 putstr(win, 0, "");
4363 putstr(win, 0, "Navigation:");
4364 Sprintf(buf, fmt1, "Go to ",
4365 visctrl(get_menu_cmd_key(MENU_NEXT_PAGE)),
4366 "Next page");
4367 putstr(win, 0, buf);
4368 Sprintf(buf, fmt1, "",
4369 visctrl(get_menu_cmd_key(MENU_PREVIOUS_PAGE)),
4370 "Previous page");
4371 putstr(win, 0, buf);
4372 Sprintf(buf, fmt1, "",
4373 visctrl(get_menu_cmd_key(MENU_FIRST_PAGE)),
4374 "First page");
4375 putstr(win, 0, buf);
4376 Sprintf(buf, fmt1, "",
4377 visctrl(get_menu_cmd_key(MENU_LAST_PAGE)),
4378 "Last page");
4379 putstr(win, 0, buf);
4380 Sprintf(buf, fmt0, "SPACE", "Next page, if any, otherwise RETURN");
4381 putstr(win, 0, buf);
4382 Sprintf(buf, fmt0, "RETURN/ENTER",
4383 "Finish menu with any selection(s) made");
4384 putstr(win, 0, buf);
4385 Sprintf(buf, fmt0, "ESCAPE",
4386 "Cancel menu without selecting anything");
4387 putstr(win, 0, buf);
4388 }
4389 }
4390
4391 #if defined(MICRO) || defined(MAC) || defined(WIN32)
4392 #define OPTIONS_HEADING "OPTIONS"
4393 #else
4394 #define OPTIONS_HEADING "NETHACKOPTIONS"
4395 #endif
4396
4397 static char fmtstr_doset[] = "%s%-15s [%s] ";
4398 static char fmtstr_doset_tab[] = "%s\t[%s]";
4399 static char n_currently_set[] = "(%d currently set)";
4400
4401 /* doset('O' command) menu entries for compound options */
4402 STATIC_OVL void
doset_add_menu(win,option,indexoffset)4403 doset_add_menu(win, option, indexoffset)
4404 winid win; /* window to add to */
4405 const char *option; /* option name */
4406 int indexoffset; /* value to add to index in compopt[], or zero
4407 if option cannot be changed */
4408 {
4409 const char *value = "unknown"; /* current value */
4410 char buf[BUFSZ], buf2[BUFSZ];
4411 anything any;
4412 int i;
4413
4414 any = zeroany;
4415 if (indexoffset == 0) {
4416 any.a_int = 0;
4417 value = get_compopt_value(option, buf2);
4418 } else {
4419 for (i = 0; compopt[i].name; i++)
4420 if (strcmp(option, compopt[i].name) == 0)
4421 break;
4422
4423 if (compopt[i].name) {
4424 any.a_int = i + 1 + indexoffset;
4425 value = get_compopt_value(option, buf2);
4426 } else {
4427 /* We are trying to add an option not found in compopt[].
4428 This is almost certainly bad, but we'll let it through anyway
4429 (with a zero value, so it can't be selected). */
4430 any.a_int = 0;
4431 }
4432 }
4433 /* " " replaces "a - " -- assumes menus follow that style */
4434 if (!iflags.menu_tab_sep)
4435 Sprintf(buf, fmtstr_doset, any.a_int ? "" : " ", option,
4436 value);
4437 else
4438 Sprintf(buf, fmtstr_doset_tab, option, value);
4439 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
4440 }
4441
4442 STATIC_OVL void
opts_add_others(win,name,id,bufx,nset)4443 opts_add_others(win, name, id, bufx, nset)
4444 winid win;
4445 const char *name;
4446 int id;
4447 char *bufx;
4448 int nset;
4449 {
4450 char buf[BUFSZ], buf2[BUFSZ];
4451 anything any = zeroany;
4452
4453 any.a_int = id;
4454 if (!bufx)
4455 Sprintf(buf2, n_currently_set, nset);
4456 else
4457 Sprintf(buf2, "%s", bufx);
4458 if (!iflags.menu_tab_sep)
4459 Sprintf(buf, fmtstr_doset, any.a_int ? "" : " ",
4460 name, buf2);
4461 else
4462 Sprintf(buf, fmtstr_doset_tab, name, buf2);
4463 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
4464 }
4465
4466 int
count_apes(VOID_ARGS)4467 count_apes(VOID_ARGS)
4468 {
4469 int numapes = 0;
4470 struct autopickup_exception *ape = apelist;
4471
4472 while (ape) {
4473 numapes++;
4474 ape = ape->next;
4475 }
4476
4477 return numapes;
4478 }
4479
4480 enum opt_other_enums {
4481 OPT_OTHER_MSGTYPE = -4,
4482 OPT_OTHER_MENUCOLOR = -3,
4483 OPT_OTHER_STATHILITE = -2,
4484 OPT_OTHER_APEXC = -1
4485 /* these must be < 0 */
4486 };
4487
4488 static struct other_opts {
4489 const char *name;
4490 int optflags;
4491 enum opt_other_enums code;
4492 int NDECL((*othr_count_func));
4493 } othropt[] = {
4494 { "autopickup exceptions", SET_IN_GAME, OPT_OTHER_APEXC, count_apes },
4495 { "menu colors", SET_IN_GAME, OPT_OTHER_MENUCOLOR, count_menucolors },
4496 { "message types", SET_IN_GAME, OPT_OTHER_MSGTYPE, msgtype_count },
4497 #ifdef STATUS_HILITES
4498 { "status hilite rules", SET_IN_GAME, OPT_OTHER_STATHILITE,
4499 count_status_hilites },
4500 #endif
4501 { (char *) 0, 0, (enum opt_other_enums) 0 },
4502 };
4503
4504 /* the 'O' command */
4505 int
doset()4506 doset() /* changing options via menu by Per Liboriussen */
4507 {
4508 static boolean made_fmtstr = FALSE;
4509 char buf[BUFSZ];
4510 const char *name;
4511 int i = 0, pass, boolcount, pick_cnt, pick_idx, opt_indx;
4512 boolean *bool_p;
4513 winid tmpwin;
4514 anything any;
4515 menu_item *pick_list;
4516 int indexoffset, startpass, endpass, optflags;
4517 boolean setinitial = FALSE, fromfile = FALSE;
4518 unsigned longest_name_len;
4519
4520 tmpwin = create_nhwindow(NHW_MENU);
4521 start_menu(tmpwin);
4522
4523 #ifdef notyet /* SYSCF */
4524 /* XXX I think this is still fragile. Fixing initial/from_file and/or
4525 changing the SET_* etc to bitmaps will let me make this better. */
4526 if (wizard)
4527 startpass = SET_IN_SYS;
4528 else
4529 #endif
4530 startpass = DISP_IN_GAME;
4531 endpass = (wizard) ? SET_IN_WIZGAME : SET_IN_GAME;
4532
4533 if (!made_fmtstr && !iflags.menu_tab_sep) {
4534 /* spin through the options to find the longest name
4535 and adjust the format string accordingly */
4536 longest_name_len = 0;
4537 for (pass = 0; pass <= 2; pass++)
4538 for (i = 0; (name = ((pass == 0) ? boolopt[i].name
4539 : (pass == 1) ? compopt[i].name
4540 : othropt[i].name)) != 0; i++) {
4541 if (pass == 0 && !boolopt[i].addr)
4542 continue;
4543 optflags = (pass == 0) ? boolopt[i].optflags
4544 : (pass == 1) ? compopt[i].optflags
4545 : othropt[i].optflags;
4546 if (optflags < startpass || optflags > endpass)
4547 continue;
4548 if ((is_wc_option(name) && !wc_supported(name))
4549 || (is_wc2_option(name) && !wc2_supported(name)))
4550 continue;
4551
4552 if (strlen(name) > longest_name_len)
4553 longest_name_len = strlen(name);
4554 }
4555 Sprintf(fmtstr_doset, "%%s%%-%us [%%s]", longest_name_len);
4556 made_fmtstr = TRUE;
4557 }
4558
4559 any = zeroany;
4560 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
4561 "Booleans (selecting will toggle value):", MENU_UNSELECTED);
4562 any.a_int = 0;
4563 /* first list any other non-modifiable booleans, then modifiable ones */
4564 for (pass = 0; pass <= 1; pass++)
4565 for (i = 0; (name = boolopt[i].name) != 0; i++)
4566 if ((bool_p = boolopt[i].addr) != 0
4567 && ((boolopt[i].optflags <= DISP_IN_GAME && pass == 0)
4568 || (boolopt[i].optflags >= SET_IN_GAME && pass == 1))) {
4569 if (bool_p == &flags.female)
4570 continue; /* obsolete */
4571 if (boolopt[i].optflags == SET_IN_WIZGAME && !wizard)
4572 continue;
4573 if ((is_wc_option(name) && !wc_supported(name))
4574 || (is_wc2_option(name) && !wc2_supported(name)))
4575 continue;
4576
4577 any.a_int = (pass == 0) ? 0 : i + 1;
4578 if (!iflags.menu_tab_sep)
4579 Sprintf(buf, fmtstr_doset, (pass == 0) ? " " : "",
4580 name, *bool_p ? "true" : "false");
4581 else
4582 Sprintf(buf, fmtstr_doset_tab,
4583 name, *bool_p ? "true" : "false");
4584 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
4585 MENU_UNSELECTED);
4586 }
4587
4588 boolcount = i;
4589 indexoffset = boolcount;
4590 any = zeroany;
4591 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4592 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
4593 "Compounds (selecting will prompt for new value):",
4594 MENU_UNSELECTED);
4595
4596 /* deliberately put playmode, name, role+race+gender+align first */
4597 doset_add_menu(tmpwin, "playmode", 0);
4598 doset_add_menu(tmpwin, "name", 0);
4599 doset_add_menu(tmpwin, "role", 0);
4600 doset_add_menu(tmpwin, "race", 0);
4601 doset_add_menu(tmpwin, "gender", 0);
4602 doset_add_menu(tmpwin, "align", 0);
4603
4604 for (pass = startpass; pass <= endpass; pass++)
4605 for (i = 0; (name = compopt[i].name) != 0; i++)
4606 if (compopt[i].optflags == pass) {
4607 if (!strcmp(name, "playmode") || !strcmp(name, "name")
4608 || !strcmp(name, "role") || !strcmp(name, "race")
4609 || !strcmp(name, "gender") || !strcmp(name, "align"))
4610 continue;
4611 if ((is_wc_option(name) && !wc_supported(name))
4612 || (is_wc2_option(name) && !wc2_supported(name)))
4613 continue;
4614
4615 doset_add_menu(tmpwin, name,
4616 (pass == DISP_IN_GAME) ? 0 : indexoffset);
4617 }
4618
4619 any = zeroany;
4620 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4621 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
4622 "Other settings:", MENU_UNSELECTED);
4623
4624 for (i = 0; (name = othropt[i].name) != 0; i++) {
4625 if ((is_wc_option(name) && !wc_supported(name))
4626 || (is_wc2_option(name) && !wc2_supported(name)))
4627 continue;
4628 opts_add_others(tmpwin, name, othropt[i].code,
4629 (char *) 0, othropt[i].othr_count_func());
4630 }
4631
4632 #ifdef PREFIXES_IN_USE
4633 any = zeroany;
4634 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4635 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
4636 "Variable playground locations:", MENU_UNSELECTED);
4637 for (i = 0; i < PREFIX_COUNT; i++)
4638 doset_add_menu(tmpwin, fqn_prefix_names[i], 0);
4639 #endif
4640 end_menu(tmpwin, "Set what options?");
4641 need_redraw = FALSE;
4642 if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
4643 /*
4644 * Walk down the selection list and either invert the booleans
4645 * or prompt for new values. In most cases, call parseoptions()
4646 * to take care of options that require special attention, like
4647 * redraws.
4648 */
4649 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
4650 opt_indx = pick_list[pick_idx].item.a_int - 1;
4651 if (opt_indx < -1)
4652 opt_indx++; /* -1 offset for select_menu() */
4653 if (opt_indx == OPT_OTHER_APEXC) {
4654 (void) special_handling("autopickup_exception", setinitial,
4655 fromfile);
4656 #ifdef STATUS_HILITES
4657 } else if (opt_indx == OPT_OTHER_STATHILITE) {
4658 if (!status_hilite_menu()) {
4659 pline("Bad status hilite(s) specified.");
4660 } else {
4661 if (wc2_supported("hilite_status"))
4662 preference_update("hilite_status");
4663 }
4664 #endif
4665 } else if (opt_indx == OPT_OTHER_MENUCOLOR) {
4666 (void) special_handling("menu_colors", setinitial,
4667 fromfile);
4668 } else if (opt_indx == OPT_OTHER_MSGTYPE) {
4669 (void) special_handling("msgtype", setinitial, fromfile);
4670 } else if (opt_indx < boolcount) {
4671 /* boolean option */
4672 Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "",
4673 boolopt[opt_indx].name);
4674 (void) parseoptions(buf, setinitial, fromfile);
4675 if (wc_supported(boolopt[opt_indx].name)
4676 || wc2_supported(boolopt[opt_indx].name))
4677 preference_update(boolopt[opt_indx].name);
4678 } else {
4679 /* compound option */
4680 opt_indx -= boolcount;
4681
4682 if (!special_handling(compopt[opt_indx].name, setinitial,
4683 fromfile)) {
4684 char abuf[BUFSZ];
4685
4686 Sprintf(buf, "Set %s to what?", compopt[opt_indx].name);
4687 abuf[0] = '\0';
4688 getlin(buf, abuf);
4689 if (abuf[0] == '\033')
4690 continue;
4691 Sprintf(buf, "%s:", compopt[opt_indx].name);
4692 (void) strncat(eos(buf), abuf,
4693 (sizeof buf - 1 - strlen(buf)));
4694 /* pass the buck */
4695 (void) parseoptions(buf, setinitial, fromfile);
4696 }
4697 if (wc_supported(compopt[opt_indx].name)
4698 || wc2_supported(compopt[opt_indx].name))
4699 preference_update(compopt[opt_indx].name);
4700 }
4701 }
4702 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4703 }
4704
4705 destroy_nhwindow(tmpwin);
4706 if (need_redraw) {
4707 check_gold_symbol();
4708 reglyph_darkroom();
4709 (void) doredraw();
4710 }
4711 return 0;
4712 }
4713
4714 /* common to msg-types, menu-colors, autopickup-exceptions */
4715 STATIC_OVL int
handle_add_list_remove(optname,numtotal)4716 handle_add_list_remove(optname, numtotal)
4717 const char *optname;
4718 int numtotal;
4719 {
4720 winid tmpwin;
4721 anything any;
4722 int i, pick_cnt, opt_idx;
4723 menu_item *pick_list = (menu_item *) 0;
4724 static const struct action {
4725 char letr;
4726 const char *desc;
4727 } action_titles[] = {
4728 { 'a', "add new %s" }, /* [0] */
4729 { 'l', "list %s" }, /* [1] */
4730 { 'r', "remove existing %s" }, /* [2] */
4731 { 'x', "exit this menu" }, /* [3] */
4732 };
4733
4734 opt_idx = 0;
4735 tmpwin = create_nhwindow(NHW_MENU);
4736 start_menu(tmpwin);
4737 any = zeroany;
4738 for (i = 0; i < SIZE(action_titles); i++) {
4739 char tmpbuf[BUFSZ];
4740
4741 any.a_int++;
4742 /* omit list and remove if there aren't any yet */
4743 if (!numtotal && (i == 1 || i == 2))
4744 continue;
4745 Sprintf(tmpbuf, action_titles[i].desc,
4746 (i == 1) ? makeplural(optname) : optname);
4747 add_menu(tmpwin, NO_GLYPH, &any, action_titles[i].letr, 0, ATR_NONE,
4748 tmpbuf, (i == 3) ? MENU_SELECTED : MENU_UNSELECTED);
4749 }
4750 end_menu(tmpwin, "Do what?");
4751 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
4752 opt_idx = pick_list[0].item.a_int - 1;
4753 if (pick_cnt > 1 && opt_idx == 3)
4754 opt_idx = pick_list[1].item.a_int - 1;
4755 free((genericptr_t) pick_list);
4756 } else
4757 opt_idx = 3; /* none selected, exit menu */
4758 destroy_nhwindow(tmpwin);
4759 return opt_idx;
4760 }
4761
4762 struct symsetentry *symset_list = 0; /* files.c will populate this with
4763 * list of available sets */
4764
4765 STATIC_OVL boolean
special_handling(optname,setinitial,setfromfile)4766 special_handling(optname, setinitial, setfromfile)
4767 const char *optname;
4768 boolean setinitial, setfromfile;
4769 {
4770 winid tmpwin;
4771 anything any;
4772 int i, n;
4773 char buf[BUFSZ];
4774
4775 /* Special handling of menustyle, pickup_burden, pickup_types,
4776 * disclose, runmode, msg_window, menu_headings, sortloot,
4777 * and number_pad options.
4778 * Also takes care of interactive autopickup_exception_handling changes.
4779 */
4780 if (!strcmp("menustyle", optname)) {
4781 const char *style_name;
4782 menu_item *style_pick = (menu_item *) 0;
4783
4784 tmpwin = create_nhwindow(NHW_MENU);
4785 start_menu(tmpwin);
4786 any = zeroany;
4787 for (i = 0; i < SIZE(menutype); i++) {
4788 style_name = menutype[i];
4789 /* note: separate `style_name' variable used
4790 to avoid an optimizer bug in VAX C V2.3 */
4791 any.a_int = i + 1;
4792 add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, ATR_NONE,
4793 style_name, MENU_UNSELECTED);
4794 }
4795 end_menu(tmpwin, "Select menustyle:");
4796 if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
4797 flags.menu_style = style_pick->item.a_int - 1;
4798 free((genericptr_t) style_pick);
4799 }
4800 destroy_nhwindow(tmpwin);
4801 } else if (!strcmp("paranoid_confirmation", optname)) {
4802 menu_item *paranoia_picks = (menu_item *) 0;
4803
4804 tmpwin = create_nhwindow(NHW_MENU);
4805 start_menu(tmpwin);
4806 any = zeroany;
4807 for (i = 0; paranoia[i].flagmask != 0; ++i) {
4808 if (paranoia[i].flagmask == PARANOID_BONES && !wizard)
4809 continue;
4810 any.a_int = paranoia[i].flagmask;
4811 add_menu(tmpwin, NO_GLYPH, &any, *paranoia[i].argname, 0,
4812 ATR_NONE, paranoia[i].explain,
4813 (flags.paranoia_bits & paranoia[i].flagmask)
4814 ? MENU_SELECTED
4815 : MENU_UNSELECTED);
4816 }
4817 end_menu(tmpwin, "Actions requiring extra confirmation:");
4818 i = select_menu(tmpwin, PICK_ANY, ¶noia_picks);
4819 if (i >= 0) {
4820 /* player didn't cancel; we reset all the paranoia options
4821 here even if there were no items picked, since user
4822 could have toggled off preselected ones to end up with 0 */
4823 flags.paranoia_bits = 0;
4824 if (i > 0) {
4825 /* at least 1 item set, either preselected or newly picked */
4826 while (--i >= 0)
4827 flags.paranoia_bits |= paranoia_picks[i].item.a_int;
4828 free((genericptr_t) paranoia_picks);
4829 }
4830 }
4831 destroy_nhwindow(tmpwin);
4832 } else if (!strcmp("pickup_burden", optname)) {
4833 const char *burden_name, *burden_letters = "ubsntl";
4834 menu_item *burden_pick = (menu_item *) 0;
4835
4836 tmpwin = create_nhwindow(NHW_MENU);
4837 start_menu(tmpwin);
4838 any = zeroany;
4839 for (i = 0; i < SIZE(burdentype); i++) {
4840 burden_name = burdentype[i];
4841 any.a_int = i + 1;
4842 add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, ATR_NONE,
4843 burden_name, MENU_UNSELECTED);
4844 }
4845 end_menu(tmpwin, "Select encumbrance level:");
4846 if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
4847 flags.pickup_burden = burden_pick->item.a_int - 1;
4848 free((genericptr_t) burden_pick);
4849 }
4850 destroy_nhwindow(tmpwin);
4851 } else if (!strcmp("pickup_types", optname)) {
4852 /* parseoptions will prompt for the list of types */
4853 (void) parseoptions(strcpy(buf, "pickup_types"),
4854 setinitial, setfromfile);
4855 } else if (!strcmp("disclose", optname)) {
4856 /* order of disclose_names[] must correspond to
4857 disclosure_options in decl.c */
4858 static const char *disclosure_names[] = {
4859 "inventory", "attributes", "vanquished",
4860 "genocides", "conduct", "overview",
4861 };
4862 int disc_cat[NUM_DISCLOSURE_OPTIONS];
4863 int pick_cnt, pick_idx, opt_idx;
4864 char c;
4865 menu_item *disclosure_pick = (menu_item *) 0;
4866
4867 tmpwin = create_nhwindow(NHW_MENU);
4868 start_menu(tmpwin);
4869 any = zeroany;
4870 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4871 Sprintf(buf, "%-12s[%c%c]", disclosure_names[i],
4872 flags.end_disclose[i], disclosure_options[i]);
4873 any.a_int = i + 1;
4874 add_menu(tmpwin, NO_GLYPH, &any, disclosure_options[i], 0,
4875 ATR_NONE, buf, MENU_UNSELECTED);
4876 disc_cat[i] = 0;
4877 }
4878 end_menu(tmpwin, "Change which disclosure options categories:");
4879 pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_pick);
4880 if (pick_cnt > 0) {
4881 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
4882 opt_idx = disclosure_pick[pick_idx].item.a_int - 1;
4883 disc_cat[opt_idx] = 1;
4884 }
4885 free((genericptr_t) disclosure_pick);
4886 disclosure_pick = (menu_item *) 0;
4887 }
4888 destroy_nhwindow(tmpwin);
4889
4890 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4891 if (disc_cat[i]) {
4892 c = flags.end_disclose[i];
4893 Sprintf(buf, "Disclosure options for %s:",
4894 disclosure_names[i]);
4895 tmpwin = create_nhwindow(NHW_MENU);
4896 start_menu(tmpwin);
4897 any = zeroany;
4898 /* 'y','n',and '+' work as alternate selectors; '-' doesn't */
4899 any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
4900 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4901 "Never disclose, without prompting",
4902 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4903 any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
4904 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4905 "Always disclose, without prompting",
4906 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4907 if (*disclosure_names[i] == 'v') {
4908 any.a_char = DISCLOSE_SPECIAL_WITHOUT_PROMPT; /* '#' */
4909 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4910 "Always disclose, pick sort order from menu",
4911 (c == any.a_char) ? MENU_SELECTED
4912 : MENU_UNSELECTED);
4913 }
4914 any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
4915 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4916 "Prompt, with default answer of \"No\"",
4917 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4918 any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
4919 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4920 "Prompt, with default answer of \"Yes\"",
4921 (c == any.a_char) ? MENU_SELECTED : MENU_UNSELECTED);
4922 if (*disclosure_names[i] == 'v') {
4923 any.a_char = DISCLOSE_PROMPT_DEFAULT_SPECIAL; /* '?' */
4924 add_menu(tmpwin, NO_GLYPH, &any, 0, any.a_char, ATR_NONE,
4925 "Prompt, with default answer of \"Ask\" to request sort menu",
4926 (c == any.a_char) ? MENU_SELECTED
4927 : MENU_UNSELECTED);
4928 }
4929 end_menu(tmpwin, buf);
4930 n = select_menu(tmpwin, PICK_ONE, &disclosure_pick);
4931 if (n > 0) {
4932 flags.end_disclose[i] = disclosure_pick[0].item.a_char;
4933 if (n > 1 && flags.end_disclose[i] == c)
4934 flags.end_disclose[i] = disclosure_pick[1].item.a_char;
4935 free((genericptr_t) disclosure_pick);
4936 }
4937 destroy_nhwindow(tmpwin);
4938 }
4939 }
4940 } else if (!strcmp("runmode", optname)) {
4941 const char *mode_name;
4942 menu_item *mode_pick = (menu_item *) 0;
4943
4944 tmpwin = create_nhwindow(NHW_MENU);
4945 start_menu(tmpwin);
4946 any = zeroany;
4947 for (i = 0; i < SIZE(runmodes); i++) {
4948 mode_name = runmodes[i];
4949 any.a_int = i + 1;
4950 add_menu(tmpwin, NO_GLYPH, &any, *mode_name, 0, ATR_NONE,
4951 mode_name, MENU_UNSELECTED);
4952 }
4953 end_menu(tmpwin, "Select run/travel display mode:");
4954 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4955 flags.runmode = mode_pick->item.a_int - 1;
4956 free((genericptr_t) mode_pick);
4957 }
4958 destroy_nhwindow(tmpwin);
4959 } else if (!strcmp("whatis_coord", optname)) {
4960 menu_item *window_pick = (menu_item *) 0;
4961 int pick_cnt;
4962 char gp = iflags.getpos_coords;
4963
4964 tmpwin = create_nhwindow(NHW_MENU);
4965 start_menu(tmpwin);
4966 any = zeroany;
4967 any.a_char = GPCOORDS_COMPASS;
4968 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMPASS, 0, ATR_NONE,
4969 "compass ('east' or '3s' or '2n,4w')",
4970 (gp == GPCOORDS_COMPASS) ? MENU_SELECTED : MENU_UNSELECTED);
4971 any.a_char = GPCOORDS_COMFULL;
4972 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_COMFULL, 0, ATR_NONE,
4973 "full compass ('east' or '3south' or '2north,4west')",
4974 (gp == GPCOORDS_COMFULL) ? MENU_SELECTED : MENU_UNSELECTED);
4975 any.a_char = GPCOORDS_MAP;
4976 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_MAP, 0, ATR_NONE,
4977 "map <x,y>",
4978 (gp == GPCOORDS_MAP) ? MENU_SELECTED : MENU_UNSELECTED);
4979 any.a_char = GPCOORDS_SCREEN;
4980 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_SCREEN, 0, ATR_NONE,
4981 "screen [row,column]",
4982 (gp == GPCOORDS_SCREEN) ? MENU_SELECTED : MENU_UNSELECTED);
4983 any.a_char = GPCOORDS_NONE;
4984 add_menu(tmpwin, NO_GLYPH, &any, GPCOORDS_NONE, 0, ATR_NONE,
4985 "none (no coordinates displayed)",
4986 (gp == GPCOORDS_NONE) ? MENU_SELECTED : MENU_UNSELECTED);
4987 any.a_long = 0L;
4988 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
4989 Sprintf(buf, "map: upper-left: <%d,%d>, lower-right: <%d,%d>%s",
4990 1, 0, COLNO - 1, ROWNO - 1,
4991 flags.verbose ? "; column 0 unused, off left edge" : "");
4992 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
4993 if (strcmp(windowprocs.name, "tty")) /* only show for non-tty */
4994 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
4995 "screen: row is offset to accommodate tty interface's use of top line",
4996 MENU_UNSELECTED);
4997 #if COLNO == 80
4998 #define COL80ARG flags.verbose ? "; column 80 is not used" : ""
4999 #else
5000 #define COL80ARG ""
5001 #endif
5002 Sprintf(buf, "screen: upper-left: [%02d,%02d], lower-right: [%d,%d]%s",
5003 0 + 2, 1, ROWNO - 1 + 2, COLNO - 1, COL80ARG);
5004 #undef COL80ARG
5005 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED);
5006 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
5007 end_menu(tmpwin,
5008 "Select coordinate display when auto-describing a map position:");
5009 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
5010 iflags.getpos_coords = window_pick[0].item.a_char;
5011 /* PICK_ONE doesn't unselect preselected entry when
5012 selecting another one */
5013 if (pick_cnt > 1 && iflags.getpos_coords == gp)
5014 iflags.getpos_coords = window_pick[1].item.a_char;
5015 free((genericptr_t) window_pick);
5016 }
5017 destroy_nhwindow(tmpwin);
5018 } else if (!strcmp("whatis_filter", optname)) {
5019 menu_item *window_pick = (menu_item *) 0;
5020 int pick_cnt;
5021 char gf = iflags.getloc_filter;
5022
5023 tmpwin = create_nhwindow(NHW_MENU);
5024 start_menu(tmpwin);
5025 any = zeroany;
5026 any.a_char = (GFILTER_NONE + 1);
5027 add_menu(tmpwin, NO_GLYPH, &any, 'n',
5028 0, ATR_NONE, "no filtering",
5029 (gf == GFILTER_NONE) ? MENU_SELECTED : MENU_UNSELECTED);
5030 any.a_char = (GFILTER_VIEW + 1);
5031 add_menu(tmpwin, NO_GLYPH, &any, 'v',
5032 0, ATR_NONE, "in view only",
5033 (gf == GFILTER_VIEW) ? MENU_SELECTED : MENU_UNSELECTED);
5034 any.a_char = (GFILTER_AREA + 1);
5035 add_menu(tmpwin, NO_GLYPH, &any, 'a',
5036 0, ATR_NONE, "in same area",
5037 (gf == GFILTER_AREA) ? MENU_SELECTED : MENU_UNSELECTED);
5038 end_menu(tmpwin,
5039 "Select location filtering when going for next/previous map position:");
5040 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
5041 iflags.getloc_filter = (window_pick[0].item.a_char - 1);
5042 /* PICK_ONE doesn't unselect preselected entry when
5043 selecting another one */
5044 if (pick_cnt > 1 && iflags.getloc_filter == gf)
5045 iflags.getloc_filter = (window_pick[1].item.a_char - 1);
5046 free((genericptr_t) window_pick);
5047 }
5048 destroy_nhwindow(tmpwin);
5049 } else if (!strcmp("msg_window", optname)) {
5050 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
5051 if (WINDOWPORT("tty") || WINDOWPORT("curses")) {
5052 /* by Christian W. Cooper */
5053 menu_item *window_pick = (menu_item *) 0;
5054
5055 tmpwin = create_nhwindow(NHW_MENU);
5056 start_menu(tmpwin);
5057 any = zeroany;
5058 if (!WINDOWPORT("curses")) {
5059 any.a_char = 's';
5060 add_menu(tmpwin, NO_GLYPH, &any, 's', 0, ATR_NONE,
5061 "single", MENU_UNSELECTED);
5062 any.a_char = 'c';
5063 add_menu(tmpwin, NO_GLYPH, &any, 'c', 0, ATR_NONE,
5064 "combination", MENU_UNSELECTED);
5065 }
5066 any.a_char = 'f';
5067 add_menu(tmpwin, NO_GLYPH, &any, 'f', 0, ATR_NONE, "full",
5068 MENU_UNSELECTED);
5069 any.a_char = 'r';
5070 add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "reversed",
5071 MENU_UNSELECTED);
5072 end_menu(tmpwin, "Select message history display type:");
5073 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
5074 iflags.prevmsg_window = window_pick->item.a_char;
5075 free((genericptr_t) window_pick);
5076 }
5077 destroy_nhwindow(tmpwin);
5078 } else
5079 #endif /* msg_window for tty or curses */
5080 pline("'%s' option is not supported for '%s'.",
5081 optname, windowprocs.name);
5082 } else if (!strcmp("sortloot", optname)) {
5083 const char *sortl_name;
5084 menu_item *sortl_pick = (menu_item *) 0;
5085
5086 tmpwin = create_nhwindow(NHW_MENU);
5087 start_menu(tmpwin);
5088 any = zeroany;
5089 for (i = 0; i < SIZE(sortltype); i++) {
5090 sortl_name = sortltype[i];
5091 any.a_char = *sortl_name;
5092 add_menu(tmpwin, NO_GLYPH, &any, *sortl_name, 0, ATR_NONE,
5093 sortl_name, (flags.sortloot == *sortl_name)
5094 ? MENU_SELECTED : MENU_UNSELECTED);
5095 }
5096 end_menu(tmpwin, "Select loot sorting type:");
5097 n = select_menu(tmpwin, PICK_ONE, &sortl_pick);
5098 if (n > 0) {
5099 char c = sortl_pick[0].item.a_char;
5100
5101 if (n > 1 && c == flags.sortloot)
5102 c = sortl_pick[1].item.a_char;
5103 flags.sortloot = c;
5104 free((genericptr_t) sortl_pick);
5105 }
5106 destroy_nhwindow(tmpwin);
5107 } else if (!strcmp("align_message", optname)
5108 || !strcmp("align_status", optname)) {
5109 menu_item *window_pick = (menu_item *) 0;
5110 char abuf[BUFSZ];
5111 boolean msg = (*(optname + 6) == 'm');
5112
5113 tmpwin = create_nhwindow(NHW_MENU);
5114 start_menu(tmpwin);
5115 any = zeroany;
5116 any.a_int = ALIGN_TOP;
5117 add_menu(tmpwin, NO_GLYPH, &any, 't', 0, ATR_NONE, "top",
5118 MENU_UNSELECTED);
5119 any.a_int = ALIGN_BOTTOM;
5120 add_menu(tmpwin, NO_GLYPH, &any, 'b', 0, ATR_NONE, "bottom",
5121 MENU_UNSELECTED);
5122 any.a_int = ALIGN_LEFT;
5123 add_menu(tmpwin, NO_GLYPH, &any, 'l', 0, ATR_NONE, "left",
5124 MENU_UNSELECTED);
5125 any.a_int = ALIGN_RIGHT;
5126 add_menu(tmpwin, NO_GLYPH, &any, 'r', 0, ATR_NONE, "right",
5127 MENU_UNSELECTED);
5128 Sprintf(abuf, "Select %s window placement relative to the map:",
5129 msg ? "message" : "status");
5130 end_menu(tmpwin, abuf);
5131 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
5132 if (msg)
5133 iflags.wc_align_message = window_pick->item.a_int;
5134 else
5135 iflags.wc_align_status = window_pick->item.a_int;
5136 free((genericptr_t) window_pick);
5137 }
5138 destroy_nhwindow(tmpwin);
5139 } else if (!strcmp("number_pad", optname)) {
5140 static const char *npchoices[] = {
5141 " 0 (off)", " 1 (on)", " 2 (on, MSDOS compatible)",
5142 " 3 (on, phone-style digit layout)",
5143 " 4 (on, phone-style layout, MSDOS compatible)",
5144 "-1 (off, 'z' to move upper-left, 'y' to zap wands)"
5145 };
5146 menu_item *mode_pick = (menu_item *) 0;
5147
5148 tmpwin = create_nhwindow(NHW_MENU);
5149 start_menu(tmpwin);
5150 any = zeroany;
5151 for (i = 0; i < SIZE(npchoices); i++) {
5152 any.a_int = i + 1;
5153 add_menu(tmpwin, NO_GLYPH, &any, 'a' + i, 0, ATR_NONE,
5154 npchoices[i], MENU_UNSELECTED);
5155 }
5156 end_menu(tmpwin, "Select number_pad mode:");
5157 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
5158 switch (mode_pick->item.a_int - 1) {
5159 case 0:
5160 iflags.num_pad = FALSE;
5161 iflags.num_pad_mode = 0;
5162 break;
5163 case 1:
5164 iflags.num_pad = TRUE;
5165 iflags.num_pad_mode = 0;
5166 break;
5167 case 2:
5168 iflags.num_pad = TRUE;
5169 iflags.num_pad_mode = 1;
5170 break;
5171 case 3:
5172 iflags.num_pad = TRUE;
5173 iflags.num_pad_mode = 2;
5174 break;
5175 case 4:
5176 iflags.num_pad = TRUE;
5177 iflags.num_pad_mode = 3;
5178 break;
5179 /* last menu choice: number_pad == -1 */
5180 case 5:
5181 iflags.num_pad = FALSE;
5182 iflags.num_pad_mode = 1;
5183 break;
5184 }
5185 reset_commands(FALSE);
5186 number_pad(iflags.num_pad ? 1 : 0);
5187 free((genericptr_t) mode_pick);
5188 }
5189 destroy_nhwindow(tmpwin);
5190 } else if (!strcmp("menu_headings", optname)) {
5191 int mhattr = query_attr("How to highlight menu headings:");
5192
5193 if (mhattr != -1)
5194 iflags.menu_headings = mhattr;
5195 } else if (!strcmp("msgtype", optname)) {
5196 int opt_idx, nmt, mttyp;
5197 char mtbuf[BUFSZ];
5198
5199 msgtypes_again:
5200 nmt = msgtype_count();
5201 opt_idx = handle_add_list_remove("message type", nmt);
5202 if (opt_idx == 3) { /* done */
5203 return TRUE;
5204 } else if (opt_idx == 0) { /* add new */
5205 mtbuf[0] = '\0';
5206 getlin("What new message pattern?", mtbuf);
5207 if (*mtbuf == '\033')
5208 return TRUE;
5209 if (*mtbuf
5210 && test_regex_pattern(mtbuf, (const char *)0)
5211 && (mttyp = query_msgtype()) != -1
5212 && !msgtype_add(mttyp, mtbuf)) {
5213 pline("Error adding the message type.");
5214 wait_synch();
5215 }
5216 goto msgtypes_again;
5217 } else { /* list (1) or remove (2) */
5218 int pick_idx, pick_cnt;
5219 int mt_idx;
5220 unsigned ln;
5221 const char *mtype;
5222 menu_item *pick_list = (menu_item *) 0;
5223 struct plinemsg_type *tmp = plinemsg_types;
5224
5225 tmpwin = create_nhwindow(NHW_MENU);
5226 start_menu(tmpwin);
5227 any = zeroany;
5228 mt_idx = 0;
5229 while (tmp) {
5230 mtype = msgtype2name(tmp->msgtype);
5231 any.a_int = ++mt_idx;
5232 Sprintf(mtbuf, "%-5s \"", mtype);
5233 ln = sizeof mtbuf - strlen(mtbuf) - sizeof "\"";
5234 if (strlen(tmp->pattern) > ln)
5235 Strcat(strncat(mtbuf, tmp->pattern, ln - 3), "...\"");
5236 else
5237 Strcat(strcat(mtbuf, tmp->pattern), "\"");
5238 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mtbuf,
5239 MENU_UNSELECTED);
5240 tmp = tmp->next;
5241 }
5242 Sprintf(mtbuf, "%s message types",
5243 (opt_idx == 1) ? "List of" : "Remove which");
5244 end_menu(tmpwin, mtbuf);
5245 pick_cnt = select_menu(tmpwin,
5246 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
5247 &pick_list);
5248 if (pick_cnt > 0) {
5249 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
5250 free_one_msgtype(pick_list[pick_idx].item.a_int - 1
5251 - pick_idx);
5252 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
5253 }
5254 destroy_nhwindow(tmpwin);
5255 if (pick_cnt >= 0)
5256 goto msgtypes_again;
5257 }
5258 } else if (!strcmp("menu_colors", optname)) {
5259 int opt_idx, nmc, mcclr, mcattr;
5260 char mcbuf[BUFSZ];
5261
5262 menucolors_again:
5263 nmc = count_menucolors();
5264 opt_idx = handle_add_list_remove("menucolor", nmc);
5265 if (opt_idx == 3) { /* done */
5266 menucolors_done:
5267 /* in case we've made a change which impacts current persistent
5268 inventory window; we don't track whether an actual changed
5269 occurred, so just assume there was one and that it matters;
5270 if we're wrong, a redundant update is cheap... */
5271 if (iflags.use_menu_color)
5272 update_inventory();
5273
5274 /* menu colors aren't being used; if any are defined, remind
5275 player how to use them */
5276 else if (nmc > 0)
5277 pline(
5278 "To have menu colors become active, toggle 'menucolors' option to True.");
5279 return TRUE;
5280
5281 } else if (opt_idx == 0) { /* add new */
5282 mcbuf[0] = '\0';
5283 getlin("What new menucolor pattern?", mcbuf);
5284 if (*mcbuf == '\033')
5285 goto menucolors_done;
5286 if (*mcbuf
5287 && test_regex_pattern(mcbuf, (const char *)0)
5288 && (mcclr = query_color((char *) 0)) != -1
5289 && (mcattr = query_attr((char *) 0)) != -1
5290 && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) {
5291 pline("Error adding the menu color.");
5292 wait_synch();
5293 }
5294 goto menucolors_again;
5295
5296 } else { /* list (1) or remove (2) */
5297 int pick_idx, pick_cnt;
5298 int mc_idx;
5299 unsigned ln;
5300 const char *sattr, *sclr;
5301 menu_item *pick_list = (menu_item *) 0;
5302 struct menucoloring *tmp = menu_colorings;
5303 char clrbuf[QBUFSZ];
5304
5305 tmpwin = create_nhwindow(NHW_MENU);
5306 start_menu(tmpwin);
5307 any = zeroany;
5308 mc_idx = 0;
5309 while (tmp) {
5310 sattr = attr2attrname(tmp->attr);
5311 sclr = strcpy(clrbuf, clr2colorname(tmp->color));
5312 (void) strNsubst(clrbuf, " ", "-", 0);
5313 any.a_int = ++mc_idx;
5314 /* construct suffix */
5315 Sprintf(buf, "\"\"=%s%s%s", sclr,
5316 (tmp->attr != ATR_NONE) ? "&" : "",
5317 (tmp->attr != ATR_NONE) ? sattr : "");
5318 /* now main string */
5319 ln = sizeof buf - strlen(buf) - 1; /* length available */
5320 Strcpy(mcbuf, "\"");
5321 if (strlen(tmp->origstr) > ln)
5322 Strcat(strncat(mcbuf, tmp->origstr, ln - 3), "...");
5323 else
5324 Strcat(mcbuf, tmp->origstr);
5325 /* combine main string and suffix */
5326 Strcat(mcbuf, &buf[1]); /* skip buf[]'s initial quote */
5327 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, mcbuf,
5328 MENU_UNSELECTED);
5329 tmp = tmp->next;
5330 }
5331 Sprintf(mcbuf, "%s menu colors",
5332 (opt_idx == 1) ? "List of" : "Remove which");
5333 end_menu(tmpwin, mcbuf);
5334 pick_cnt = select_menu(tmpwin,
5335 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
5336 &pick_list);
5337 if (pick_cnt > 0) {
5338 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
5339 free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1
5340 - pick_idx);
5341 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
5342 }
5343 destroy_nhwindow(tmpwin);
5344 if (pick_cnt >= 0)
5345 goto menucolors_again;
5346 }
5347 } else if (!strcmp("autopickup_exception", optname)) {
5348 int opt_idx, numapes = 0;
5349 char apebuf[2 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */
5350 struct autopickup_exception *ape;
5351
5352 ape_again:
5353 numapes = count_apes();
5354 opt_idx = handle_add_list_remove("autopickup exception", numapes);
5355 if (opt_idx == 3) { /* done */
5356 return TRUE;
5357 } else if (opt_idx == 0) { /* add new */
5358 /* EDIT_GETLIN: assume user doesn't user want previous
5359 exception used as default input string for this one... */
5360 apebuf[0] = apebuf[1] = '\0';
5361 getlin("What new autopickup exception pattern?", &apebuf[1]);
5362 mungspaces(&apebuf[1]); /* regularize whitespace */
5363 if (apebuf[1] == '\033')
5364 return TRUE;
5365 if (apebuf[1]) {
5366 apebuf[0] = '\"';
5367 /* guarantee room for \" prefix and \"\0 suffix;
5368 -2 is good enough for apebuf[] but -3 makes
5369 sure the whole thing fits within normal BUFSZ */
5370 apebuf[sizeof apebuf - 2] = '\0';
5371 Strcat(apebuf, "\"");
5372 add_autopickup_exception(apebuf);
5373 }
5374 goto ape_again;
5375 } else { /* list (1) or remove (2) */
5376 int pick_idx, pick_cnt;
5377 menu_item *pick_list = (menu_item *) 0;
5378
5379 tmpwin = create_nhwindow(NHW_MENU);
5380 start_menu(tmpwin);
5381 if (numapes) {
5382 ape = apelist;
5383 any = zeroany;
5384 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
5385 "Always pickup '<'; never pickup '>'",
5386 MENU_UNSELECTED);
5387 for (i = 0; i < numapes && ape; i++) {
5388 any.a_void = (opt_idx == 1) ? 0 : ape;
5389 /* length of pattern plus quotes (plus '<'/'>') is
5390 less than BUFSZ */
5391 Sprintf(apebuf, "\"%c%s\"", ape->grab ? '<' : '>',
5392 ape->pattern);
5393 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, apebuf,
5394 MENU_UNSELECTED);
5395 ape = ape->next;
5396 }
5397 }
5398 Sprintf(apebuf, "%s autopickup exceptions",
5399 (opt_idx == 1) ? "List of" : "Remove which");
5400 end_menu(tmpwin, apebuf);
5401 pick_cnt = select_menu(tmpwin,
5402 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
5403 &pick_list);
5404 if (pick_cnt > 0) {
5405 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
5406 remove_autopickup_exception(
5407 (struct autopickup_exception *)
5408 pick_list[pick_idx].item.a_void);
5409 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
5410 }
5411 destroy_nhwindow(tmpwin);
5412 if (pick_cnt >= 0)
5413 goto ape_again;
5414 }
5415 } else if (!strcmp("symset", optname)
5416 || !strcmp("roguesymset", optname)) {
5417 menu_item *symset_pick = (menu_item *) 0;
5418 boolean rogueflag = (*optname == 'r'),
5419 ready_to_switch = FALSE,
5420 nothing_to_do = FALSE;
5421 char *symset_name, fmtstr[20];
5422 struct symsetentry *sl;
5423 int res, which_set, setcount = 0, chosen = -2, defindx = 0;
5424
5425 which_set = rogueflag ? ROGUESET : PRIMARY;
5426 symset_list = (struct symsetentry *) 0;
5427 /* clear symset[].name as a flag to read_sym_file() to build list */
5428 symset_name = symset[which_set].name;
5429 symset[which_set].name = (char *) 0;
5430
5431 res = read_sym_file(which_set);
5432 /* put symset name back */
5433 symset[which_set].name = symset_name;
5434
5435 if (res && symset_list) {
5436 int thissize,
5437 biggest = (int) (sizeof "Default Symbols" - sizeof ""),
5438 big_desc = 0;
5439
5440 for (sl = symset_list; sl; sl = sl->next) {
5441 /* check restrictions */
5442 if (rogueflag ? sl->primary : sl->rogue)
5443 continue;
5444 #ifndef MAC_GRAPHICS_ENV
5445 if (sl->handling == H_MAC)
5446 continue;
5447 #endif
5448
5449 setcount++;
5450 /* find biggest name */
5451 thissize = sl->name ? (int) strlen(sl->name) : 0;
5452 if (thissize > biggest)
5453 biggest = thissize;
5454 thissize = sl->desc ? (int) strlen(sl->desc) : 0;
5455 if (thissize > big_desc)
5456 big_desc = thissize;
5457 }
5458 if (!setcount) {
5459 pline("There are no appropriate %s symbol sets available.",
5460 rogueflag ? "rogue level" : "primary");
5461 return TRUE;
5462 }
5463
5464 Sprintf(fmtstr, "%%-%ds %%s", biggest + 2);
5465 tmpwin = create_nhwindow(NHW_MENU);
5466 start_menu(tmpwin);
5467 any = zeroany;
5468 any.a_int = 1; /* -1 + 2 [see 'if (sl->name) {' below]*/
5469 if (!symset_name)
5470 defindx = any.a_int;
5471 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
5472 "Default Symbols",
5473 (any.a_int == defindx) ? MENU_SELECTED
5474 : MENU_UNSELECTED);
5475
5476 for (sl = symset_list; sl; sl = sl->next) {
5477 /* check restrictions */
5478 if (rogueflag ? sl->primary : sl->rogue)
5479 continue;
5480 #ifndef MAC_GRAPHICS_ENV
5481 if (sl->handling == H_MAC)
5482 continue;
5483 #endif
5484 if (sl->name) {
5485 /* +2: sl->idx runs from 0 to N-1 for N symsets;
5486 +1 because Defaults are implicitly in slot [0];
5487 +1 again so that valid data is never 0 */
5488 any.a_int = sl->idx + 2;
5489 if (symset_name && !strcmpi(sl->name, symset_name))
5490 defindx = any.a_int;
5491 Sprintf(buf, fmtstr, sl->name, sl->desc ? sl->desc : "");
5492 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf,
5493 (any.a_int == defindx) ? MENU_SELECTED
5494 : MENU_UNSELECTED);
5495 }
5496 }
5497 Sprintf(buf, "Select %ssymbol set:",
5498 rogueflag ? "rogue level " : "");
5499 end_menu(tmpwin, buf);
5500 n = select_menu(tmpwin, PICK_ONE, &symset_pick);
5501 if (n > 0) {
5502 chosen = symset_pick[0].item.a_int;
5503 /* if picking non-preselected entry yields 2, make sure
5504 that we're going with the non-preselected one */
5505 if (n == 2 && chosen == defindx)
5506 chosen = symset_pick[1].item.a_int;
5507 chosen -= 2; /* convert menu index to symset index;
5508 * "Default symbols" have index -1 */
5509 free((genericptr_t) symset_pick);
5510 } else if (n == 0 && defindx > 0) {
5511 chosen = defindx - 2;
5512 }
5513 destroy_nhwindow(tmpwin);
5514
5515 if (chosen > -1) {
5516 /* chose an actual symset name from file */
5517 for (sl = symset_list; sl; sl = sl->next)
5518 if (sl->idx == chosen)
5519 break;
5520 if (sl) {
5521 /* free the now stale attributes */
5522 clear_symsetentry(which_set, TRUE);
5523
5524 /* transfer only the name of the symbol set */
5525 symset[which_set].name = dupstr(sl->name);
5526 ready_to_switch = TRUE;
5527 }
5528 } else if (chosen == -1) {
5529 /* explicit selection of defaults */
5530 /* free the now stale symset attributes */
5531 clear_symsetentry(which_set, TRUE);
5532 } else
5533 nothing_to_do = TRUE;
5534 } else if (!res) {
5535 /* The symbols file could not be accessed */
5536 pline("Unable to access \"%s\" file.", SYMBOLS);
5537 return TRUE;
5538 } else if (!symset_list) {
5539 /* The symbols file was empty */
5540 pline("There were no symbol sets found in \"%s\".", SYMBOLS);
5541 return TRUE;
5542 }
5543
5544 /* clean up */
5545 while ((sl = symset_list) != 0) {
5546 symset_list = sl->next;
5547 if (sl->name)
5548 free((genericptr_t) sl->name), sl->name = (char *) 0;
5549 if (sl->desc)
5550 free((genericptr_t) sl->desc), sl->desc = (char *) 0;
5551 free((genericptr_t) sl);
5552 }
5553
5554 if (nothing_to_do)
5555 return TRUE;
5556
5557 /* Set default symbols and clear the handling value */
5558 if (rogueflag)
5559 init_rogue_symbols();
5560 else
5561 init_primary_symbols();
5562
5563 if (symset[which_set].name) {
5564 /* non-default symbols */
5565 if (read_sym_file(which_set)) {
5566 ready_to_switch = TRUE;
5567 } else {
5568 clear_symsetentry(which_set, TRUE);
5569 return TRUE;
5570 }
5571 }
5572
5573 if (ready_to_switch)
5574 switch_symbols(TRUE);
5575
5576 if (Is_rogue_level(&u.uz)) {
5577 if (rogueflag)
5578 assign_graphics(ROGUESET);
5579 } else if (!rogueflag)
5580 assign_graphics(PRIMARY);
5581 preference_update("symset");
5582 need_redraw = TRUE;
5583
5584 } else {
5585 /* didn't match any of the special options */
5586 return FALSE;
5587 }
5588 return TRUE;
5589 }
5590
5591 #define rolestring(val, array, field) \
5592 ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none)
5593
5594 /* This is ugly. We have all the option names in the compopt[] array,
5595 but we need to look at each option individually to get the value. */
5596 STATIC_OVL const char *
get_compopt_value(optname,buf)5597 get_compopt_value(optname, buf)
5598 const char *optname;
5599 char *buf;
5600 {
5601 static const char none[] = "(none)", randomrole[] = "random",
5602 to_be_done[] = "(to be done)",
5603 defopt[] = "default", defbrief[] = "def";
5604 char ocl[MAXOCLASSES + 1];
5605 int i;
5606
5607 buf[0] = '\0';
5608 if (!strcmp(optname, "align_message")
5609 || !strcmp(optname, "align_status")) {
5610 int which = !strcmp(optname, "align_status") ? iflags.wc_align_status
5611 : iflags.wc_align_message;
5612 Sprintf(buf, "%s",
5613 (which == ALIGN_TOP) ? "top"
5614 : (which == ALIGN_LEFT) ? "left"
5615 : (which == ALIGN_BOTTOM) ? "bottom"
5616 : (which == ALIGN_RIGHT) ? "right"
5617 : defopt);
5618 } else if (!strcmp(optname, "align"))
5619 Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj));
5620 #ifdef WIN32
5621 else if (!strcmp(optname, "altkeyhandler"))
5622 Sprintf(buf, "%s",
5623 iflags.altkeyhandler[0] ? iflags.altkeyhandler : "default");
5624 #endif
5625 #ifdef BACKWARD_COMPAT
5626 else if (!strcmp(optname, "boulder"))
5627 Sprintf(buf, "%c",
5628 ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
5629 ? ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
5630 : showsyms[(int) objects[BOULDER].oc_class + SYM_OFF_O]);
5631 #endif
5632 else if (!strcmp(optname, "catname"))
5633 Sprintf(buf, "%s", catname[0] ? catname : none);
5634 else if (!strcmp(optname, "disclose"))
5635 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
5636 if (i)
5637 (void) strkitten(buf, ' ');
5638 (void) strkitten(buf, flags.end_disclose[i]);
5639 (void) strkitten(buf, disclosure_options[i]);
5640 }
5641 else if (!strcmp(optname, "dogname"))
5642 Sprintf(buf, "%s", dogname[0] ? dogname : none);
5643 else if (!strcmp(optname, "dungeon"))
5644 Sprintf(buf, "%s", to_be_done);
5645 else if (!strcmp(optname, "effects"))
5646 Sprintf(buf, "%s", to_be_done);
5647 else if (!strcmp(optname, "font_map"))
5648 Sprintf(buf, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
5649 else if (!strcmp(optname, "font_message"))
5650 Sprintf(buf, "%s",
5651 iflags.wc_font_message ? iflags.wc_font_message : defopt);
5652 else if (!strcmp(optname, "font_status"))
5653 Sprintf(buf, "%s",
5654 iflags.wc_font_status ? iflags.wc_font_status : defopt);
5655 else if (!strcmp(optname, "font_menu"))
5656 Sprintf(buf, "%s",
5657 iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
5658 else if (!strcmp(optname, "font_text"))
5659 Sprintf(buf, "%s",
5660 iflags.wc_font_text ? iflags.wc_font_text : defopt);
5661 else if (!strcmp(optname, "font_size_map")) {
5662 if (iflags.wc_fontsiz_map)
5663 Sprintf(buf, "%d", iflags.wc_fontsiz_map);
5664 else
5665 Strcpy(buf, defopt);
5666 } else if (!strcmp(optname, "font_size_message")) {
5667 if (iflags.wc_fontsiz_message)
5668 Sprintf(buf, "%d", iflags.wc_fontsiz_message);
5669 else
5670 Strcpy(buf, defopt);
5671 } else if (!strcmp(optname, "font_size_status")) {
5672 if (iflags.wc_fontsiz_status)
5673 Sprintf(buf, "%d", iflags.wc_fontsiz_status);
5674 else
5675 Strcpy(buf, defopt);
5676 } else if (!strcmp(optname, "font_size_menu")) {
5677 if (iflags.wc_fontsiz_menu)
5678 Sprintf(buf, "%d", iflags.wc_fontsiz_menu);
5679 else
5680 Strcpy(buf, defopt);
5681 } else if (!strcmp(optname, "font_size_text")) {
5682 if (iflags.wc_fontsiz_text)
5683 Sprintf(buf, "%d", iflags.wc_fontsiz_text);
5684 else
5685 Strcpy(buf, defopt);
5686 } else if (!strcmp(optname, "fruit"))
5687 Sprintf(buf, "%s", pl_fruit);
5688 else if (!strcmp(optname, "gender"))
5689 Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj));
5690 else if (!strcmp(optname, "horsename"))
5691 Sprintf(buf, "%s", horsename[0] ? horsename : none);
5692 else if (!strcmp(optname, "map_mode")) {
5693 i = iflags.wc_map_mode;
5694 Sprintf(buf, "%s",
5695 (i == MAP_MODE_TILES) ? "tiles"
5696 : (i == MAP_MODE_ASCII4x6) ? "ascii4x6"
5697 : (i == MAP_MODE_ASCII6x8) ? "ascii6x8"
5698 : (i == MAP_MODE_ASCII8x8) ? "ascii8x8"
5699 : (i == MAP_MODE_ASCII16x8) ? "ascii16x8"
5700 : (i == MAP_MODE_ASCII7x12) ? "ascii7x12"
5701 : (i == MAP_MODE_ASCII8x12) ? "ascii8x12"
5702 : (i == MAP_MODE_ASCII16x12) ? "ascii16x12"
5703 : (i == MAP_MODE_ASCII12x16) ? "ascii12x16"
5704 : (i == MAP_MODE_ASCII10x18) ? "ascii10x18"
5705 : (i == MAP_MODE_ASCII_FIT_TO_SCREEN)
5706 ? "fit_to_screen"
5707 : defopt);
5708 } else if (!strcmp(optname, "menustyle"))
5709 Sprintf(buf, "%s", menutype[(int) flags.menu_style]);
5710 else if (!strcmp(optname, "menu_deselect_all"))
5711 Sprintf(buf, "%s", to_be_done);
5712 else if (!strcmp(optname, "menu_deselect_page"))
5713 Sprintf(buf, "%s", to_be_done);
5714 else if (!strcmp(optname, "menu_first_page"))
5715 Sprintf(buf, "%s", to_be_done);
5716 else if (!strcmp(optname, "menu_invert_all"))
5717 Sprintf(buf, "%s", to_be_done);
5718 else if (!strcmp(optname, "menu_headings"))
5719 Sprintf(buf, "%s", attr2attrname(iflags.menu_headings));
5720 else if (!strcmp(optname, "menu_invert_page"))
5721 Sprintf(buf, "%s", to_be_done);
5722 else if (!strcmp(optname, "menu_last_page"))
5723 Sprintf(buf, "%s", to_be_done);
5724 else if (!strcmp(optname, "menu_next_page"))
5725 Sprintf(buf, "%s", to_be_done);
5726 else if (!strcmp(optname, "menu_previous_page"))
5727 Sprintf(buf, "%s", to_be_done);
5728 else if (!strcmp(optname, "menu_search"))
5729 Sprintf(buf, "%s", to_be_done);
5730 else if (!strcmp(optname, "menu_select_all"))
5731 Sprintf(buf, "%s", to_be_done);
5732 else if (!strcmp(optname, "menu_select_page"))
5733 Sprintf(buf, "%s", to_be_done);
5734 else if (!strcmp(optname, "monsters")) {
5735 Sprintf(buf, "%s", to_be_done);
5736 } else if (!strcmp(optname, "msghistory")) {
5737 Sprintf(buf, "%u", iflags.msg_history);
5738 #ifdef TTY_GRAPHICS
5739 } else if (!strcmp(optname, "msg_window")) {
5740 Sprintf(buf, "%s", (iflags.prevmsg_window == 's') ? "single"
5741 : (iflags.prevmsg_window == 'c') ? "combination"
5742 : (iflags.prevmsg_window == 'f') ? "full"
5743 : "reversed");
5744 #endif
5745 } else if (!strcmp(optname, "name")) {
5746 Sprintf(buf, "%s", plname);
5747 } else if (!strcmp(optname, "mouse_support")) {
5748 #ifdef WIN32
5749 #define MOUSEFIX1 ", QuickEdit off"
5750 #define MOUSEFIX2 ", QuickEdit unchanged"
5751 #else
5752 #define MOUSEFIX1 ", O/S adjusted"
5753 #define MOUSEFIX2 ", O/S unchanged"
5754 #endif
5755 static const char *mousemodes[][2] = {
5756 { "0=off", "" },
5757 { "1=on", MOUSEFIX1 },
5758 { "2=on", MOUSEFIX2 },
5759 };
5760 #undef MOUSEFIX1
5761 #undef MOUSEFIX2
5762 int ms = iflags.wc_mouse_support;
5763
5764 if (ms >= 0 && ms <= 2)
5765 Sprintf(buf, "%s%s", mousemodes[ms][0], mousemodes[ms][1]);
5766 } else if (!strcmp(optname, "number_pad")) {
5767 static const char *numpadmodes[] = {
5768 "0=off", "1=on", "2=on, MSDOS compatible",
5769 "3=on, phone-style layout",
5770 "4=on, phone layout, MSDOS compatible",
5771 "-1=off, y & z swapped", /*[5]*/
5772 };
5773 int indx = Cmd.num_pad
5774 ? (Cmd.phone_layout ? (Cmd.pcHack_compat ? 4 : 3)
5775 : (Cmd.pcHack_compat ? 2 : 1))
5776 : Cmd.swap_yz ? 5 : 0;
5777
5778 Strcpy(buf, numpadmodes[indx]);
5779 } else if (!strcmp(optname, "objects")) {
5780 Sprintf(buf, "%s", to_be_done);
5781 } else if (!strcmp(optname, "packorder")) {
5782 oc_to_str(flags.inv_order, ocl);
5783 Sprintf(buf, "%s", ocl);
5784 #ifdef CHANGE_COLOR
5785 } else if (!strcmp(optname, "palette")) {
5786 Sprintf(buf, "%s", get_color_string());
5787 #endif
5788 } else if (!strcmp(optname, "paranoid_confirmation")) {
5789 char tmpbuf[QBUFSZ];
5790
5791 tmpbuf[0] = '\0';
5792 for (i = 0; paranoia[i].flagmask != 0; ++i)
5793 if (flags.paranoia_bits & paranoia[i].flagmask)
5794 Sprintf(eos(tmpbuf), " %s", paranoia[i].argname);
5795 Strcpy(buf, tmpbuf[0] ? &tmpbuf[1] : "none");
5796 } else if (!strcmp(optname, "petattr")) {
5797 #ifdef CURSES_GRAPHICS
5798 if (WINDOWPORT("curses")) {
5799 char tmpbuf[QBUFSZ];
5800
5801 Strcpy(buf, curses_fmt_attrs(tmpbuf));
5802 } else
5803 #endif
5804 if (iflags.wc2_petattr != 0)
5805 Sprintf(buf, "0x%08x", iflags.wc2_petattr);
5806 else
5807 Strcpy(buf, defopt);
5808 } else if (!strcmp(optname, "pettype")) {
5809 Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat"
5810 : (preferred_pet == 'd') ? "dog"
5811 : (preferred_pet == 'h') ? "horse"
5812 : (preferred_pet == 'n') ? "none"
5813 : "random");
5814 } else if (!strcmp(optname, "pickup_burden")) {
5815 Sprintf(buf, "%s", burdentype[flags.pickup_burden]);
5816 } else if (!strcmp(optname, "pickup_types")) {
5817 oc_to_str(flags.pickup_types, ocl);
5818 Sprintf(buf, "%s", ocl[0] ? ocl : "all");
5819 } else if (!strcmp(optname, "pile_limit")) {
5820 Sprintf(buf, "%d", flags.pile_limit);
5821 } else if (!strcmp(optname, "playmode")) {
5822 Strcpy(buf, wizard ? "debug" : discover ? "explore" : "normal");
5823 } else if (!strcmp(optname, "race")) {
5824 Sprintf(buf, "%s", rolestring(flags.initrace, races, noun));
5825 } else if (!strcmp(optname, "roguesymset")) {
5826 Sprintf(buf, "%s",
5827 symset[ROGUESET].name ? symset[ROGUESET].name : "default");
5828 if (currentgraphics == ROGUESET && symset[ROGUESET].name)
5829 Strcat(buf, ", active");
5830 } else if (!strcmp(optname, "role")) {
5831 Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m));
5832 } else if (!strcmp(optname, "runmode")) {
5833 Sprintf(buf, "%s", runmodes[flags.runmode]);
5834 } else if (!strcmp(optname, "whatis_coord")) {
5835 Sprintf(buf, "%s",
5836 (iflags.getpos_coords == GPCOORDS_MAP) ? "map"
5837 : (iflags.getpos_coords == GPCOORDS_COMPASS) ? "compass"
5838 : (iflags.getpos_coords == GPCOORDS_COMFULL) ? "full compass"
5839 : (iflags.getpos_coords == GPCOORDS_SCREEN) ? "screen"
5840 : "none");
5841 } else if (!strcmp(optname, "whatis_filter")) {
5842 Sprintf(buf, "%s",
5843 (iflags.getloc_filter == GFILTER_VIEW) ? "view"
5844 : (iflags.getloc_filter == GFILTER_AREA) ? "area"
5845 : "none");
5846 } else if (!strcmp(optname, "scores")) {
5847 Sprintf(buf, "%d top/%d around%s", flags.end_top, flags.end_around,
5848 flags.end_own ? "/own" : "");
5849 } else if (!strcmp(optname, "scroll_amount")) {
5850 if (iflags.wc_scroll_amount)
5851 Sprintf(buf, "%d", iflags.wc_scroll_amount);
5852 else
5853 Strcpy(buf, defopt);
5854 } else if (!strcmp(optname, "scroll_margin")) {
5855 if (iflags.wc_scroll_margin)
5856 Sprintf(buf, "%d", iflags.wc_scroll_margin);
5857 else
5858 Strcpy(buf, defopt);
5859 } else if (!strcmp(optname, "sortloot")) {
5860 for (i = 0; i < SIZE(sortltype); i++)
5861 if (flags.sortloot == sortltype[i][0]) {
5862 Strcpy(buf, sortltype[i]);
5863 break;
5864 }
5865 } else if (!strcmp(optname, "player_selection")) {
5866 Sprintf(buf, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
5867 #ifdef MSDOS
5868 } else if (!strcmp(optname, "soundcard")) {
5869 Sprintf(buf, "%s", to_be_done);
5870 #endif
5871 #ifdef STATUS_HILITES
5872 } else if (!strcmp("statushilites", optname)) {
5873 if (!iflags.hilite_delta)
5874 Strcpy(buf, "0 (off: don't highlight status fields)");
5875 else
5876 Sprintf(buf, "%ld (on: highlight status for %ld turns)",
5877 iflags.hilite_delta, iflags.hilite_delta);
5878 #endif
5879 } else if (!strcmp(optname,"statuslines")) {
5880 if (wc2_supported(optname))
5881 Strcpy(buf, (iflags.wc2_statuslines < 3) ? "2" : "3");
5882 /* else default to "unknown" */
5883 } else if (!strcmp(optname, "suppress_alert")) {
5884 if (flags.suppress_alert == 0L)
5885 Strcpy(buf, none);
5886 else
5887 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
5888 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
5889 } else if (!strcmp(optname, "symset")) {
5890 Sprintf(buf, "%s",
5891 symset[PRIMARY].name ? symset[PRIMARY].name : "default");
5892 if (currentgraphics == PRIMARY && symset[PRIMARY].name)
5893 Strcat(buf, ", active");
5894 } else if (!strcmp(optname, "term_cols")) {
5895 if (iflags.wc2_term_cols)
5896 Sprintf(buf, "%d", iflags.wc2_term_cols);
5897 else
5898 Strcpy(buf, defopt);
5899 } else if (!strcmp(optname, "term_rows")) {
5900 if (iflags.wc2_term_rows)
5901 Sprintf(buf, "%d", iflags.wc2_term_rows);
5902 else
5903 Strcpy(buf, defopt);
5904 } else if (!strcmp(optname, "tile_file")) {
5905 Sprintf(buf, "%s",
5906 iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
5907 } else if (!strcmp(optname, "tile_height")) {
5908 if (iflags.wc_tile_height)
5909 Sprintf(buf, "%d", iflags.wc_tile_height);
5910 else
5911 Strcpy(buf, defopt);
5912 } else if (!strcmp(optname, "tile_width")) {
5913 if (iflags.wc_tile_width)
5914 Sprintf(buf, "%d", iflags.wc_tile_width);
5915 else
5916 Strcpy(buf, defopt);
5917 } else if (!strcmp(optname, "traps")) {
5918 Sprintf(buf, "%s", to_be_done);
5919 } else if (!strcmp(optname, "vary_msgcount")) {
5920 if (iflags.wc_vary_msgcount)
5921 Sprintf(buf, "%d", iflags.wc_vary_msgcount);
5922 else
5923 Strcpy(buf, defopt);
5924 #ifdef MSDOS
5925 } else if (!strcmp(optname, "video")) {
5926 Sprintf(buf, "%s", to_be_done);
5927 #endif
5928 #ifdef VIDEOSHADES
5929 } else if (!strcmp(optname, "videoshades")) {
5930 Sprintf(buf, "%s-%s-%s", shade[0], shade[1], shade[2]);
5931 } else if (!strcmp(optname, "videocolors")) {
5932 Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
5933 ttycolors[CLR_RED], ttycolors[CLR_GREEN],
5934 ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
5935 ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
5936 ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
5937 ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
5938 ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
5939 #endif /* VIDEOSHADES */
5940 } else if (!strcmp(optname,"windowborders")) {
5941 Sprintf(buf, "%s",
5942 (iflags.wc2_windowborders == 0) ? "0=off"
5943 : (iflags.wc2_windowborders == 1) ? "1=on"
5944 : (iflags.wc2_windowborders == 2) ? "2=auto"
5945 : defopt);
5946 } else if (!strcmp(optname, "windowtype")) {
5947 Sprintf(buf, "%s", windowprocs.name);
5948 } else if (!strcmp(optname, "windowcolors")) {
5949 Sprintf(
5950 buf, "%s/%s %s/%s %s/%s %s/%s",
5951 iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief,
5952 iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief,
5953 iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message
5954 : defbrief,
5955 iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message
5956 : defbrief,
5957 iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief,
5958 iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief,
5959 iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief,
5960 iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief);
5961 #ifdef PREFIXES_IN_USE
5962 } else {
5963 for (i = 0; i < PREFIX_COUNT; ++i)
5964 if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i])
5965 Sprintf(buf, "%s", fqn_prefix[i]);
5966 #endif
5967 }
5968
5969 if (!buf[0])
5970 Strcpy(buf, "unknown");
5971 return buf;
5972 }
5973
5974 int
dotogglepickup()5975 dotogglepickup()
5976 {
5977 char buf[BUFSZ], ocl[MAXOCLASSES + 1];
5978
5979 flags.pickup = !flags.pickup;
5980 if (flags.pickup) {
5981 oc_to_str(flags.pickup_types, ocl);
5982 Sprintf(buf, "ON, for %s objects%s", ocl[0] ? ocl : "all",
5983 (apelist)
5984 ? ((count_apes() == 1)
5985 ? ", with one exception"
5986 : ", with some exceptions")
5987 : "");
5988 } else {
5989 Strcpy(buf, "OFF");
5990 }
5991 pline("Autopickup: %s.", buf);
5992 return 0;
5993 }
5994
5995 int
add_autopickup_exception(mapping)5996 add_autopickup_exception(mapping)
5997 const char *mapping;
5998 {
5999 static const char
6000 APE_regex_error[] = "regex error in AUTOPICKUP_EXCEPTION",
6001 APE_syntax_error[] = "syntax error in AUTOPICKUP_EXCEPTION";
6002
6003 struct autopickup_exception *ape;
6004 char text[256], end;
6005 int n;
6006 boolean grab = FALSE;
6007
6008 /* scan length limit used to be 255, but smaller size allows the
6009 quoted value to fit within BUFSZ, simplifying formatting elsewhere;
6010 this used to ignore the possibility of trailing junk but now checks
6011 for it, accepting whitespace but rejecting anything else unless it
6012 starts with '#" for a comment */
6013 end = '\0';
6014 if ((n = sscanf(mapping, "\"<%253[^\"]\" %c", text, &end)) == 1
6015 || (n == 2 && end == '#')) {
6016 grab = TRUE;
6017 } else if ((n = sscanf(mapping, "\">%253[^\"]\" %c", text, &end)) == 1
6018 || (n = sscanf(mapping, "\"%253[^\"]\" %c", text, &end)) == 1
6019 || (n == 2 && end == '#')) {
6020 grab = FALSE;
6021 } else {
6022 config_error_add("%s", APE_syntax_error);
6023 return 0;
6024 }
6025
6026 ape = (struct autopickup_exception *) alloc(sizeof *ape);
6027 ape->regex = regex_init();
6028 if (!regex_compile(text, ape->regex)) {
6029 config_error_add("%s: %s", APE_regex_error,
6030 regex_error_desc(ape->regex));
6031 regex_free(ape->regex);
6032 free((genericptr_t) ape);
6033 return 0;
6034 }
6035
6036 ape->pattern = dupstr(text);
6037 ape->grab = grab;
6038 ape->next = apelist;
6039 apelist = ape;
6040 return 1;
6041 }
6042
6043 STATIC_OVL void
remove_autopickup_exception(whichape)6044 remove_autopickup_exception(whichape)
6045 struct autopickup_exception *whichape;
6046 {
6047 struct autopickup_exception *ape, *freeape, *prev = 0;
6048
6049 for (ape = apelist; ape;) {
6050 if (ape == whichape) {
6051 freeape = ape;
6052 ape = ape->next;
6053 if (prev)
6054 prev->next = ape;
6055 else
6056 apelist = ape;
6057 regex_free(freeape->regex);
6058 free((genericptr_t) freeape->pattern);
6059 free((genericptr_t) freeape);
6060 } else {
6061 prev = ape;
6062 ape = ape->next;
6063 }
6064 }
6065 }
6066
6067 void
free_autopickup_exceptions()6068 free_autopickup_exceptions()
6069 {
6070 struct autopickup_exception *ape = apelist;
6071
6072 while ((ape = apelist) != 0) {
6073 regex_free(ape->regex);
6074 free((genericptr_t) ape->pattern);
6075 apelist = ape->next;
6076 free((genericptr_t) ape);
6077 }
6078 }
6079
6080 /* bundle some common usage into one easy-to-use routine */
6081 int
load_symset(s,which_set)6082 load_symset(s, which_set)
6083 const char *s;
6084 int which_set;
6085 {
6086 clear_symsetentry(which_set, TRUE);
6087
6088 if (symset[which_set].name)
6089 free((genericptr_t) symset[which_set].name);
6090 symset[which_set].name = dupstr(s);
6091
6092 if (read_sym_file(which_set)) {
6093 switch_symbols(TRUE);
6094 } else {
6095 clear_symsetentry(which_set, TRUE);
6096 return 0;
6097 }
6098 return 1;
6099 }
6100
6101 void
free_symsets()6102 free_symsets()
6103 {
6104 clear_symsetentry(PRIMARY, TRUE);
6105 clear_symsetentry(ROGUESET, TRUE);
6106
6107 /* symset_list is cleaned up as soon as it's used, so we shouldn't
6108 have to anything about it here */
6109 /* assert( symset_list == NULL ); */
6110 }
6111
6112 /* Parse the value of a SYMBOLS line from a config file */
6113 boolean
parsesymbols(opts,which_set)6114 parsesymbols(opts, which_set)
6115 register char *opts;
6116 int which_set;
6117 {
6118 int val;
6119 char *op, *symname, *strval;
6120 struct symparse *symp;
6121
6122 if ((op = index(opts, ',')) != 0) {
6123 *op++ = 0;
6124 if (!parsesymbols(op, which_set))
6125 return FALSE;
6126 }
6127
6128 /* S_sample:string */
6129 symname = opts;
6130 strval = index(opts, ':');
6131 if (!strval)
6132 strval = index(opts, '=');
6133 if (!strval)
6134 return FALSE;
6135 *strval++ = '\0';
6136
6137 /* strip leading and trailing white space from symname and strval */
6138 mungspaces(symname);
6139 mungspaces(strval);
6140
6141 symp = match_sym(symname);
6142 if (!symp)
6143 return FALSE;
6144
6145 if (symp->range && symp->range != SYM_CONTROL) {
6146 val = sym_val(strval);
6147 if (which_set == ROGUESET)
6148 update_ov_rogue_symset(symp, val);
6149 else
6150 update_ov_primary_symset(symp, val);
6151 }
6152 return TRUE;
6153 }
6154
6155 struct symparse *
match_sym(buf)6156 match_sym(buf)
6157 char *buf;
6158 {
6159 size_t len = strlen(buf);
6160 const char *p = index(buf, ':'), *q = index(buf, '=');
6161 struct symparse *sp = loadsyms;
6162
6163 if (!p || (q && q < p))
6164 p = q;
6165 if (p) {
6166 /* note: there will be at most one space before the '='
6167 because caller has condensed buf[] with mungspaces() */
6168 if (p > buf && p[-1] == ' ')
6169 p--;
6170 len = (int) (p - buf);
6171 }
6172 while (sp->range) {
6173 if ((len >= strlen(sp->name)) && !strncmpi(buf, sp->name, len))
6174 return sp;
6175 sp++;
6176 }
6177 return (struct symparse *) 0;
6178 }
6179
6180 int
sym_val(strval)6181 sym_val(strval)
6182 const char *strval; /* up to 4*BUFSZ-1 long; only first few chars matter */
6183 {
6184 char buf[QBUFSZ], tmp[QBUFSZ]; /* to hold trucated copy of 'strval' */
6185
6186 buf[0] = '\0';
6187 if (!strval[0] || !strval[1]) { /* empty, or single character */
6188 /* if single char is space or tab, leave buf[0]=='\0' */
6189 if (!isspace((uchar) strval[0]))
6190 buf[0] = strval[0];
6191 } else if (strval[0] == '\'') { /* single quote */
6192 /* simple matching single quote; we know strval[1] isn't '\0' */
6193 if (strval[2] == '\'' && !strval[3]) {
6194 /* accepts '\' as backslash and ''' as single quote */
6195 buf[0] = strval[1];
6196
6197 /* if backslash, handle single or double quote or second backslash */
6198 } else if (strval[1] == '\\' && strval[2] && strval[3] == '\''
6199 && index("'\"\\", strval[2]) && !strval[4]) {
6200 buf[0] = strval[2];
6201
6202 /* not simple quote or basic backslash;
6203 strip closing quote and let escapes() deal with it */
6204 } else {
6205 char *p;
6206
6207 /* +1: skip opening single quote */
6208 (void) strncpy(tmp, strval + 1, sizeof tmp - 1);
6209 tmp[sizeof tmp - 1] = '\0';
6210 if ((p = rindex(tmp, '\'')) != 0) {
6211 *p = '\0';
6212 escapes(tmp, buf);
6213 } /* else buf[0] stays '\0' */
6214 }
6215 } else { /* not lone char nor single quote */
6216 (void) strncpy(tmp, strval, sizeof tmp - 1);
6217 tmp[sizeof tmp - 1] = '\0';
6218 escapes(tmp, buf);
6219 }
6220
6221 return (int) *buf;
6222 }
6223
6224 /* data for option_help() */
6225 static const char *opt_intro[] = {
6226 "",
6227 " NetHack Options Help:", "",
6228 #define CONFIG_SLOT 3 /* fill in next value at run-time */
6229 (char *) 0,
6230 #if !defined(MICRO) && !defined(MAC)
6231 "or use `NETHACKOPTIONS=\"<options>\"' in your environment",
6232 #endif
6233 "(<options> is a list of options separated by commas)",
6234 #ifdef VMS
6235 "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
6236 #endif
6237 "or press \"O\" while playing and use the menu.",
6238 "",
6239 "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
6240 (char *) 0
6241 };
6242
6243 static const char *opt_epilog[] = {
6244 "",
6245 "Some of the options can be set only before the game is started; those",
6246 "items will not be selectable in the 'O' command's menu.",
6247 (char *) 0
6248 };
6249
6250 void
option_help()6251 option_help()
6252 {
6253 char buf[BUFSZ], buf2[BUFSZ];
6254 register int i;
6255 winid datawin;
6256
6257 datawin = create_nhwindow(NHW_TEXT);
6258 Sprintf(buf, "Set options as OPTIONS=<options> in %s", configfile);
6259 opt_intro[CONFIG_SLOT] = (const char *) buf;
6260 for (i = 0; opt_intro[i]; i++)
6261 putstr(datawin, 0, opt_intro[i]);
6262
6263 /* Boolean options */
6264 for (i = 0; boolopt[i].name; i++) {
6265 if (boolopt[i].addr) {
6266 if (boolopt[i].addr == &iflags.sanity_check && !wizard)
6267 continue;
6268 if (boolopt[i].addr == &iflags.menu_tab_sep && !wizard)
6269 continue;
6270 next_opt(datawin, boolopt[i].name);
6271 }
6272 }
6273 next_opt(datawin, "");
6274
6275 /* Compound options */
6276 putstr(datawin, 0, "Compound options:");
6277 for (i = 0; compopt[i].name; i++) {
6278 Sprintf(buf2, "`%s'", compopt[i].name);
6279 Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr,
6280 compopt[i + 1].name ? ',' : '.');
6281 putstr(datawin, 0, buf);
6282 }
6283
6284 for (i = 0; opt_epilog[i]; i++)
6285 putstr(datawin, 0, opt_epilog[i]);
6286
6287 display_nhwindow(datawin, FALSE);
6288 destroy_nhwindow(datawin);
6289 return;
6290 }
6291
6292 /*
6293 * prints the next boolean option, on the same line if possible, on a new
6294 * line if not. End with next_opt("").
6295 */
6296 void
next_opt(datawin,str)6297 next_opt(datawin, str)
6298 winid datawin;
6299 const char *str;
6300 {
6301 static char *buf = 0;
6302 int i;
6303 char *s;
6304
6305 if (!buf)
6306 *(buf = (char *) alloc(BUFSZ)) = '\0';
6307
6308 if (!*str) {
6309 s = eos(buf);
6310 if (s > &buf[1] && s[-2] == ',')
6311 Strcpy(s - 2, "."); /* replace last ", " */
6312 i = COLNO; /* (greater than COLNO - 2) */
6313 } else {
6314 i = strlen(buf) + strlen(str) + 2;
6315 }
6316
6317 if (i > COLNO - 2) { /* rule of thumb */
6318 putstr(datawin, 0, buf);
6319 buf[0] = 0;
6320 }
6321 if (*str) {
6322 Strcat(buf, str);
6323 Strcat(buf, ", ");
6324 } else {
6325 putstr(datawin, 0, str);
6326 free((genericptr_t) buf), buf = 0;
6327 }
6328 return;
6329 }
6330
6331 /* Returns the fid of the fruit type; if that type already exists, it
6332 * returns the fid of that one; if it does not exist, it adds a new fruit
6333 * type to the chain and returns the new one.
6334 * If replace_fruit is sent in, replace the fruit in the chain rather than
6335 * adding a new entry--for user specified fruits only.
6336 */
6337 int
fruitadd(str,replace_fruit)6338 fruitadd(str, replace_fruit)
6339 char *str;
6340 struct fruit *replace_fruit;
6341 {
6342 register int i;
6343 register struct fruit *f;
6344 int highest_fruit_id = 0, globpfx;
6345 char buf[PL_FSIZ], altname[PL_FSIZ];
6346 boolean user_specified = (str == pl_fruit);
6347 /* if not user-specified, then it's a fruit name for a fruit on
6348 * a bones level or from orctown raider's loot...
6349 */
6350
6351 /* Note: every fruit has an id (kept in obj->spe) of at least 1;
6352 * 0 is an error.
6353 */
6354 if (user_specified) {
6355 boolean found = FALSE, numeric = FALSE;
6356
6357 /* force fruit to be singular; this handling is not
6358 needed--or wanted--for fruits from bones because
6359 they already received it in their original game;
6360 str==pl_fruit but makesingular() creates a copy
6361 so we need to copy that back into pl_fruit */
6362 nmcpy(pl_fruit, makesingular(str), PL_FSIZ);
6363 /* (assertion doesn't matter; we use 'pl_fruit' from here on out) */
6364 /* assert( str == pl_fruit ); */
6365
6366 /* disallow naming after other foods (since it'd be impossible
6367 * to tell the difference); globs might have a size prefix which
6368 * needs to be skipped in order to match the object type name
6369 */
6370 globpfx = (!strncmp(pl_fruit, "small ", 6)
6371 || !strncmp(pl_fruit, "large ", 6)) ? 6
6372 : (!strncmp(pl_fruit, "very large ", 11)) ? 11
6373 : 0;
6374 for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
6375 if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)
6376 || (globpfx > 0
6377 && !strcmp(OBJ_NAME(objects[i]), &pl_fruit[globpfx]))) {
6378 found = TRUE;
6379 break;
6380 }
6381 }
6382 if (!found) {
6383 char *c;
6384
6385 for (c = pl_fruit; *c >= '0' && *c <= '9'; c++)
6386 continue;
6387 if (!*c || isspace((uchar) *c))
6388 numeric = TRUE;
6389 }
6390 if (found || numeric
6391 /* these checks for applying food attributes to actual items
6392 are case sensitive; "glob of foo" is caught by 'found'
6393 if 'foo' is a valid glob; when not valid, allow it as-is */
6394 || !strncmp(pl_fruit, "cursed ", 7)
6395 || !strncmp(pl_fruit, "uncursed ", 9)
6396 || !strncmp(pl_fruit, "blessed ", 8)
6397 || !strncmp(pl_fruit, "partly eaten ", 13)
6398 || (!strncmp(pl_fruit, "tin of ", 7)
6399 && (!strcmp(pl_fruit + 7, "spinach")
6400 || name_to_mon(pl_fruit + 7) >= LOW_PM))
6401 || !strcmp(pl_fruit, "empty tin")
6402 || (!strcmp(pl_fruit, "glob")
6403 || (globpfx > 0 && !strcmp("glob", &pl_fruit[globpfx])))
6404 || ((str_end_is(pl_fruit, " corpse")
6405 || str_end_is(pl_fruit, " egg"))
6406 && name_to_mon(pl_fruit) >= LOW_PM)) {
6407 Strcpy(buf, pl_fruit);
6408 Strcpy(pl_fruit, "candied ");
6409 nmcpy(pl_fruit + 8, buf, PL_FSIZ - 8);
6410 }
6411 *altname = '\0';
6412 /* This flag indicates that a fruit has been made since the
6413 * last time the user set the fruit. If it hasn't, we can
6414 * safely overwrite the current fruit, preventing the user from
6415 * setting many fruits in a row and overflowing.
6416 * Possible expansion: check for specific fruit IDs, not for
6417 * any fruit.
6418 */
6419 flags.made_fruit = FALSE;
6420 if (replace_fruit) {
6421 /* replace_fruit is already part of the fruit chain;
6422 update it in place rather than looking it up again */
6423 f = replace_fruit;
6424 copynchars(f->fname, pl_fruit, PL_FSIZ - 1);
6425 goto nonew;
6426 }
6427 } else {
6428 /* not user_supplied, so assumed to be from bones (or orc gang) */
6429 copynchars(altname, str, PL_FSIZ - 1);
6430 sanitize_name(altname);
6431 flags.made_fruit = TRUE; /* for safety. Any fruit name added from a
6432 * bones level should exist anyway. */
6433 }
6434 f = fruit_from_name(*altname ? altname : str, FALSE, &highest_fruit_id);
6435 if (f)
6436 goto nonew;
6437
6438 /* Maximum number of named fruits is 127, even if obj->spe can
6439 handle bigger values. If adding another fruit would overflow,
6440 use a random fruit instead... we've got a lot to choose from.
6441 current_fruit remains as is. */
6442 if (highest_fruit_id >= 127)
6443 return rnd(127);
6444
6445 f = newfruit();
6446 (void) memset((genericptr_t) f, 0, sizeof (struct fruit));
6447 copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
6448 f->fid = ++highest_fruit_id;
6449 /* we used to go out of our way to add it at the end of the list,
6450 but the order is arbitrary so use simpler insertion at start */
6451 f->nextf = ffruit;
6452 ffruit = f;
6453 nonew:
6454 if (user_specified)
6455 context.current_fruit = f->fid;
6456 return f->fid;
6457 }
6458
6459 /*
6460 * This is a somewhat generic menu for taking a list of NetHack style
6461 * class choices and presenting them via a description
6462 * rather than the traditional NetHack characters.
6463 * (Benefits users whose first exposure to NetHack is via tiles).
6464 *
6465 * prompt
6466 * The title at the top of the menu.
6467 *
6468 * category: 0 = monster class
6469 * 1 = object class
6470 *
6471 * way
6472 * FALSE = PICK_ONE, TRUE = PICK_ANY
6473 *
6474 * class_list
6475 * a null terminated string containing the list of choices.
6476 *
6477 * class_selection
6478 * a null terminated string containing the selected characters.
6479 *
6480 * Returns number selected.
6481 */
6482 int
choose_classes_menu(prompt,category,way,class_list,class_select)6483 choose_classes_menu(prompt, category, way, class_list, class_select)
6484 const char *prompt;
6485 int category;
6486 boolean way;
6487 char *class_list;
6488 char *class_select;
6489 {
6490 menu_item *pick_list = (menu_item *) 0;
6491 winid win;
6492 anything any;
6493 char buf[BUFSZ];
6494 int i, n;
6495 int ret;
6496 int next_accelerator, accelerator;
6497
6498 if (class_list == (char *) 0 || class_select == (char *) 0)
6499 return 0;
6500 accelerator = 0;
6501 next_accelerator = 'a';
6502 any = zeroany;
6503 win = create_nhwindow(NHW_MENU);
6504 start_menu(win);
6505 while (*class_list) {
6506 const char *text;
6507 boolean selected;
6508
6509 text = (char *) 0;
6510 selected = FALSE;
6511 switch (category) {
6512 case 0:
6513 text = def_monsyms[def_char_to_monclass(*class_list)].explain;
6514 accelerator = *class_list;
6515 Sprintf(buf, "%s", text);
6516 break;
6517 case 1:
6518 text = def_oc_syms[def_char_to_objclass(*class_list)].explain;
6519 accelerator = next_accelerator;
6520 Sprintf(buf, "%c %s", *class_list, text);
6521 break;
6522 default:
6523 impossible("choose_classes_menu: invalid category %d", category);
6524 }
6525 if (way && *class_select) { /* Selections there already */
6526 if (index(class_select, *class_list)) {
6527 selected = TRUE;
6528 }
6529 }
6530 any.a_int = *class_list;
6531 add_menu(win, NO_GLYPH, &any, accelerator, category ? *class_list : 0,
6532 ATR_NONE, buf, selected);
6533 ++class_list;
6534 if (category > 0) {
6535 ++next_accelerator;
6536 if (next_accelerator == ('z' + 1))
6537 next_accelerator = 'A';
6538 if (next_accelerator == ('Z' + 1))
6539 break;
6540 }
6541 }
6542 if (category == 1 && next_accelerator <= 'z') {
6543 /* for objects, add "A - ' ' all classes", after a separator */
6544 any = zeroany;
6545 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED);
6546 any.a_int = (int) ' ';
6547 Sprintf(buf, "%c %s", (char) any.a_int, "all classes of objects");
6548 /* we won't preselect this even if the incoming list is empty;
6549 having it selected means that it would have to be explicitly
6550 de-selected in order to select anything else */
6551 add_menu(win, NO_GLYPH, &any, 'A', 0, ATR_NONE, buf, MENU_UNSELECTED);
6552 }
6553 end_menu(win, prompt);
6554 n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
6555 destroy_nhwindow(win);
6556 if (n > 0) {
6557 if (category == 1) {
6558 /* for object classes, first check for 'all'; it means 'use
6559 a blank list' rather than 'collect every possible choice' */
6560 for (i = 0; i < n; ++i)
6561 if (pick_list[i].item.a_int == ' ') {
6562 pick_list[0].item.a_int = ' ';
6563 n = 1; /* return 1; also an implicit 'break;' */
6564 }
6565 }
6566 for (i = 0; i < n; ++i)
6567 *class_select++ = (char) pick_list[i].item.a_int;
6568 free((genericptr_t) pick_list);
6569 ret = n;
6570 } else if (n == -1) {
6571 class_select = eos(class_select);
6572 ret = -1;
6573 } else
6574 ret = 0;
6575 *class_select = '\0';
6576 return ret;
6577 }
6578
6579 static struct wc_Opt wc_options[] = {
6580 { "ascii_map", WC_ASCII_MAP },
6581 { "color", WC_COLOR },
6582 { "eight_bit_tty", WC_EIGHT_BIT_IN },
6583 { "hilite_pet", WC_HILITE_PET },
6584 { "popup_dialog", WC_POPUP_DIALOG },
6585 { "player_selection", WC_PLAYER_SELECTION },
6586 { "preload_tiles", WC_PRELOAD_TILES },
6587 { "tiled_map", WC_TILED_MAP },
6588 { "tile_file", WC_TILE_FILE },
6589 { "tile_width", WC_TILE_WIDTH },
6590 { "tile_height", WC_TILE_HEIGHT },
6591 { "use_inverse", WC_INVERSE },
6592 { "align_message", WC_ALIGN_MESSAGE },
6593 { "align_status", WC_ALIGN_STATUS },
6594 { "font_map", WC_FONT_MAP },
6595 { "font_menu", WC_FONT_MENU },
6596 { "font_message", WC_FONT_MESSAGE },
6597 #if 0
6598 {"perm_invent", WC_PERM_INVENT},
6599 #endif
6600 { "font_size_map", WC_FONTSIZ_MAP },
6601 { "font_size_menu", WC_FONTSIZ_MENU },
6602 { "font_size_message", WC_FONTSIZ_MESSAGE },
6603 { "font_size_status", WC_FONTSIZ_STATUS },
6604 { "font_size_text", WC_FONTSIZ_TEXT },
6605 { "font_status", WC_FONT_STATUS },
6606 { "font_text", WC_FONT_TEXT },
6607 { "map_mode", WC_MAP_MODE },
6608 { "scroll_amount", WC_SCROLL_AMOUNT },
6609 { "scroll_margin", WC_SCROLL_MARGIN },
6610 { "splash_screen", WC_SPLASH_SCREEN },
6611 { "vary_msgcount", WC_VARY_MSGCOUNT },
6612 { "windowcolors", WC_WINDOWCOLORS },
6613 { "mouse_support", WC_MOUSE_SUPPORT },
6614 { (char *) 0, 0L }
6615 };
6616 static struct wc_Opt wc2_options[] = {
6617 { "fullscreen", WC2_FULLSCREEN },
6618 { "softkeyboard", WC2_SOFTKEYBOARD },
6619 { "wraptext", WC2_WRAPTEXT },
6620 { "use_darkgray", WC2_DARKGRAY },
6621 { "hitpointbar", WC2_HITPOINTBAR },
6622 { "hilite_status", WC2_HILITE_STATUS },
6623 /* name shown in 'O' menu is different */
6624 { "status hilite rules", WC2_HILITE_STATUS },
6625 /* statushilites doesn't have its own bit */
6626 { "statushilites", WC2_HILITE_STATUS },
6627 { "term_cols", WC2_TERM_SIZE },
6628 { "term_rows", WC2_TERM_SIZE },
6629 { "petattr", WC2_PETATTR },
6630 { "guicolor", WC2_GUICOLOR },
6631 { "statuslines", WC2_STATUSLINES },
6632 { "windowborders", WC2_WINDOWBORDERS },
6633 { (char *) 0, 0L }
6634 };
6635
6636 /*
6637 * If a port wants to change or ensure that the SET_IN_SYS,
6638 * SET_IN_FILE, DISP_IN_GAME, or SET_IN_GAME status of an option is
6639 * correct (for controlling its display in the option menu) call
6640 * set_option_mod_status()
6641 * with the appropriate second argument.
6642 */
6643 void
set_option_mod_status(optnam,status)6644 set_option_mod_status(optnam, status)
6645 const char *optnam;
6646 int status;
6647 {
6648 int k;
6649
6650 if (SET__IS_VALUE_VALID(status)) {
6651 impossible("set_option_mod_status: status out of range %d.", status);
6652 return;
6653 }
6654 for (k = 0; boolopt[k].name; k++) {
6655 if (!strncmpi(boolopt[k].name, optnam, strlen(optnam))) {
6656 boolopt[k].optflags = status;
6657 return;
6658 }
6659 }
6660 for (k = 0; compopt[k].name; k++) {
6661 if (!strncmpi(compopt[k].name, optnam, strlen(optnam))) {
6662 compopt[k].optflags = status;
6663 return;
6664 }
6665 }
6666 }
6667
6668 /*
6669 * You can set several wc_options in one call to
6670 * set_wc_option_mod_status() by setting
6671 * the appropriate bits for each option that you
6672 * are setting in the optmask argument
6673 * prior to calling.
6674 * example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN,
6675 * SET_IN_GAME);
6676 */
6677 void
set_wc_option_mod_status(optmask,status)6678 set_wc_option_mod_status(optmask, status)
6679 unsigned long optmask;
6680 int status;
6681 {
6682 int k = 0;
6683
6684 if (SET__IS_VALUE_VALID(status)) {
6685 impossible("set_wc_option_mod_status: status out of range %d.",
6686 status);
6687 return;
6688 }
6689 while (wc_options[k].wc_name) {
6690 if (optmask & wc_options[k].wc_bit) {
6691 set_option_mod_status(wc_options[k].wc_name, status);
6692 }
6693 k++;
6694 }
6695 }
6696
6697 STATIC_OVL boolean
is_wc_option(optnam)6698 is_wc_option(optnam)
6699 const char *optnam;
6700 {
6701 int k = 0;
6702
6703 while (wc_options[k].wc_name) {
6704 if (strcmp(wc_options[k].wc_name, optnam) == 0)
6705 return TRUE;
6706 k++;
6707 }
6708 return FALSE;
6709 }
6710
6711 STATIC_OVL boolean
wc_supported(optnam)6712 wc_supported(optnam)
6713 const char *optnam;
6714 {
6715 int k;
6716
6717 for (k = 0; wc_options[k].wc_name; ++k) {
6718 if (!strcmp(wc_options[k].wc_name, optnam))
6719 return (windowprocs.wincap & wc_options[k].wc_bit) ? TRUE : FALSE;
6720 }
6721 return FALSE;
6722 }
6723
6724 /*
6725 * You can set several wc2_options in one call to
6726 * set_wc2_option_mod_status() by setting
6727 * the appropriate bits for each option that you
6728 * are setting in the optmask argument
6729 * prior to calling.
6730 * example:
6731 * set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT,
6732 * SET_IN_FILE);
6733 */
6734
6735 void
set_wc2_option_mod_status(optmask,status)6736 set_wc2_option_mod_status(optmask, status)
6737 unsigned long optmask;
6738 int status;
6739 {
6740 int k = 0;
6741
6742 if (SET__IS_VALUE_VALID(status)) {
6743 impossible("set_wc2_option_mod_status: status out of range %d.",
6744 status);
6745 return;
6746 }
6747 while (wc2_options[k].wc_name) {
6748 if (optmask & wc2_options[k].wc_bit) {
6749 set_option_mod_status(wc2_options[k].wc_name, status);
6750 }
6751 k++;
6752 }
6753 }
6754
6755 STATIC_OVL boolean
is_wc2_option(optnam)6756 is_wc2_option(optnam)
6757 const char *optnam;
6758 {
6759 int k = 0;
6760
6761 while (wc2_options[k].wc_name) {
6762 if (strcmp(wc2_options[k].wc_name, optnam) == 0)
6763 return TRUE;
6764 k++;
6765 }
6766 return FALSE;
6767 }
6768
6769 STATIC_OVL boolean
wc2_supported(optnam)6770 wc2_supported(optnam)
6771 const char *optnam;
6772 {
6773 int k;
6774
6775 for (k = 0; wc2_options[k].wc_name; ++k) {
6776 if (!strcmp(wc2_options[k].wc_name, optnam))
6777 return (windowprocs.wincap2 & wc2_options[k].wc_bit) ? TRUE
6778 : FALSE;
6779 }
6780 return FALSE;
6781 }
6782
6783 STATIC_OVL void
wc_set_font_name(opttype,fontname)6784 wc_set_font_name(opttype, fontname)
6785 int opttype;
6786 char *fontname;
6787 {
6788 char **fn = (char **) 0;
6789
6790 if (!fontname)
6791 return;
6792 switch (opttype) {
6793 case MAP_OPTION:
6794 fn = &iflags.wc_font_map;
6795 break;
6796 case MESSAGE_OPTION:
6797 fn = &iflags.wc_font_message;
6798 break;
6799 case TEXT_OPTION:
6800 fn = &iflags.wc_font_text;
6801 break;
6802 case MENU_OPTION:
6803 fn = &iflags.wc_font_menu;
6804 break;
6805 case STATUS_OPTION:
6806 fn = &iflags.wc_font_status;
6807 break;
6808 default:
6809 return;
6810 }
6811 if (fn) {
6812 if (*fn)
6813 free((genericptr_t) *fn);
6814 *fn = dupstr(fontname);
6815 }
6816 return;
6817 }
6818
6819 STATIC_OVL int
wc_set_window_colors(op)6820 wc_set_window_colors(op)
6821 char *op;
6822 {
6823 /* syntax:
6824 * menu white/black message green/yellow status white/blue text
6825 * white/black
6826 */
6827 int j;
6828 char buf[BUFSZ];
6829 char *wn, *tfg, *tbg, *newop;
6830 static const char *wnames[] = { "menu", "message", "status", "text" };
6831 static const char *shortnames[] = { "mnu", "msg", "sts", "txt" };
6832 static char **fgp[] = { &iflags.wc_foregrnd_menu,
6833 &iflags.wc_foregrnd_message,
6834 &iflags.wc_foregrnd_status,
6835 &iflags.wc_foregrnd_text };
6836 static char **bgp[] = { &iflags.wc_backgrnd_menu,
6837 &iflags.wc_backgrnd_message,
6838 &iflags.wc_backgrnd_status,
6839 &iflags.wc_backgrnd_text };
6840
6841 Strcpy(buf, op);
6842 newop = mungspaces(buf);
6843 while (newop && *newop) {
6844 wn = tfg = tbg = (char *) 0;
6845
6846 /* until first non-space in case there's leading spaces - before
6847 * colorname*/
6848 if (*newop == ' ')
6849 newop++;
6850 if (*newop)
6851 wn = newop;
6852 else
6853 return 0;
6854
6855 /* until first space - colorname*/
6856 while (*newop && *newop != ' ')
6857 newop++;
6858 if (*newop)
6859 *newop = '\0';
6860 else
6861 return 0;
6862 newop++;
6863
6864 /* until first non-space - before foreground*/
6865 if (*newop == ' ')
6866 newop++;
6867 if (*newop)
6868 tfg = newop;
6869 else
6870 return 0;
6871
6872 /* until slash - foreground */
6873 while (*newop && *newop != '/')
6874 newop++;
6875 if (*newop)
6876 *newop = '\0';
6877 else
6878 return 0;
6879 newop++;
6880
6881 /* until first non-space (in case there's leading space after slash) -
6882 * before background */
6883 if (*newop == ' ')
6884 newop++;
6885 if (*newop)
6886 tbg = newop;
6887 else
6888 return 0;
6889
6890 /* until first space - background */
6891 while (*newop && *newop != ' ')
6892 newop++;
6893 if (*newop)
6894 *newop++ = '\0';
6895
6896 for (j = 0; j < 4; ++j) {
6897 if (!strcmpi(wn, wnames[j]) || !strcmpi(wn, shortnames[j])) {
6898 if (tfg && !strstri(tfg, " ")) {
6899 if (*fgp[j])
6900 free((genericptr_t) *fgp[j]);
6901 *fgp[j] = dupstr(tfg);
6902 }
6903 if (tbg && !strstri(tbg, " ")) {
6904 if (*bgp[j])
6905 free((genericptr_t) *bgp[j]);
6906 *bgp[j] = dupstr(tbg);
6907 }
6908 break;
6909 }
6910 }
6911 }
6912 return 1;
6913 }
6914
6915 /* set up for wizard mode if player or save file has requested to it;
6916 called from port-specific startup code to handle `nethack -D' or
6917 OPTIONS=playmode:debug, or from dorecover()'s restgamestate() if
6918 restoring a game which was saved in wizard mode */
6919 void
set_playmode()6920 set_playmode()
6921 {
6922 if (wizard) {
6923 if (authorize_wizard_mode())
6924 Strcpy(plname, "wizard");
6925 else
6926 wizard = FALSE; /* not allowed or not available */
6927 /* force explore mode if we didn't make it into wizard mode */
6928 discover = !wizard;
6929 iflags.deferred_X = FALSE;
6930 }
6931 /* don't need to do anything special for explore mode or normal play */
6932 }
6933
6934 #endif /* OPTION_LISTS_ONLY */
6935
6936 /*options.c*/
6937