1 /* NetHack 3.7 options.c $NHDT-Date: 1613723080 2021/02/19 08:24:40 $ $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.508 $ */
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 NEARDATA struct instance_flags iflags; /* provide linkage */
12 #define static
13 #else
14 #include "hack.h"
15 #include "tcap.h"
16 #include <ctype.h>
17 #endif
18
19 #define BACKWARD_COMPAT
20
21 /*
22 * NOTE: If you add (or delete) an option, please review the following:
23 * doc/options.doc
24 *
25 * It contains how-to info and outlines some required/suggested
26 * updates that should accompany your change.
27 */
28
29 /*
30 * include/optlist.h is utilized 3 successive times, for 3 different
31 * objectives.
32 *
33 * The first time is with NHOPT_PROTO defined, to produce and include
34 * the prototypes for the individual option processing functions.
35 *
36 * The second time is with NHOPT_ENUM defined, to produce the enum values
37 * for the individual options that are used throughout options processing.
38 * They are generally opt_optname, where optname is the name of the option.
39 *
40 * The third time is with NHOPT_PARSE defined, to produce the initializers
41 * to fill out the allopt[] array of options (both boolean and compound).
42 *
43 */
44
45 #define NHOPT_PROTO
46 #include "optlist.h"
47 #undef NHOPT_PROTO
48
49 #define NHOPT_ENUM
50 enum opt {
51 opt_prefix_only = -1,
52 #include "optlist.h"
53 OPTCOUNT
54 };
55 #undef NHOPT_ENUM
56
57 #define NHOPT_PARSE
58 static struct allopt_t allopt_init[] = {
59 #include "optlist.h"
60 {(const char *) 0, 0, 0, 0, set_in_sysconf, BoolOpt,
61 No, No, No, No, 0, (boolean *) 0,
62 (int (*)(int, int, boolean, char *, char *)) 0,
63 (char *) 0, (const char *) 0, (const char *) 0, 0, 0, 0}
64 };
65 #undef NHOPT_PARSE
66
67
68 #ifdef DEFAULT_WC_TILED_MAP
69 #define PREFER_TILED TRUE
70 #else
71 #define PREFER_TILED FALSE
72 #endif
73
74 #define PILE_LIMIT_DFLT 5
75 #define rolestring(val, array, field) \
76 ((val >= 0) ? array[val].field : (val == ROLE_RANDOM) ? randomrole : none)
77
78
79 enum window_option_types {
80 MESSAGE_OPTION = 1,
81 STATUS_OPTION,
82 MAP_OPTION,
83 MENU_OPTION,
84 TEXT_OPTION
85 };
86
87 enum {optn_silenterr = -1, optn_err = 0, optn_ok};
88 enum requests {do_nothing, do_init, do_set, do_handler, get_val};
89
90 static struct allopt_t allopt[SIZE(allopt_init)];
91
92 #ifndef OPTION_LISTS_ONLY
93
94 /* use rest of file */
95
96 extern char configfile[]; /* for messages */
97 extern struct symparse loadsyms[];
98 #if defined(TOS) && defined(TEXTCOLOR)
99 extern boolean colors_changed; /* in tos.c */
100 #endif
101 #ifdef VIDEOSHADES
102 extern char *shade[3]; /* in sys/msdos/video.c */
103 extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */
104 #endif
105
106 static char empty_optstr[] = { '\0' };
107 boolean duplicate, using_alias;
108
109 static const char def_inv_order[MAXOCLASSES] = {
110 COIN_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS,
111 SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS,
112 TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0,
113 };
114
115 static const char none[] = "(none)", randomrole[] = "random",
116 to_be_done[] = "(to be done)",
117 defopt[] = "default", defbrief[] = "def";
118
119 /* paranoia[] - used by parseoptions() and handler_paranoid_confirmation() */
120 static const struct paranoia_opts {
121 int flagmask; /* which paranoid option */
122 const char *argname; /* primary name */
123 int argMinLen; /* minimum number of letters to match */
124 const char *synonym; /* alternate name (optional) */
125 int synMinLen;
126 const char *explain; /* for interactive menu */
127 } paranoia[] = {
128 /* there are some initial-letter conflicts: "a"ttack vs "a"ll, "attack"
129 takes precedence and "all" isn't present in the interactive menu,
130 and "d"ie vs "d"eath, synonyms for each other so doesn't matter;
131 (also "p"ray vs "P"aranoia, "pray" takes precedence since "Paranoia"
132 is just a synonym for "Confirm"); "b"ones vs "br"eak-wand, the
133 latter requires at least two letters; "e"at vs "ex"plore,
134 "cont"inue eating vs "C"onfirm; "wand"-break vs "Were"-change,
135 both require at least two letters during config processing and use
136 case-senstivity for 'O's interactive menu */
137 { PARANOID_CONFIRM, "Confirm", 1, "Paranoia", 2,
138 "for \"yes\" confirmations, require \"no\" to reject" },
139 { PARANOID_QUIT, "quit", 1, "explore", 2,
140 "yes vs y to quit or to enter explore mode" },
141 { PARANOID_DIE, "die", 1, "death", 2,
142 "yes vs y to die (explore mode or debug mode)" },
143 { PARANOID_BONES, "bones", 1, 0, 0,
144 "yes vs y to save bones data when dying in debug mode" },
145 { PARANOID_HIT, "attack", 1, "hit", 1,
146 "yes vs y to attack a peaceful monster" },
147 { PARANOID_BREAKWAND, "wand-break", 2, "break-wand", 2,
148 "yes vs y to break a wand via (a)pply" },
149 { PARANOID_EATING, "eat", 1, "continue", 4,
150 "yes vs y to continue eating after first bite when satiated" },
151 { PARANOID_WERECHANGE, "Were-change", 2, (const char *) 0, 0,
152 "yes vs y to change form when lycanthropy is controllable" },
153 { PARANOID_PRAY, "pray", 1, 0, 0,
154 "y to pray (supersedes old \"prayconfirm\" option)" },
155 { PARANOID_REMOVE, "Remove", 1, "Takeoff", 1,
156 "always pick from inventory for Remove and Takeoff" },
157 { PARANOID_SWIM, "swim", 1, NULL, 0,
158 "y to walk into a water or lava space when moving with 'm'" },
159 { PARANOID_TRAP, "trap", 1, "move-trap", 1,
160 "yes vs y to move onto a trap" },
161 { PARANOID_THROW, "Throw", 1, NULL, 0,
162 "y to throw ammo while not wielding a corresponding launcher" },
163 /* for config file parsing; interactive menu skips these */
164 { 0, "none", 4, 0, 0, 0 }, /* require full word match */
165 { ~0, "all", 3, 0, 0, 0 }, /* ditto */
166 };
167
168 static NEARDATA const char *menutype[] = {
169 "traditional", "combination", "full", "partial"
170 };
171 static NEARDATA const char *burdentype[] = {
172 "unencumbered", "burdened", "stressed",
173 "strained", "overtaxed", "overloaded"
174 };
175 static NEARDATA const char *runmodes[] = {
176 "teleport", "run", "walk", "crawl"
177 };
178 static NEARDATA const char *sortltype[] = {
179 "none", "loot", "full"
180 };
181
182 /*
183 * Default menu manipulation command accelerators. These may _not_ be:
184 *
185 * + a number or '#' - reserved for counts
186 * + an upper or lower case US ASCII letter - used for accelerators
187 * + ESC - reserved for escaping the menu
188 * + NULL, CR or LF - reserved for commiting the selection(s). NULL
189 * is kind of odd, but the tty's xwaitforspace() will return it if
190 * someone hits a <ret>.
191 * + a default object class symbol - used for object class accelerators
192 *
193 * Standard letters (for now) are:
194 *
195 * < back 1 page
196 * > forward 1 page
197 * ^ first page
198 * | last page
199 * : search
200 *
201 * page all
202 * , select .
203 * \ deselect -
204 * ~ invert @
205 *
206 * The command name list is duplicated in the compopt array.
207 */
208 typedef struct {
209 const char *name;
210 char cmd;
211 const char *desc;
212 } menu_cmd_t;
213
214 static const menu_cmd_t default_menu_cmd_info[] = {
215 { "menu_next_page", MENU_NEXT_PAGE, "Go to next page" },
216 { "menu_previous_page", MENU_PREVIOUS_PAGE, "Go to previous page" },
217 { "menu_first_page", MENU_FIRST_PAGE, "Go to first page" },
218 { "menu_last_page", MENU_LAST_PAGE, "Go to last page" },
219 { "menu_select_all", MENU_SELECT_ALL,
220 "Select all items in entire menu" },
221 { "menu_invert_all", MENU_INVERT_ALL,
222 "Invert selection for all items" },
223 { "menu_deselect_all", MENU_UNSELECT_ALL,
224 "Unselect all items in entire menu" },
225 { "menu_select_page", MENU_SELECT_PAGE,
226 "Select all items on current page" },
227 { "menu_invert_page", MENU_INVERT_PAGE,
228 "Invert current page's selections" },
229 { "menu_deselect_page", MENU_UNSELECT_PAGE,
230 "Unselect all items on current page" },
231 { "menu_search", MENU_SEARCH,
232 "Search and invert matching items" },
233 { "menu_shift_right", MENU_SHIFT_RIGHT,
234 "Pan current page to right (perm_invent only)" },
235 { "menu_shift_left", MENU_SHIFT_LEFT,
236 "Pan current page to left (perm_invent only)" },
237 { (char *) 0, '\0', (char *) 0 }
238 };
239
240 static void nmcpy(char *, const char *, int);
241 static void escapes(const char *, char *);
242 static void rejectoption(const char *);
243 static char *string_for_opt(char *, boolean);
244 static char *string_for_env_opt(const char *, char *, boolean);
245 static void bad_negation(const char *, boolean);
246 static int change_inv_order(char *);
247 static boolean warning_opts(char *, const char *);
248 static int feature_alert_opts(char *, const char *);
249 static boolean duplicate_opt_detection(int);
250 static void complain_about_duplicate(int);
251 static int length_without_val(const char *, int len);
252 static void determine_ambiguities(void);
253 static int check_misc_menu_command(char *, char *);
254 static int shared_menu_optfn(int, int, boolean, char *, char *);
255 static int spcfn_misc_menu_cmd(int, int, boolean, char *, char *);
256
257 static const char *attr2attrname(int);
258 static void basic_menu_colors(boolean);
259 static const char * msgtype2name(int);
260 static int query_msgtype(void);
261 static boolean msgtype_add(int, char *);
262 static void free_one_msgtype(int);
263 static int msgtype_count(void);
264 static boolean test_regex_pattern(const char *, const char *);
265 static boolean add_menu_coloring_parsed(const char *, int, int);
266 static void free_one_menu_coloring(int);
267 static int count_menucolors(void);
268 static boolean parse_role_opts(int, boolean, const char *,
269 char *, char **);
270 static void doset_add_menu(winid, const char *, int, int);
271 static int handle_add_list_remove(const char *, int);
272 static void remove_autopickup_exception(struct autopickup_exception *);
273 static int count_apes(void);
274 static int count_cond(void);
275 static int count_monstercolors(void);
276
277 static int handler_align_misc(int);
278 static int handler_disclose(void);
279 static int handler_menu_headings(void);
280 static int handler_menustyle(void);
281 static int handler_msg_window(void);
282 static int handler_number_pad(void);
283 static int handler_paranoid_confirmation(void);
284 static int handler_pickup_burden(void);
285 static int handler_pickup_types(void);
286 static int handler_runmode(void);
287 static int handler_sortloot(void);
288 static int handler_whatis_coord(void);
289 static int handler_whatis_filter(void);
290 /* next few are not allopts[] entries, so will only be called
291 directly from doset, not from individual optfn's */
292 static int handler_autopickup_exception(void);
293 static int handler_menu_colors(void);
294 static int handler_msgtype(void);
295 static int handler_monstercolor(void);
296
297 static boolean is_wc_option(const char *);
298 static boolean wc_supported(const char *);
299 static boolean is_wc2_option(const char *);
300 static boolean wc2_supported(const char *);
301 static void wc_set_font_name(int, char *);
302 static int wc_set_window_colors(char *);
303 static boolean illegal_menu_cmd_key(uchar);
304 #ifdef CURSES_GRAPHICS
305 extern int curses_read_attrs(const char *attrs);
306 extern char *curses_fmt_attrs(char *);
307 #endif
308
309 /*
310 **********************************
311 *
312 * parseoptions
313 *
314 **********************************
315 */
316 boolean
parseoptions(register char * opts,boolean tinitial,boolean tfrom_file)317 parseoptions(register char *opts, boolean tinitial, boolean tfrom_file)
318 {
319 char *op;
320 boolean negated, got_match = FALSE;
321 #if 0
322 boolean has_val = FALSE;
323 #endif
324 int i, matchidx = -1, optresult = optn_err, optlen, optlen_wo_val;
325 boolean retval = TRUE;
326
327 duplicate = FALSE;
328 using_alias = FALSE;
329 g.opt_initial = tinitial;
330 g.opt_from_file = tfrom_file;
331 if ((op = index(opts, ',')) != 0) {
332 *op++ = 0;
333 if (!parseoptions(op, g.opt_initial, g.opt_from_file))
334 retval = FALSE;
335 }
336 if (strlen(opts) > BUFSZ / 2) {
337 config_error_add("Option too long, max length is %i characters",
338 (BUFSZ / 2));
339 return FALSE;
340 }
341
342 /* strip leading and trailing white space */
343 while (isspace((uchar) *opts))
344 opts++;
345 op = eos(opts);
346 while (--op >= opts && isspace((uchar) *op))
347 *op = '\0';
348
349 if (!*opts) {
350 config_error_add("Empty statement");
351 return FALSE;
352 }
353 negated = FALSE;
354 while ((*opts == '!') || !strncmpi(opts, "no", 2)) {
355 if (*opts == '!')
356 opts++;
357 else
358 opts += 2;
359 negated = !negated;
360 }
361 optlen = (int) strlen(opts);
362 optlen_wo_val = length_without_val(opts, optlen);
363 if (optlen_wo_val < optlen) {
364 #if 0
365 has_val = TRUE;
366 #endif
367 optlen = optlen_wo_val;
368 #if 0
369 } else {
370 has_val = FALSE;
371 #endif
372 }
373
374 for (i = 0; i < OPTCOUNT; ++i) {
375 got_match = FALSE;
376
377 if (allopt[i].pfx) {
378 if (!strncmpi(opts, allopt[i].name, strlen(allopt[i].name))) {
379 matchidx = i;
380 got_match = TRUE;
381 }
382 }
383 #if 0 /* this prevents "boolopt:True" &c */
384 if (!got_match) {
385 if (has_val && !allopt[i].valok)
386 continue;
387 }
388 #endif
389 /*
390 * During option initialization, the function
391 * determine_ambiguities()
392 * figured out exactly how many characters are required to
393 * unambiguously differentiate one option from all others, and it
394 * placed that number into each option's alloption[n].minmatch.
395 *
396 */
397 if (!got_match)
398 got_match = match_optname(opts, allopt[i].name,
399 allopt[i].minmatch, TRUE);
400 if (got_match) {
401 if (!allopt[i].pfx && optlen < allopt[i].minmatch) {
402 config_error_add(
403 "Ambiguous option %s, %d characters are needed to differentiate",
404 opts, allopt[i].minmatch);
405 break;
406 }
407 matchidx = i;
408 break;
409 }
410 }
411
412 if (!got_match) {
413 /* spin through the aliases to see if there's a match in those.
414 Note that if multiple delimited aliases for the same option
415 becomes desireable in the future, this is where you'll need
416 to split a delimited allopt[i].alias field into each
417 individual alias */
418
419 for (i = 0; i < OPTCOUNT; ++i) {
420 if (!allopt[i].alias)
421 continue;
422 got_match = match_optname(opts, allopt[i].alias,
423 (int) strlen(allopt[i].alias),
424 TRUE);
425 if (got_match) {
426 matchidx = i;
427 using_alias = TRUE;
428 break;
429 }
430 }
431 }
432
433 /* allow optfn's to test whether they were called from parseoptions() */
434 g.program_state.in_parseoptions++;
435
436 if (got_match && matchidx >= 0) {
437 duplicate = duplicate_opt_detection(matchidx);
438 if (duplicate && !allopt[matchidx].dupeok)
439 complain_about_duplicate(matchidx);
440
441 /* check for bad negation, so option functions don't have to */
442 if (negated && !allopt[matchidx].negateok) {
443 bad_negation(allopt[matchidx].name, TRUE);
444 return optn_err;
445 }
446
447 /*
448 * Now call the option's associated function via the function
449 * pointer for it in the allopt[] array, specifying a 'do_set' req.
450 */
451 if (allopt[matchidx].optfn) {
452 op = string_for_opt(opts, TRUE);
453 optresult = (*allopt[matchidx].optfn)(allopt[matchidx].idx,
454 do_set, negated, opts, op);
455 }
456 }
457
458 if (g.program_state.in_parseoptions > 0)
459 g.program_state.in_parseoptions--;
460
461 #if 0
462 /* This specialization shouldn't be needed any longer because each of
463 the individual options is part of the allopts[] list, thus already
464 taken care of in the for-loop above */
465 if (!got_match) {
466 int res = check_misc_menu_command(opts, op);
467
468 if (res >= 0)
469 optresult = spcfn_misc_menu_cmd(res, do_set, negated, opts, op);
470 if (optresult == optn_ok)
471 got_match = TRUE;
472 }
473 #endif
474
475 if (!got_match) {
476 /* Is it a symbol? */
477 if (strstr(opts, "S_") == opts && parsesymbols(opts, PRIMARY)) {
478 switch_symbols(TRUE);
479 check_gold_symbol();
480 optresult = optn_ok;
481 }
482 }
483
484 if (optresult == optn_silenterr)
485 return FALSE;
486 if (got_match && optresult == optn_err)
487 return FALSE;
488 if (optresult == optn_ok)
489 return retval;
490
491 /* out of valid options */
492 config_error_add("Unknown option '%s'", opts);
493 return FALSE;
494 }
495
496 static int
check_misc_menu_command(char * opts,char * op UNUSED)497 check_misc_menu_command(char *opts, char *op UNUSED)
498 {
499 int i;
500 const char *name_to_check;
501
502 /* check for menu command mapping */
503 for (i = 0; default_menu_cmd_info[i].name; i++) {
504 name_to_check = default_menu_cmd_info[i].name;
505 if (match_optname(opts, name_to_check,
506 (int) strlen(name_to_check), TRUE))
507 return i;
508 }
509 return -1;
510 }
511
512 /*
513 **********************************
514 *
515 * Per-option Functions
516 *
517 **********************************
518 */
519
520 static int
optfn_align(int optidx,int req,boolean negated,char * opts,char * op)521 optfn_align(int optidx, int req, boolean negated, char *opts, char *op)
522 {
523 if (req == do_init) {
524 return optn_ok;
525 }
526 if (req == do_set) {
527 if (parse_role_opts(optidx, negated, allopt[optidx].name, opts, &op)) {
528 if ((flags.initalign = str2align(op)) == ROLE_NONE) {
529 config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
530 return optn_err;
531 }
532 } else
533 return optn_silenterr;
534 return optn_ok;
535 }
536 if (req == get_val) {
537 if (!opts)
538 return optn_err;
539 Sprintf(opts, "%s", rolestring(flags.initalign, aligns, adj));
540 return optn_ok;
541 }
542 return optn_ok;
543 }
544
545
546 static int
optfn_align_message(int optidx,int req,boolean negated,char * opts,char * op)547 optfn_align_message(int optidx, int req, boolean negated, char *opts, char *op)
548 {
549 if (req == do_init) {
550 return optn_ok;
551 }
552 if (req == do_set) {
553 /* WINCAP align_message:[left|top|right|bottom] */
554
555 op = string_for_opt(opts, negated);
556 if ((op != empty_optstr) && !negated) {
557 if (!strncmpi(op, "left", sizeof "left" - 1))
558 iflags.wc_align_message = ALIGN_LEFT;
559 else if (!strncmpi(op, "top", sizeof "top" - 1))
560 iflags.wc_align_message = ALIGN_TOP;
561 else if (!strncmpi(op, "right", sizeof "right" - 1))
562 iflags.wc_align_message = ALIGN_RIGHT;
563 else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
564 iflags.wc_align_message = ALIGN_BOTTOM;
565 else {
566 config_error_add("Unknown %s parameter '%s'",
567 allopt[optidx].name, op);
568 return optn_err;
569 }
570 } else if (negated) {
571 bad_negation(allopt[optidx].name, TRUE);
572 return optn_err;
573 }
574 return optn_ok;
575 }
576 if (req == get_val) {
577 int which;
578
579 if (!opts)
580 return optn_err;
581 which = iflags.wc_align_message;
582 Sprintf(opts, "%s",
583 (which == ALIGN_TOP) ? "top"
584 : (which == ALIGN_LEFT) ? "left"
585 : (which == ALIGN_BOTTOM) ? "bottom"
586 : (which == ALIGN_RIGHT) ? "right"
587 : defopt);
588 return optn_ok;
589 }
590 if (req == do_handler) {
591 return handler_align_misc(optidx);
592 }
593 return optn_ok;
594 }
595
596 static int
optfn_align_status(int optidx,int req,boolean negated,char * opts,char * op)597 optfn_align_status(int optidx, int req, boolean negated, char *opts, char *op)
598 {
599 if (req == do_init) {
600 return optn_ok;
601 }
602 if (req == do_set) {
603 /* WINCAP align_status:[left|top|right|bottom] */
604 op = string_for_opt(opts, negated);
605 if ((op != empty_optstr) && !negated) {
606 if (!strncmpi(op, "left", sizeof "left" - 1))
607 iflags.wc_align_status = ALIGN_LEFT;
608 else if (!strncmpi(op, "top", sizeof "top" - 1))
609 iflags.wc_align_status = ALIGN_TOP;
610 else if (!strncmpi(op, "right", sizeof "right" - 1))
611 iflags.wc_align_status = ALIGN_RIGHT;
612 else if (!strncmpi(op, "bottom", sizeof "bottom" - 1))
613 iflags.wc_align_status = ALIGN_BOTTOM;
614 else {
615 config_error_add("Unknown %s parameter '%s'",
616 allopt[optidx].name, op);
617 return optn_err;
618 }
619 } else if (negated) {
620 bad_negation(allopt[optidx].name, TRUE);
621 return optn_err;
622 }
623 return optn_ok;
624 }
625 if (req == get_val) {
626 int which;
627
628 if (!opts)
629 return optn_err;
630 which = iflags.wc_align_status;
631 Sprintf(opts, "%s",
632 (which == ALIGN_TOP) ? "top"
633 : (which == ALIGN_LEFT) ? "left"
634 : (which == ALIGN_BOTTOM) ? "bottom"
635 : (which == ALIGN_RIGHT) ? "right"
636 : defopt);
637 return optn_ok;
638 }
639 if (req == do_handler) {
640 return handler_align_misc(optidx);
641 }
642 return optn_ok;
643 }
644
645 static int
optfn_altkeyhandler(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)646 optfn_altkeyhandler(int optidx UNUSED, int req, boolean negated UNUSED,
647 char *opts, char *op UNUSED)
648 {
649 if (req == do_init) {
650 return optn_ok;
651 }
652 if (req == do_set) {
653 /* altkeyhandler:string */
654
655 #if defined(WIN32) && defined(TTY_GRAPHICS)
656 if (op != empty_optstr) {
657 set_altkeyhandler(op);
658 } else {
659 return optn_err;
660 }
661 #endif
662 return optn_ok;
663 }
664 if (req == get_val) {
665 if (!opts)
666 return optn_err;
667 opts[0] = '\0';
668 #ifdef WIN32
669 Sprintf(opts, "%s",
670 iflags.altkeyhandler[0] ? iflags.altkeyhandler : "default");
671 #endif
672 return optn_ok;
673 }
674 return optn_ok;
675 }
676
677 static int
optfn_boulder(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)678 optfn_boulder(int optidx UNUSED, int req, boolean negated UNUSED,
679 char *opts, char *op UNUSED)
680 {
681 #ifdef BACKWARD_COMPAT
682 int clash = 0;
683 #endif
684
685 if (req == do_init) {
686 return optn_ok;
687 }
688 if (req == do_set) {
689 /* boulder:symbol */
690
691 #ifdef BACKWARD_COMPAT
692
693 /* if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
694 == empty_optstr)
695 */
696 if ((opts = string_for_opt(opts, FALSE)) == empty_optstr)
697 return FALSE;
698 escapes(opts, opts);
699 /* note: dummy monclass #0 has symbol value '\0'; we allow that--
700 attempting to set bouldersym to '^@'/'\0' will reset to default */
701 if (def_char_to_monclass(opts[0]) != MAXMCLASSES)
702 clash = opts[0] ? 1 : 0;
703 else if (opts[0] >= '1' && opts[0] < WARNCOUNT + '0')
704 clash = 2;
705 if (opts[0] < ' ') {
706 config_error_add("boulder symbol cannot be a control character");
707 return optn_ok;
708 } else if (clash) {
709 /* symbol chosen matches a used monster or warning
710 symbol which is not good - reject it */
711 config_error_add("Badoption - boulder symbol '%s' would conflict "
712 "with a %s symbol",
713 visctrl(opts[0]),
714 (clash == 1) ? "monster" : "warning");
715 } else {
716 /*
717 * Override the default boulder symbol.
718 */
719 g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X] = (nhsym) opts[0];
720 /* for 'initial', update of BOULDER symbol is done in
721 initoptions_finish(), after all symset options
722 have been processed */
723 if (!g.opt_initial) {
724 nhsym sym = get_othersym(SYM_BOULDER, PRIMARY);
725
726 if (sym)
727 g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
728 g.opt_need_redraw = TRUE;
729 }
730 }
731 return optn_ok;
732 #else
733 config_error_add("'%s' no longer supported; use S_boulder:c instead",
734 allopt[optidx].name);
735 return optn_err;
736 #endif
737 }
738 if (req == get_val) {
739 if (!opts)
740 return optn_err;
741 opts[0] = '\0';
742 #ifdef BACKWARD_COMPAT
743 Sprintf(opts, "%c",
744 g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
745 ? g.ov_primary_syms[SYM_BOULDER + SYM_OFF_X]
746 : g.showsyms[(int) objects[BOULDER].oc_class + SYM_OFF_O]);
747 #endif
748 return optn_ok;
749 }
750 return optn_ok;
751 }
752
753 static int
optfn_catname(int optidx,int req,boolean negated UNUSED,char * opts,char * op)754 optfn_catname(int optidx, int req, boolean negated UNUSED, char *opts, char *op)
755 {
756 if (req == do_init) {
757 return optn_ok;
758 }
759 if (req == do_set) {
760 if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
761 != empty_optstr) {
762 nmcpy(g.catname, op, PL_PSIZ);
763 } else {
764 return optn_err;
765 }
766 sanitize_name(g.catname);
767 return optn_ok;
768 }
769 if (req == get_val) {
770 if (!opts)
771 return optn_err;
772 Sprintf(opts, "%s", g.catname[0] ? g.catname : none);
773 return optn_ok;
774 }
775 return optn_ok;
776 }
777
778 #ifdef CURSES_GRAPHICS
779 static int
optfn_cursesgraphics(int optidx,int req,boolean negated,char * opts,char * op UNUSED)780 optfn_cursesgraphics(int optidx, int req, boolean negated,
781 char *opts, char *op UNUSED)
782 {
783 #ifdef BACKWARD_COMPAT
784 boolean badflag = FALSE;
785 #endif
786
787 if (req == do_init) {
788 return optn_ok;
789 }
790 if (req == do_set) {
791 /* "cursesgraphics" */
792
793 #ifdef BACKWARD_COMPAT
794 if (!negated) {
795 /* There is no rogue level cursesgraphics-specific set */
796 if (g.symset[PRIMARY].name) {
797 badflag = TRUE;
798 } else {
799 g.symset[PRIMARY].name = dupstr(allopt[optidx].name);
800 if (!read_sym_file(PRIMARY)) {
801 badflag = TRUE;
802 clear_symsetentry(PRIMARY, TRUE);
803 } else
804 switch_symbols(TRUE);
805 }
806 if (badflag) {
807 config_error_add("Failure to load symbol set %s.",
808 allopt[optidx].name);
809 return optn_err;
810 }
811 }
812 return optn_ok;
813 #else
814 config_error_add("'%s' no longer supported; use 'symset:%s' instead",
815 allopt[optidx].name, allopt[optidx].name);
816 return optn_err;
817 #endif
818 }
819 if (req == get_val) {
820 if (!opts)
821 return optn_err;
822 opts[0] = '\0';
823 return optn_ok;
824 }
825 return optn_ok;
826 }
827 #endif
828
829 static int
optfn_DECgraphics(int optidx,int req,boolean negated,char * opts,char * op UNUSED)830 optfn_DECgraphics(int optidx, int req, boolean negated,
831 char *opts, char *op UNUSED)
832 {
833 #ifdef BACKWARD_COMPAT
834 boolean badflag = FALSE;
835 #endif
836
837 if (req == do_init) {
838 return optn_ok;
839 }
840 if (req == do_set) {
841 /* "DECgraphics" */
842
843 #ifdef BACKWARD_COMPAT
844 if (!negated) {
845 /* There is no rogue level DECgraphics-specific set */
846 if (g.symset[PRIMARY].name) {
847 badflag = TRUE;
848 } else {
849 g.symset[PRIMARY].name = dupstr(allopt[optidx].name);
850 if (!read_sym_file(PRIMARY)) {
851 badflag = TRUE;
852 clear_symsetentry(PRIMARY, TRUE);
853 } else
854 switch_symbols(TRUE);
855 }
856 if (badflag) {
857 config_error_add("Failure to load symbol set %s.",
858 allopt[optidx].name);
859 return optn_err;
860 }
861 }
862 return optn_ok;
863 #else
864 config_error_add("'%s' no longer supported; use 'symset:%s' instead",
865 allopt[optidx].name, allopt[optidx].name);
866 return optn_err;
867 #endif
868 }
869 if (req == get_val) {
870 if (!opts)
871 return optn_err;
872 opts[0] = '\0';
873 return optn_ok;
874 }
875 return optn_ok;
876 }
877
878 static int
optfn_disclose(int optidx,int req,boolean negated,char * opts,char * op)879 optfn_disclose(int optidx, int req, boolean negated, char *opts, char *op)
880 {
881 int i, idx, prefix_val;
882 unsigned num;
883
884 if (req == do_init) {
885 return optn_ok;
886 }
887 if (req == do_set) {
888 /* things to disclose at end of game */
889
890 /*
891 * The order that the end_disclose options are stored:
892 * inventory, attribs, vanquished, genocided,
893 * conduct, overview.
894 * There is an array in flags:
895 * end_disclose[NUM_DISCLOSURE_OPT];
896 * with option settings for the each of the following:
897 * iagvc [see disclosure_options in decl.c]:
898 * Allowed setting values in that array are:
899 * DISCLOSE_PROMPT_DEFAULT_YES ask with default answer yes
900 * DISCLOSE_PROMPT_DEFAULT_NO ask with default answer no
901 * DISCLOSE_YES_WITHOUT_PROMPT always disclose and don't ask
902 * DISCLOSE_NO_WITHOUT_PROMPT never disclose and don't ask
903 * DISCLOSE_PROMPT_DEFAULT_SPECIAL for 'vanquished' only...
904 * DISCLOSE_SPECIAL_WITHOUT_PROMPT ...to set up sort order.
905 *
906 * Those setting values can be used in the option
907 * string as a prefix to get the desired behaviour.
908 *
909 * For backward compatibility, no prefix is required,
910 * and the presence of a i,a,g,v, or c without a prefix
911 * sets the corresponding value to DISCLOSE_YES_WITHOUT_PROMPT.
912 */
913
914 op = string_for_opt(opts, TRUE);
915 if (op != empty_optstr && negated) {
916 bad_negation(allopt[optidx].name, TRUE);
917 return optn_err;
918 }
919 /* "disclose" without a value means "all with prompting"
920 and negated means "none without prompting" */
921 if (op == empty_optstr || !strcmpi(op, "all")
922 || !strcmpi(op, "none")) {
923 if (op != empty_optstr && !strcmpi(op, "none"))
924 negated = TRUE;
925 for (num = 0; num < NUM_DISCLOSURE_OPTIONS; num++)
926 flags.end_disclose[num] = negated
927 ? DISCLOSE_NO_WITHOUT_PROMPT
928 : DISCLOSE_PROMPT_DEFAULT_YES;
929 return optn_ok;
930 }
931
932 num = 0;
933 prefix_val = -1;
934 while (*op && num < sizeof flags.end_disclose - 1) {
935 static char valid_settings[] = { DISCLOSE_PROMPT_DEFAULT_YES,
936 DISCLOSE_PROMPT_DEFAULT_NO,
937 DISCLOSE_PROMPT_DEFAULT_SPECIAL,
938 DISCLOSE_YES_WITHOUT_PROMPT,
939 DISCLOSE_NO_WITHOUT_PROMPT,
940 DISCLOSE_SPECIAL_WITHOUT_PROMPT,
941 '\0' };
942 register char c, *dop;
943
944 c = lowc(*op);
945 if (c == 'k')
946 c = 'v'; /* killed -> vanquished */
947 if (c == 'd')
948 c = 'o'; /* dungeon -> overview */
949 dop = index(disclosure_options, c);
950 if (dop) {
951 idx = (int) (dop - disclosure_options);
952 if (idx < 0 || idx > NUM_DISCLOSURE_OPTIONS - 1) {
953 impossible("bad disclosure index %d %c", idx, c);
954 continue;
955 }
956 if (prefix_val != -1) {
957 if (*dop != 'v') {
958 if (prefix_val == DISCLOSE_PROMPT_DEFAULT_SPECIAL)
959 prefix_val = DISCLOSE_PROMPT_DEFAULT_YES;
960 if (prefix_val == DISCLOSE_SPECIAL_WITHOUT_PROMPT)
961 prefix_val = DISCLOSE_YES_WITHOUT_PROMPT;
962 }
963 flags.end_disclose[idx] = prefix_val;
964 prefix_val = -1;
965 } else
966 flags.end_disclose[idx] = DISCLOSE_YES_WITHOUT_PROMPT;
967 } else if (index(valid_settings, c)) {
968 prefix_val = c;
969 } else if (c == ' ') {
970 ; /* do nothing */
971 } else {
972 config_error_add("Unknown %s parameter '%c'",
973 allopt[optidx].name, *op);
974 return optn_err;
975 }
976 op++;
977 }
978 return optn_ok;
979 }
980 if (req == get_val) {
981 if (!opts)
982 return optn_err;
983
984 opts[0] = '\0';
985 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
986 if (i)
987 (void) strkitten(opts, ' ');
988 (void) strkitten(opts, flags.end_disclose[i]);
989 (void) strkitten(opts, disclosure_options[i]);
990 }
991 return optn_ok;
992 }
993 if (req == do_handler) {
994 return handler_disclose();
995 }
996 return optn_ok;
997 }
998
999 static int
optfn_dogname(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op)1000 optfn_dogname(int optidx UNUSED, int req, boolean negated UNUSED,
1001 char *opts, char *op)
1002 {
1003 if (req == do_init) {
1004 return optn_ok;
1005 }
1006 if (req == do_set) {
1007 if (op != empty_optstr) {
1008 nmcpy(g.dogname, op, PL_PSIZ);
1009 } else {
1010 return optn_err;
1011 }
1012 sanitize_name(g.dogname);
1013 return optn_ok;
1014 }
1015 if (req == get_val) {
1016 if (!opts)
1017 return optn_err;
1018 Sprintf(opts, "%s", g.dogname[0] ? g.dogname : none);
1019 return optn_ok;
1020 }
1021 return optn_ok;
1022 }
1023
1024 static int
optfn_dungeon(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)1025 optfn_dungeon(int optidx UNUSED, int req, boolean negated UNUSED,
1026 char *opts, char *op UNUSED)
1027 {
1028 if (req == do_init) {
1029 return optn_ok;
1030 }
1031 if (req == do_set) {
1032 return optn_ok;
1033 }
1034 if (req == get_val) {
1035 if (!opts)
1036 return optn_err;
1037 Sprintf(opts, "%s", to_be_done);
1038 return optn_ok;
1039 }
1040 return optn_ok;
1041 }
1042
1043 static int
optfn_effects(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)1044 optfn_effects(int optidx UNUSED, int req, boolean negated UNUSED,
1045 char *opts, char *op UNUSED)
1046 {
1047 if (req == do_init) {
1048 return optn_ok;
1049 }
1050 if (req == do_set) {
1051 return optn_ok;
1052 }
1053 if (req == get_val) {
1054 if (!opts)
1055 return optn_err;
1056 Sprintf(opts, "%s", to_be_done);
1057 return optn_ok;
1058 }
1059 return optn_ok;
1060 }
1061
1062 static int
optfn_font_map(int optidx,int req,boolean negated,char * opts,char * op)1063 optfn_font_map(int optidx, int req, boolean negated, char *opts, char *op)
1064 {
1065 /* send them over to the prefix handling for font_ */
1066 return pfxfn_font(optidx, req, negated, opts, op);
1067 }
1068
1069 static int
optfn_font_menu(int optidx,int req,boolean negated,char * opts,char * op)1070 optfn_font_menu(int optidx, int req, boolean negated, char *opts, char *op)
1071 {
1072 /* send them over to the prefix handling for font_ */
1073 return pfxfn_font(optidx, req, negated, opts, op);
1074 }
1075
1076 static int
optfn_font_message(int optidx,int req,boolean negated,char * opts,char * op)1077 optfn_font_message(int optidx, int req, boolean negated, char *opts, char *op)
1078 {
1079 /* send them over to the prefix handling for font_ */
1080 return pfxfn_font(optidx, req, negated, opts, op);
1081 }
1082
1083 static int
optfn_font_size_map(int optidx,int req,boolean negated,char * opts,char * op)1084 optfn_font_size_map(int optidx, int req, boolean negated, char *opts, char *op)
1085 {
1086 /* send them over to the prefix handling for font_ */
1087 return pfxfn_font(optidx, req, negated, opts, op);
1088 }
1089
1090 static int
optfn_font_size_menu(int optidx,int req,boolean negated,char * opts,char * op)1091 optfn_font_size_menu(int optidx, int req, boolean negated, char *opts, char *op)
1092 {
1093 /* send them over to the prefix handling for font_ */
1094 return pfxfn_font(optidx, req, negated, opts, op);
1095 }
1096
1097 static int
optfn_font_size_message(int optidx,int req,boolean negated,char * opts,char * op)1098 optfn_font_size_message(int optidx, int req, boolean negated, char *opts, char *op)
1099 {
1100 /* send them over to the prefix handling for font_ */
1101 return pfxfn_font(optidx, req, negated, opts, op);
1102 }
1103
1104 static int
optfn_font_size_status(int optidx,int req,boolean negated,char * opts,char * op)1105 optfn_font_size_status(int optidx, int req, boolean negated, char *opts, char *op)
1106 {
1107 /* send them over to the prefix handling for font_ */
1108 return pfxfn_font(optidx, req, negated, opts, op);
1109 }
1110
1111 static int
optfn_font_size_text(int optidx,int req,boolean negated,char * opts,char * op)1112 optfn_font_size_text(int optidx, int req, boolean negated, char *opts, char *op)
1113 {
1114 /* send them over to the prefix handling for font_ */
1115 return pfxfn_font(optidx, req, negated, opts, op);
1116 }
1117
1118 static int
optfn_font_status(int optidx,int req,boolean negated,char * opts,char * op)1119 optfn_font_status(int optidx, int req, boolean negated, char *opts, char *op)
1120 {
1121 /* send them over to the prefix handling for font_ */
1122 return pfxfn_font(optidx, req, negated, opts, op);
1123 }
1124
1125 static int
optfn_font_text(int optidx,int req,boolean negated,char * opts,char * op)1126 optfn_font_text(int optidx, int req, boolean negated, char *opts, char *op)
1127 {
1128 /* send them over to the prefix handling for font_ */
1129 return pfxfn_font(optidx, req, negated, opts, op);
1130 }
1131
1132 static int
optfn_fruit(int optidx UNUSED,int req,boolean negated,char * opts,char * op)1133 optfn_fruit(int optidx UNUSED, int req, boolean negated,
1134 char *opts, char *op)
1135 {
1136 struct fruit *forig = 0;
1137
1138 if (req == do_init) {
1139 return optn_ok;
1140 }
1141 if (req == do_set) {
1142 op = string_for_opt(opts, negated || !g.opt_initial);
1143 if (negated) {
1144 if (op != empty_optstr) {
1145 bad_negation("fruit", TRUE);
1146 return optn_err;
1147 }
1148 op = empty_optstr;
1149 goto goodfruit;
1150 }
1151 if (op == empty_optstr)
1152 return optn_err;
1153 /* strip leading/trailing spaces, condense internal ones (3.6.2) */
1154 mungspaces(op);
1155 if (!g.opt_initial) {
1156 struct fruit *f;
1157 int fnum = 0;
1158
1159 /* count number of named fruits; if 'op' is found among them,
1160 then the count doesn't matter because we won't be adding it */
1161 f = fruit_from_name(op, FALSE, &fnum);
1162 if (!f) {
1163 if (!flags.made_fruit)
1164 forig = fruit_from_name(g.pl_fruit, FALSE, (int *) 0);
1165
1166 if (!forig && fnum >= 100) {
1167 config_error_add(
1168 "Doing that so many times isn't very fruitful.");
1169 return optn_ok;
1170 }
1171 }
1172 }
1173 goodfruit:
1174 nmcpy(g.pl_fruit, op, PL_FSIZ);
1175 sanitize_name(g.pl_fruit);
1176 /* OBJ_NAME(objects[SLIME_MOLD]) won't work for this after
1177 initialization; it gets changed to generic "fruit" */
1178 if (!*g.pl_fruit)
1179 nmcpy(g.pl_fruit, "slime mold", PL_FSIZ);
1180 if (!g.opt_initial) {
1181 /* if 'forig' is nonNull, we replace it rather than add
1182 a new fruit; it can only be nonNull if no fruits have
1183 been created since the previous name was put in place */
1184 (void) fruitadd(g.pl_fruit, forig);
1185 pline("Fruit is now \"%s\".", g.pl_fruit);
1186 }
1187 /* If initial, then initoptions is allowed to do it instead
1188 * of here (initoptions always has to do it even if there's
1189 * no fruit option at all. Also, we don't want people
1190 * setting multiple fruits in their options.)
1191 */
1192 return optn_ok;
1193 }
1194 if (req == get_val) {
1195 if (!opts)
1196 return optn_err;
1197 Sprintf(opts, "%s", g.pl_fruit);
1198 return optn_ok;
1199 }
1200 return optn_ok;
1201 }
1202
1203 static int
optfn_gender(int optidx,int req,boolean negated,char * opts,char * op)1204 optfn_gender(int optidx, int req, boolean negated, char *opts, char *op)
1205 {
1206 if (req == do_init) {
1207 return optn_ok;
1208 }
1209 if (req == do_set) {
1210 /* gender:string */
1211 if (parse_role_opts(optidx, negated, allopt[optidx].name, opts, &op)) {
1212 if ((flags.initgend = str2gend(op)) == ROLE_NONE) {
1213 config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
1214 return optn_err;
1215 } else
1216 flags.female = flags.initgend;
1217 } else
1218 return optn_silenterr;
1219 return optn_ok;
1220 }
1221 if (req == get_val) {
1222 if (!opts)
1223 return optn_err;
1224 Sprintf(opts, "%s", rolestring(flags.initgend, genders, adj));
1225 return optn_ok;
1226 }
1227 return optn_ok;
1228 }
1229
1230 static int
optfn_hilite_status(int optidx UNUSED,int req,boolean negated,char * opts,char * op)1231 optfn_hilite_status(int optidx UNUSED, int req, boolean negated,
1232 char *opts, char *op)
1233 {
1234 if (req == do_init) {
1235 return optn_ok;
1236 }
1237 if (req == do_set) {
1238 /* hilite fields in status prompt */
1239 #ifdef STATUS_HILITES
1240 op = string_for_opt(opts, TRUE);
1241 if (op != empty_optstr && negated) {
1242 clear_status_hilites();
1243 return optn_ok;
1244 } else if (op == empty_optstr) {
1245 config_error_add("Value is mandatory for hilite_status");
1246 return optn_err;
1247 }
1248 if (!parse_status_hl1(op, g.opt_from_file))
1249 return optn_err;
1250 return optn_ok;
1251 #else
1252 config_error_add("'%s' is not supported", allopt[optidx].name);
1253 return optn_err;
1254 #endif
1255 }
1256 if (req == get_val) {
1257 if (!opts)
1258 return optn_err;
1259 opts[0] = '\0';
1260 return optn_ok;
1261 }
1262 return optn_ok;
1263 }
1264
1265 static int
optfn_horsename(int optidx,int req,boolean negated UNUSED,char * opts,char * op)1266 optfn_horsename(int optidx, int req, boolean negated UNUSED, char *opts, char *op)
1267 {
1268 if (req == do_init) {
1269 return optn_ok;
1270 }
1271 if (req == do_set) {
1272 if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
1273 != empty_optstr) {
1274 nmcpy(g.horsename, op, PL_PSIZ);
1275 } else {
1276 return optn_err;
1277 }
1278 sanitize_name(g.horsename);
1279 return optn_ok;
1280 }
1281 if (req == get_val) {
1282 if (!opts)
1283 return optn_err;
1284 Sprintf(opts, "%s", g.horsename[0] ? g.horsename : none);
1285 return optn_ok;
1286 }
1287 return optn_ok;
1288 }
1289
1290 static int
optfn_IBMgraphics(int optidx,int req,boolean negated,char * opts,char * op UNUSED)1291 optfn_IBMgraphics(int optidx, int req, boolean negated,
1292 char *opts, char *op UNUSED)
1293 {
1294 #ifdef BACKWARD_COMPAT
1295 const char *sym_name = allopt[optidx].name;
1296 boolean badflag = FALSE;
1297 int i;
1298 #endif
1299
1300 if (req == do_init) {
1301 return optn_ok;
1302 }
1303 if (req == do_set) {
1304 /* "IBMgraphics" */
1305
1306 #ifdef BACKWARD_COMPAT
1307
1308 if (!negated) {
1309 for (i = 0; i < NUM_GRAPHICS; ++i) {
1310 if (g.symset[i].name) {
1311 badflag = TRUE;
1312 } else {
1313 g.symset[i].name = dupstr(sym_name);
1314 if (!read_sym_file(i)) {
1315 badflag = TRUE;
1316 clear_symsetentry(i, TRUE);
1317 break;
1318 }
1319 }
1320 }
1321 if (badflag) {
1322 config_error_add("Failure to load symbol set %s.", sym_name);
1323 return optn_err;
1324 } else {
1325 switch_symbols(TRUE);
1326 }
1327 }
1328 return optn_ok;
1329 #else
1330 config_error_add("'%s' no longer supported; use 'symset:%s' instead",
1331 allopt[optidx].name, allopt[optidx].name);
1332 return optn_err;
1333 #endif
1334 }
1335 if (req == get_val) {
1336 if (!opts)
1337 return optn_err;
1338 opts[0] = '\0';
1339 return optn_ok;
1340 }
1341 return optn_ok;
1342 }
1343
1344 #if defined(BACKWARD_COMPAT) && defined(MAC_GRAPHICS_ENV)
1345 static int
optfn_MACgraphics(int optidx,int req,boolean negated,char * opts,char * op)1346 optfn_MACgraphics(int optidx, int req, boolean negated, char *opts, char *op)
1347 {
1348 boolean badflag = FALSE;
1349
1350 if (req == do_init) {
1351 return optn_ok;
1352 }
1353 if (req == do_set) {
1354 /* "MACgraphics" */
1355 if (!negated) {
1356 if (g.symset[PRIMARY].name) {
1357 badflag = TRUE;
1358 } else {
1359 g.symset[PRIMARY].name = dupstr(allopt[optidx].name);
1360 if (!read_sym_file(PRIMARY)) {
1361 badflag = TRUE;
1362 clear_symsetentry(PRIMARY, TRUE);
1363 }
1364 }
1365 if (badflag) {
1366 config_error_add("Failure to load symbol set %s.",
1367 allopt[optidx].name);
1368 return FALSE;
1369 } else {
1370 switch_symbols(TRUE);
1371 }
1372 }
1373 return optn_ok;
1374 }
1375 if (req == get_val) {
1376 if (!opts)
1377 return optn_err;
1378 opts[0] = '\0';
1379 return optn_ok;
1380 }
1381 return optn_ok;
1382 }
1383 #endif /* BACKWARD_COMPAT && MAC_GRAPHICS_ENV */
1384
1385 static int
optfn_map_mode(int optidx,int req,boolean negated,char * opts,char * op)1386 optfn_map_mode(int optidx, int req, boolean negated, char *opts, char *op)
1387 {
1388 int i;
1389
1390 if (req == do_init) {
1391 return optn_ok;
1392 }
1393 if (req == do_set) {
1394 /* WINCAP
1395 *
1396 * map_mode:[tiles|ascii4x6|ascii6x8|ascii8x8|ascii16x8|ascii7x12
1397 * |ascii8x12|ascii16x12|ascii12x16|ascii10x18|fit_to_screen
1398 * |ascii_fit_to_screen|tiles_fit_to_screen]
1399 */
1400 op = string_for_opt(opts, negated);
1401 if (op != empty_optstr && !negated) {
1402 if (!strcmpi(op, "tiles"))
1403 iflags.wc_map_mode = MAP_MODE_TILES;
1404 else if (!strncmpi(op, "ascii4x6", sizeof "ascii4x6" - 1))
1405 iflags.wc_map_mode = MAP_MODE_ASCII4x6;
1406 else if (!strncmpi(op, "ascii6x8", sizeof "ascii6x8" - 1))
1407 iflags.wc_map_mode = MAP_MODE_ASCII6x8;
1408 else if (!strncmpi(op, "ascii8x8", sizeof "ascii8x8" - 1))
1409 iflags.wc_map_mode = MAP_MODE_ASCII8x8;
1410 else if (!strncmpi(op, "ascii16x8", sizeof "ascii16x8" - 1))
1411 iflags.wc_map_mode = MAP_MODE_ASCII16x8;
1412 else if (!strncmpi(op, "ascii7x12", sizeof "ascii7x12" - 1))
1413 iflags.wc_map_mode = MAP_MODE_ASCII7x12;
1414 else if (!strncmpi(op, "ascii8x12", sizeof "ascii8x12" - 1))
1415 iflags.wc_map_mode = MAP_MODE_ASCII8x12;
1416 else if (!strncmpi(op, "ascii16x12", sizeof "ascii16x12" - 1))
1417 iflags.wc_map_mode = MAP_MODE_ASCII16x12;
1418 else if (!strncmpi(op, "ascii12x16", sizeof "ascii12x16" - 1))
1419 iflags.wc_map_mode = MAP_MODE_ASCII12x16;
1420 else if (!strncmpi(op, "ascii10x18", sizeof "ascii10x18" - 1))
1421 iflags.wc_map_mode = MAP_MODE_ASCII10x18;
1422 else if (!strncmpi(op, "fit_to_screen",
1423 sizeof "fit_to_screen" - 1))
1424 iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
1425 else if (!strncmpi(op, "ascii_fit_to_screen",
1426 sizeof "ascii_fit_to_screen" - 1))
1427 iflags.wc_map_mode = MAP_MODE_ASCII_FIT_TO_SCREEN;
1428 else if (!strncmpi(op, "tiles_fit_to_screen",
1429 sizeof "tiles_fit_to_screen" - 1))
1430 iflags.wc_map_mode = MAP_MODE_TILES_FIT_TO_SCREEN;
1431 else {
1432 config_error_add("Unknown %s parameter '%s'",
1433 allopt[optidx].name, op);
1434 return optn_err;
1435 }
1436 } else if (negated) {
1437 bad_negation(allopt[optidx].name, TRUE);
1438 return optn_err;
1439 }
1440 return optn_ok;
1441 }
1442 if (req == get_val) {
1443 if (!opts)
1444 return optn_err;
1445 i = iflags.wc_map_mode;
1446 Sprintf(opts, "%s",
1447 (i == MAP_MODE_TILES) ? "tiles"
1448 : (i == MAP_MODE_ASCII4x6) ? "ascii4x6"
1449 : (i == MAP_MODE_ASCII6x8) ? "ascii6x8"
1450 : (i == MAP_MODE_ASCII8x8) ? "ascii8x8"
1451 : (i == MAP_MODE_ASCII16x8) ? "ascii16x8"
1452 : (i == MAP_MODE_ASCII7x12) ? "ascii7x12"
1453 : (i == MAP_MODE_ASCII8x12) ? "ascii8x12"
1454 : (i == MAP_MODE_ASCII16x12) ? "ascii16x12"
1455 : (i == MAP_MODE_ASCII12x16) ? "ascii12x16"
1456 : (i == MAP_MODE_ASCII10x18) ? "ascii10x18"
1457 : (i == MAP_MODE_ASCII_FIT_TO_SCREEN)
1458 ? "fit_to_screen"
1459 : defopt);
1460 return optn_ok;
1461 }
1462 return optn_ok;
1463 }
1464
1465 /* all the key assignment options for menu_* commands are identical
1466 but optlist.h treats them as distinct rather than sharing one */
1467 static int
shared_menu_optfn(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op)1468 shared_menu_optfn(int optidx UNUSED, int req, boolean negated UNUSED,
1469 char *opts, char *op)
1470 {
1471 if (req == do_init) {
1472 return optn_ok;
1473 }
1474 if (req == do_set) {
1475 int res = check_misc_menu_command(opts, op);
1476
1477 if (res < 0)
1478 return optn_err;
1479 return spcfn_misc_menu_cmd(res, req, negated, opts, op);
1480 }
1481 if (req == get_val) {
1482 if (!opts)
1483 return optn_err;
1484 Sprintf(opts, "%s", to_be_done);
1485 return optn_ok;
1486 }
1487 return optn_ok;
1488 }
1489
1490 static int
optfn_menu_deselect_all(int optidx,int req,boolean negated,char * opts,char * op)1491 optfn_menu_deselect_all(int optidx, int req, boolean negated,
1492 char *opts, char *op)
1493 {
1494 return shared_menu_optfn(optidx, req, negated, opts, op);
1495 }
1496
1497 static int
optfn_menu_deselect_page(int optidx,int req,boolean negated,char * opts,char * op)1498 optfn_menu_deselect_page(int optidx, int req, boolean negated,
1499 char *opts, char *op)
1500 {
1501 return shared_menu_optfn(optidx, req, negated, opts, op);
1502 }
1503
1504 static int
optfn_menu_first_page(int optidx,int req,boolean negated,char * opts,char * op)1505 optfn_menu_first_page(int optidx, int req, boolean negated,
1506 char *opts, char *op)
1507 {
1508 return shared_menu_optfn(optidx, req, negated, opts, op);
1509 }
1510
1511 static int
optfn_menu_invert_all(int optidx,int req,boolean negated,char * opts,char * op)1512 optfn_menu_invert_all(int optidx, int req, boolean negated,
1513 char *opts, char *op)
1514 {
1515 return shared_menu_optfn(optidx, req, negated, opts, op);
1516 }
1517
1518 static int
optfn_menu_invert_page(int optidx,int req,boolean negated,char * opts,char * op)1519 optfn_menu_invert_page(int optidx, int req, boolean negated,
1520 char *opts, char *op)
1521 {
1522 return shared_menu_optfn(optidx, req, negated, opts, op);
1523 }
1524
1525 static int
optfn_menu_last_page(int optidx,int req,boolean negated,char * opts,char * op)1526 optfn_menu_last_page(int optidx, int req, boolean negated,
1527 char *opts, char *op)
1528 {
1529 return shared_menu_optfn(optidx, req, negated, opts, op);
1530 }
1531
1532 static int
optfn_menu_next_page(int optidx,int req,boolean negated,char * opts,char * op)1533 optfn_menu_next_page(int optidx , int req, boolean negated,
1534 char *opts, char *op)
1535 {
1536 return shared_menu_optfn(optidx, req, negated, opts, op);
1537 }
1538
1539 static int
optfn_menu_previous_page(int optidx,int req,boolean negated,char * opts,char * op)1540 optfn_menu_previous_page(int optidx, int req, boolean negated,
1541 char *opts, char *op)
1542 {
1543 return shared_menu_optfn(optidx, req, negated, opts, op);
1544 }
1545
1546 static int
optfn_menu_search(int optidx,int req,boolean negated,char * opts,char * op)1547 optfn_menu_search(int optidx, int req, boolean negated,
1548 char *opts, char *op)
1549 {
1550 return shared_menu_optfn(optidx, req, negated, opts, op);
1551 }
1552
1553 static int
optfn_menu_select_all(int optidx,int req,boolean negated,char * opts,char * op)1554 optfn_menu_select_all(int optidx, int req, boolean negated,
1555 char *opts, char *op)
1556 {
1557 return shared_menu_optfn(optidx, req, negated, opts, op);
1558 }
1559
1560 static int
optfn_menu_select_page(int optidx,int req,boolean negated,char * opts,char * op)1561 optfn_menu_select_page(int optidx, int req, boolean negated,
1562 char *opts, char *op)
1563 {
1564 return shared_menu_optfn(optidx, req, negated, opts, op);
1565 }
1566
1567 static int
optfn_menu_shift_left(int optidx,int req,boolean negated,char * opts,char * op)1568 optfn_menu_shift_left(int optidx, int req, boolean negated,
1569 char *opts, char *op)
1570 {
1571 return shared_menu_optfn(optidx, req, negated, opts, op);
1572 }
1573
1574 static int
optfn_menu_shift_right(int optidx,int req,boolean negated,char * opts,char * op)1575 optfn_menu_shift_right(int optidx, int req, boolean negated,
1576 char *opts, char *op)
1577 {
1578 return shared_menu_optfn(optidx, req, negated, opts, op);
1579 }
1580
1581 /* end of shared key assignments for menu commands */
1582
1583 static int
optfn_menu_headings(int optidx,int req,boolean negated UNUSED,char * opts,char * op UNUSED)1584 optfn_menu_headings(int optidx, int req, boolean negated UNUSED,
1585 char *opts, char *op UNUSED)
1586 {
1587 int tmpattr;
1588
1589 if (req == do_init) {
1590 return optn_ok;
1591 }
1592 if (req == do_set) {
1593 if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
1594 == empty_optstr) {
1595 return optn_err;
1596 }
1597 tmpattr = match_str2attr(opts, TRUE);
1598 if (tmpattr == -1)
1599 return optn_err;
1600 iflags.menu_headings = tmpattr;
1601 return optn_ok;
1602 }
1603 if (req == get_val) {
1604 if (!opts)
1605 return optn_err;
1606 Sprintf(opts, "%s", attr2attrname(iflags.menu_headings));
1607 return optn_ok;
1608 }
1609 if (req == do_handler) {
1610 return handler_menu_headings();
1611 }
1612 return optn_ok;
1613 }
1614
1615 static int
optfn_menuinvertmode(int optidx,int req,boolean negated UNUSED,char * opts,char * op)1616 optfn_menuinvertmode(int optidx, int req, boolean negated UNUSED,
1617 char *opts, char *op)
1618 {
1619 if (req == do_init) {
1620 return optn_ok;
1621 }
1622 if (req == do_set) {
1623 /* menuinvertmode=0 or 1 or 2 (2 is experimental) */
1624 if (op != empty_optstr) {
1625 int mode = atoi(op);
1626
1627 if (mode < 0 || mode > 2) {
1628 config_error_add("Illegal %s parameter '%s'",
1629 allopt[optidx].name, op);
1630 return optn_err;
1631 }
1632 iflags.menuinvertmode = mode;
1633 }
1634 return optn_ok;
1635 }
1636 if (req == get_val) {
1637 if (!opts)
1638 return optn_err;
1639 Sprintf(opts, "%d", iflags.menuinvertmode);
1640 return optn_ok;
1641 }
1642 return optn_ok;
1643 }
1644
1645 static int
optfn_menustyle(int optidx,int req,boolean negated,char * opts,char * op)1646 optfn_menustyle(int optidx, int req, boolean negated, char *opts, char *op)
1647 {
1648 int tmp;
1649 boolean val_required; /* no initializer based on opts because this can be
1650 called with init and invalid opts and op */
1651
1652 if (req == do_init) {
1653 return optn_ok;
1654 }
1655 if (req == do_set) {
1656 /* menustyle:traditional or combination or full or partial */
1657
1658 val_required = (strlen(opts) > 5 && !negated);
1659 if ((op = string_for_opt(opts, !val_required)) == empty_optstr) {
1660 if (val_required)
1661 return optn_err; /* string_for_opt gave feedback */
1662 tmp = negated ? 'n' : 'f';
1663 } else {
1664 tmp = lowc(*op);
1665 }
1666 switch (tmp) {
1667 case 'n': /* none */
1668 case 't': /* traditional: prompt for class(es) by symbol,
1669 prompt for each item within class(es) one at a time */
1670 flags.menu_style = MENU_TRADITIONAL;
1671 break;
1672 case 'c': /* combination: prompt for class(es) by symbol,
1673 choose items within selected class(es) by menu */
1674 flags.menu_style = MENU_COMBINATION;
1675 break;
1676 case 'f': /* full: choose class(es) by first menu,
1677 choose items within selected class(es) by second menu */
1678 flags.menu_style = MENU_FULL;
1679 break;
1680 case 'p': /* partial: skip class filtering, choose items among all
1681 classes by menu */
1682 flags.menu_style = MENU_PARTIAL;
1683 break;
1684 default:
1685 config_error_add("Unknown %s parameter '%s'", allopt[optidx].name,
1686 op);
1687 return optn_err;
1688 }
1689 return optn_ok;
1690 }
1691 if (req == get_val) {
1692 if (!opts)
1693 return optn_err;
1694 Sprintf(opts, "%s", menutype[(int) flags.menu_style]);
1695 return optn_ok;
1696 }
1697 if (req == do_handler) {
1698 return handler_menustyle();
1699 }
1700 return optn_ok;
1701 }
1702
1703 static int
optfn_monsters(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)1704 optfn_monsters(int optidx UNUSED, int req, boolean negated UNUSED,
1705 char *opts, char *op UNUSED)
1706 {
1707 if (req == do_init) {
1708 return optn_ok;
1709 }
1710 if (req == do_set) {
1711 return optn_ok;
1712 }
1713 if (req == get_val) {
1714 if (!opts)
1715 return optn_err;
1716 opts[0] = '\0';
1717 return optn_ok;
1718 }
1719 return optn_ok;
1720 }
1721
1722 static int
optfn_mouse_support(int optidx,int req,boolean negated,char * opts,char * op)1723 optfn_mouse_support(int optidx, int req, boolean negated, char *opts, char *op)
1724 {
1725 boolean compat;
1726
1727 if (req == do_init) {
1728 return optn_ok;
1729 }
1730 if (req == do_set) {
1731 compat = (strlen(opts) <= 13);
1732 op = string_for_opt(opts, (compat || !g.opt_initial));
1733 if (op == empty_optstr) {
1734 if (compat || negated || g.opt_initial) {
1735 /* for backwards compatibility, "mouse_support" without a
1736 value is a synonym for mouse_support:1 */
1737 iflags.wc_mouse_support = !negated;
1738 }
1739 } else {
1740 int mode = atoi(op);
1741
1742 if (mode < 0 || mode > 2 || (mode == 0 && *op != '0')) {
1743 config_error_add("Illegal %s parameter '%s'",
1744 allopt[optidx].name, op);
1745 return optn_err;
1746 } else { /* mode >= 0 */
1747 iflags.wc_mouse_support = mode;
1748 }
1749 }
1750 return optn_ok;
1751 }
1752 if (req == get_val) {
1753 #ifdef WIN32
1754 #define MOUSEFIX1 ", QuickEdit off"
1755 #define MOUSEFIX2 ", QuickEdit unchanged"
1756 #else
1757 #define MOUSEFIX1 ", O/S adjusted"
1758 #define MOUSEFIX2 ", O/S unchanged"
1759 #endif
1760 static const char *mousemodes[][2] = {
1761 { "0=off", "" },
1762 { "1=on", MOUSEFIX1 },
1763 { "2=on", MOUSEFIX2 },
1764 };
1765 #undef MOUSEFIX1
1766 #undef MOUSEFIX2
1767 int ms = iflags.wc_mouse_support;
1768
1769 if (!opts)
1770 return optn_err;
1771
1772 if (ms >= 0 && ms <= 2)
1773 Sprintf(opts, "%s%s", mousemodes[ms][0], mousemodes[ms][1]);
1774 return optn_ok;
1775 }
1776 return optn_ok;
1777 }
1778
1779 /* whether the 'msg_window' option is used to control ^P behavior */
1780 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
1781 #define PREV_MSGS 1
1782 #else
1783 #define PREV_MSGS 0
1784 #endif
1785
1786 static int
optfn_msg_window(int optidx,int req,boolean negated,char * opts,char * op)1787 optfn_msg_window(int optidx, int req, boolean negated, char *opts, char *op)
1788 {
1789 int retval = optn_ok;
1790 #if PREV_MSGS
1791 int tmp;
1792 #else
1793 nhUse(optidx);
1794 nhUse(negated);
1795 nhUse(op);
1796 #endif
1797
1798 if (req == do_init) {
1799 return optn_ok;
1800 }
1801 if (req == do_set) {
1802 /* msg_window:single, combo, full or reversed */
1803
1804 /* allow option to be silently ignored by non-tty ports */
1805 #if PREV_MSGS
1806 if (op == empty_optstr) {
1807 tmp = negated ? 's' : 'f';
1808 } else {
1809 if (negated) {
1810 bad_negation(allopt[optidx].name, TRUE);
1811 return optn_err;
1812 }
1813 tmp = lowc(*op);
1814 }
1815 switch (tmp) {
1816 case 's': /* single message history cycle (default if negated) */
1817 case 'c': /* combination: first two as singles, then full page */
1818 case 'f': /* full page (default if specified without argument) */
1819 case 'r': /* full page in reverse order (LIFO; default for curses) */
1820 iflags.prevmsg_window = (char) tmp;
1821 break;
1822 default:
1823 config_error_add("Unknown %s parameter '%s'", allopt[optidx].name,
1824 op);
1825 retval = optn_err;
1826 }
1827 #endif
1828 return retval;
1829 }
1830 if (req == get_val) {
1831 if (!opts)
1832 return optn_err;
1833 opts[0] = '\0';
1834 #if PREV_MSGS
1835 tmp = iflags.prevmsg_window;
1836 if (WINDOWPORT("curses")) {
1837 if (tmp == 's' || tmp == 'c')
1838 tmp = iflags.prevmsg_window = 'r';
1839 }
1840 Sprintf(opts, "%s", (tmp == 's') ? "single"
1841 : (tmp == 'c') ? "combination"
1842 : (tmp == 'f') ? "full"
1843 : "reversed");
1844 #endif
1845 return optn_ok;
1846 }
1847 if (req == do_handler) {
1848 return handler_msg_window();
1849 }
1850 return optn_ok;
1851 }
1852
1853 static int
optfn_msghistory(int optidx,int req,boolean negated,char * opts,char * op)1854 optfn_msghistory(int optidx, int req, boolean negated, char *opts, char *op)
1855 {
1856 if (req == do_init) {
1857 return optn_ok;
1858 }
1859 if (req == do_set) {
1860 op = string_for_env_opt(allopt[optidx].name, opts, negated);
1861 if ((negated && op == empty_optstr)
1862 || (!negated && op != empty_optstr)) {
1863 iflags.msg_history = negated ? 0 : atoi(op);
1864 } else if (negated) {
1865 bad_negation(allopt[optidx].name, TRUE);
1866 return optn_err;
1867 }
1868 return optn_ok;
1869 }
1870 if (req == get_val) {
1871 if (!opts)
1872 return optn_err;
1873 Sprintf(opts, "%u", iflags.msg_history);
1874 return optn_ok;
1875 }
1876 return optn_ok;
1877 }
1878
1879 static int
optfn_name(int optidx,int req,boolean negated UNUSED,char * opts,char * op)1880 optfn_name(int optidx, int req, boolean negated UNUSED, char *opts, char *op)
1881 {
1882 if (req == do_init) {
1883 return optn_ok;
1884 }
1885 if (req == do_set) {
1886 /* name:string */
1887
1888 if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
1889 != empty_optstr) {
1890 nmcpy(g.plname, op, PL_NSIZ);
1891 } else
1892 return optn_err;
1893 return optn_ok;
1894 }
1895 if (req == get_val) {
1896 if (!opts)
1897 return optn_err;
1898 Sprintf(opts, "%s", g.plname);
1899 return optn_ok;
1900 }
1901 return optn_ok;
1902 }
1903
1904 static int
optfn_number_pad(int optidx,int req,boolean negated,char * opts,char * op)1905 optfn_number_pad(int optidx, int req, boolean negated, char *opts, char *op)
1906 {
1907 boolean compat;
1908
1909 if (req == do_init) {
1910 return optn_ok;
1911 }
1912 if (req == do_set) {
1913 compat = (strlen(opts) <= 10);
1914 op = string_for_opt(opts, (compat || !g.opt_initial));
1915 if (op == empty_optstr) {
1916 if (compat || negated || g.opt_initial) {
1917 /* for backwards compatibility, "number_pad" without a
1918 value is a synonym for number_pad:1 */
1919 iflags.num_pad = !negated;
1920 iflags.num_pad_mode = 0;
1921 }
1922 } else if (negated) {
1923 bad_negation(allopt[optidx].name, TRUE);
1924 return optn_err;
1925 } else {
1926 int mode = atoi(op);
1927
1928 if (mode < -1 || mode > 4 || (mode == 0 && *op != '0')) {
1929 config_error_add("Illegal %s parameter '%s'",
1930 allopt[optidx].name, op);
1931 return optn_err;
1932 } else if (mode <= 0) {
1933 iflags.num_pad = FALSE;
1934 /* German keyboard; y and z keys swapped */
1935 iflags.num_pad_mode = (mode < 0); /* 0 or 1 */
1936 } else { /* mode > 0 */
1937 iflags.num_pad = TRUE;
1938 iflags.num_pad_mode = 0;
1939 /* PC Hack / MSDOS compatibility */
1940 if (mode == 2 || mode == 4)
1941 iflags.num_pad_mode |= 1;
1942 /* phone keypad layout */
1943 if (mode == 3 || mode == 4)
1944 iflags.num_pad_mode |= 2;
1945 }
1946 }
1947 reset_commands(FALSE);
1948 number_pad(iflags.num_pad ? 1 : 0);
1949 return optn_ok;
1950 }
1951 if (req == get_val) {
1952 static const char *numpadmodes[] = {
1953 "0=off", "1=on", "2=on, MSDOS compatible",
1954 "3=on, phone-style layout",
1955 "4=on, phone layout, MSDOS compatible",
1956 "-1=off, y & z swapped", /*[5]*/
1957 };
1958 int indx = g.Cmd.num_pad
1959 ? (g.Cmd.phone_layout ? (g.Cmd.pcHack_compat ? 4 : 3)
1960 : (g.Cmd.pcHack_compat ? 2 : 1))
1961 : g.Cmd.swap_yz ? 5 : 0;
1962
1963 if (!opts)
1964 return optn_err;
1965 Strcpy(opts, numpadmodes[indx]);
1966 return optn_ok;
1967 }
1968 if (req == do_handler) {
1969 return handler_number_pad();
1970 }
1971 return optn_ok;
1972 }
1973
1974 static int
optfn_objects(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)1975 optfn_objects(int optidx UNUSED, int req, boolean negated UNUSED,
1976 char *opts, char *op UNUSED)
1977 {
1978 if (req == do_init) {
1979 return optn_ok;
1980 }
1981 if (req == do_set) {
1982 return optn_ok;
1983 }
1984 if (req == get_val) {
1985 if (!opts)
1986 return optn_err;
1987 Sprintf(opts, "%s", to_be_done);
1988 return optn_ok;
1989 }
1990 return optn_ok;
1991 }
1992
1993 static int
optfn_orientation(int optidx,int req,boolean negated,char * opts,char * op)1994 optfn_orientation(int optidx, int req, boolean negated, char *opts, char *op)
1995 {
1996 if (req == do_init) {
1997 return optn_ok;
1998 }
1999 else if (req == do_set) {
2000 flags.orientation = ORIENT_STRAIGHT;
2001 if (parse_role_opts(optidx, negated, allopt[optidx].name, opts, &op)) {
2002 if ((flags.orientation = str2orientation(op)) == ROLE_NONE) {
2003 config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
2004 return optn_err;
2005 } else {
2006 return optn_ok;
2007 }
2008 } else {
2009 return optn_err;
2010 }
2011 }
2012 else if (req == get_val) {
2013 if (!opts)
2014 return optn_err;
2015 Sprintf(opts, "%s", rolestring(flags.orientation, orientations,
2016 technical));
2017 return optn_ok;
2018 }
2019 return optn_ok;
2020
2021 }
2022
2023 static int
optfn_packorder(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op)2024 optfn_packorder(int optidx UNUSED, int req, boolean negated UNUSED,
2025 char *opts, char *op)
2026 {
2027 if (req == do_init) {
2028 return optn_ok;
2029 }
2030 if (req == do_set) {
2031 if (op == empty_optstr)
2032 return optn_err;
2033 if (!change_inv_order(op))
2034 return optn_err;
2035 return optn_ok;
2036 }
2037 if (req == get_val) {
2038 char ocl[MAXOCLASSES + 1];
2039
2040 if (!opts)
2041 return optn_err;
2042 oc_to_str(flags.inv_order, ocl);
2043 Sprintf(opts, "%s", ocl);
2044 return optn_ok;
2045 }
2046 return optn_ok;
2047 }
2048
2049 #ifdef CHANGE_COLOR
2050 static int
optfn_palette(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op)2051 optfn_palette(int optidx UNUSED, int req, boolean negated UNUSED, char *opts, char *op)
2052 {
2053 #ifndef WIN32
2054 int cnt, tmp, reverse;
2055 char *pt = op;
2056 long rgb;
2057 #endif
2058
2059 if (req == do_init) {
2060 return optn_ok;
2061 }
2062 if (req == do_set) {
2063 if (op == empty_optstr)
2064 return optn_err;
2065 /*
2066 Non-WIN32 variant
2067 palette (00c/880/-fff is blue/yellow/reverse white)
2068 WIN32 variant
2069 palette (adjust an RGB color in palette (color-R-G-B)
2070 */
2071
2072 if (match_optname(opts, "palette", 3, TRUE)
2073 #ifdef MAC
2074 || match_optname(opts, "hicolor", 3, TRUE)
2075 #endif
2076 ) {
2077 int color_number, color_incr;
2078
2079 #ifndef WIN32
2080 if (duplicate)
2081 complain_about_duplicate(optidx);
2082 #endif
2083 #ifdef MAC
2084 if (match_optname(opts, "hicolor", 3, TRUE)) {
2085 color_number = CLR_MAX + 4; /* HARDCODED inverse number */
2086 color_incr = -1;
2087 } else
2088 #endif
2089 {
2090 color_number = 0;
2091 color_incr = 1;
2092 }
2093 #ifdef WIN32
2094 if (!alternative_palette(op)) {
2095 config_error_add("Error in palette parameter '%s'", op);
2096 return optn_err;
2097 }
2098 #else
2099 while (*pt && color_number >= 0) {
2100 cnt = 3;
2101 rgb = 0L;
2102 if (*pt == '-') {
2103 reverse = 1;
2104 pt++;
2105 } else {
2106 reverse = 0;
2107 }
2108 while (cnt-- > 0) {
2109 if (*pt && *pt != '/') {
2110 #ifdef AMIGA
2111 rgb <<= 4;
2112 #else
2113 rgb <<= 8;
2114 #endif
2115 tmp = *pt++;
2116 if (isalpha((uchar) tmp)) {
2117 tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */
2118 } else {
2119 tmp &= 0xf; /* Digits in ASCII too... */
2120 }
2121 #ifndef AMIGA
2122 /* Add an extra so we fill f -> ff and 0 -> 00 */
2123 rgb += tmp << 4;
2124 #endif
2125 rgb += tmp;
2126 }
2127 }
2128 if (*pt == '/')
2129 pt++;
2130 change_color(color_number, rgb, reverse);
2131 color_number += color_incr;
2132 }
2133 #endif /* !WIN32 */
2134 if (!g.opt_initial) {
2135 g.opt_need_redraw = TRUE;
2136 }
2137 }
2138 return optn_ok;
2139 }
2140 if (req == get_val) {
2141 if (!opts)
2142 return optn_err;
2143 opts[0] = '\0';
2144 Sprintf(opts, "%s", get_color_string());
2145 return optn_ok;
2146 }
2147 return optn_ok;
2148 }
2149 #endif /* CHANGE_COLOR */
2150
2151 static int
optfn_paranoid_confirmation(int optidx,int req,boolean negated,char * opts,char * op)2152 optfn_paranoid_confirmation(int optidx, int req, boolean negated, char *opts, char *op)
2153 {
2154 int i;
2155
2156 if (req == do_init) {
2157 return optn_ok;
2158 }
2159 if (req == do_set) {
2160 /* user can change required response for some prompts (quit, die,
2161 hit), or add an extra prompt (pray, Remove) that isn't
2162 ordinarily there */
2163
2164 if (strncmpi(opts, "prayconfirm", 4) != 0) { /* not prayconfirm */
2165 /* at present we don't complain about duplicates for this
2166 option, but we do throw away the old settings whenever
2167 we process a new one [clearing old flags is essential
2168 for handling default paranoid_confirm:pray sanely] */
2169 flags.paranoia_bits = 0; /* clear all */
2170 if (negated) {
2171 flags.paranoia_bits = 0; /* [now redundant...] */
2172 } else if (op != empty_optstr) {
2173 char *pp, buf[BUFSZ];
2174
2175 strncpy(buf, op, sizeof buf - 1);
2176 buf[sizeof buf - 1] = '\0';
2177 op = mungspaces(buf);
2178 for (;;) {
2179 /* We're looking to parse
2180 "paranoid_confirm:whichone wheretwo whothree"
2181 and "paranoid_confirm:" prefix has already
2182 been stripped off by the time we get here */
2183 pp = index(op, ' ');
2184 if (pp)
2185 *pp = '\0';
2186 /* we aren't matching option names but match_optname()
2187 does what we want once we've broken the space
2188 delimited aggregate into separate tokens */
2189 for (i = 0; i < SIZE(paranoia); ++i) {
2190 if (match_optname(op, paranoia[i].argname,
2191 paranoia[i].argMinLen, FALSE)
2192 || (paranoia[i].synonym
2193 && match_optname(op, paranoia[i].synonym,
2194 paranoia[i].synMinLen,
2195 FALSE))) {
2196 if (paranoia[i].flagmask)
2197 flags.paranoia_bits |= paranoia[i].flagmask;
2198 else /* 0 == "none", so clear all */
2199 flags.paranoia_bits = 0;
2200 break;
2201 }
2202 }
2203 if (i == SIZE(paranoia)) {
2204 /* didn't match anything, so arg is bad;
2205 any flags already set will stay set */
2206 config_error_add("Unknown %s parameter '%s'",
2207 allopt[optidx].name, op);
2208 return optn_err;
2209 }
2210 /* move on to next token */
2211 if (pp)
2212 op = pp + 1;
2213 else
2214 break; /* no next token */
2215 } /* for(;;) */
2216 } else
2217 return optn_err;
2218 return optn_ok;
2219 } else { /* prayconfirm */
2220 if (negated)
2221 flags.paranoia_bits &= ~PARANOID_PRAY;
2222 else
2223 flags.paranoia_bits |= PARANOID_PRAY;
2224 }
2225 return optn_ok;
2226 }
2227 if (req == get_val) {
2228 char tmpbuf[QBUFSZ];
2229
2230 if (!opts)
2231 return optn_err;
2232 tmpbuf[0] = '\0';
2233 for (i = 0; paranoia[i].flagmask != 0; ++i)
2234 if (flags.paranoia_bits & paranoia[i].flagmask)
2235 Sprintf(eos(tmpbuf), " %s", paranoia[i].argname);
2236 Strcpy(opts, tmpbuf[0] ? &tmpbuf[1] : "none");
2237 return optn_ok;
2238 }
2239 if (req == do_handler) {
2240 return handler_paranoid_confirmation();
2241 }
2242 return optn_ok;
2243 }
2244
2245 static int
optfn_petattr(int optidx,int req,boolean negated,char * opts,char * op)2246 optfn_petattr(int optidx, int req, boolean negated, char *opts, char *op)
2247 {
2248 int retval = optn_ok;
2249
2250 if (req == do_init) {
2251 return optn_ok;
2252 }
2253 if (req == do_set) {
2254 /* WINCAP2 petattr:string */
2255
2256 op = string_for_opt(opts, negated);
2257 if (op != empty_optstr && negated) {
2258 bad_negation(allopt[optidx].name, TRUE);
2259 retval = optn_err;
2260 } else if (op != empty_optstr) {
2261 #ifdef CURSES_GRAPHICS
2262 int itmp = curses_read_attrs(op);
2263
2264 if (itmp == -1) {
2265 config_error_add("Unknown %s parameter '%s'",
2266 allopt[optidx].name, opts);
2267 retval = optn_err;
2268 } else
2269 iflags.wc2_petattr = itmp;
2270 #else
2271 /* non-curses windowports will not use this flag anyway
2272 * but the above will not compile if we don't have curses.
2273 * Just set it to a sensible default: */
2274 iflags.wc2_petattr = ATR_INVERSE;
2275 #endif
2276 } else if (negated) {
2277 iflags.wc2_petattr = ATR_NONE;
2278 }
2279 if (retval != optn_err) {
2280 iflags.hilite_pet = (iflags.wc2_petattr != ATR_NONE);
2281 if (!g.opt_initial)
2282 g.opt_need_redraw = TRUE;
2283 }
2284 return retval;
2285 }
2286 if (req == get_val) {
2287 if (!opts)
2288 return optn_err;
2289
2290 #ifdef CURSES_GRAPHICS
2291 if (WINDOWPORT("curses")) {
2292 char tmpbuf[QBUFSZ];
2293
2294 Strcpy(opts, curses_fmt_attrs(tmpbuf));
2295 } else
2296 #endif
2297 if (iflags.wc2_petattr != 0)
2298 Sprintf(opts, "0x%08x", iflags.wc2_petattr);
2299 else
2300 Strcpy(opts, defopt);
2301 }
2302 return optn_ok;
2303 }
2304
2305 static int
optfn_pettype(int optidx,int req,boolean negated,char * opts,char * op)2306 optfn_pettype(int optidx, int req, boolean negated, char *opts, char *op)
2307 {
2308 if (req == do_init) {
2309 return optn_ok;
2310 }
2311 if (req == do_set) {
2312 if ((op = string_for_env_opt(allopt[optidx].name, opts, negated))
2313 != empty_optstr) {
2314 switch (lowc(*op)) {
2315 case 'd': /* dog */
2316 g.preferred_pet = 'd';
2317 break;
2318 case 'c': /* cat */
2319 case 'f': /* feline */
2320 g.preferred_pet = 'c';
2321 break;
2322 case 'h': /* horse */
2323 case 'q': /* quadruped */
2324 /* avoids giving "unrecognized type of pet" but
2325 pet_type(dog.c) won't actually honor this */
2326 g.preferred_pet = 'h';
2327 break;
2328 case 'n': /* no pet */
2329 g.preferred_pet = 'n';
2330 break;
2331 case '*': /* random */
2332 g.preferred_pet = '\0';
2333 break;
2334 default:
2335 config_error_add("Unrecognized pet type '%s'.", op);
2336 return optn_err;
2337 break;
2338 }
2339 } else if (negated)
2340 g.preferred_pet = 'n';
2341 return optn_ok;
2342 }
2343 if (req == get_val) {
2344 if (!opts)
2345 return optn_err;
2346 Sprintf(opts, "%s", (g.preferred_pet == 'c') ? "cat"
2347 : (g.preferred_pet == 'd') ? "dog"
2348 : (g.preferred_pet == 'h') ? "horse"
2349 : (g.preferred_pet == 'n') ? "none"
2350 : "random");
2351 return optn_ok;
2352 }
2353 return optn_ok;
2354 }
2355
2356 static int
optfn_pickup_burden(int optidx,int req,boolean negated UNUSED,char * opts,char * op)2357 optfn_pickup_burden(int optidx, int req, boolean negated UNUSED, char *opts, char *op)
2358 {
2359 if (req == do_init) {
2360 return optn_ok;
2361 }
2362 if (req == do_set) {
2363 /* maximum burden picked up before prompt (Warren Cheung) */
2364
2365 if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
2366 != empty_optstr) {
2367 switch (lowc(*op)) {
2368 case 'u': /* Unencumbered */
2369 flags.pickup_burden = UNENCUMBERED;
2370 break;
2371 case 'b': /* Burdened (slight encumbrance) */
2372 flags.pickup_burden = SLT_ENCUMBER;
2373 break;
2374 case 's': /* streSsed (moderate encumbrance) */
2375 flags.pickup_burden = MOD_ENCUMBER;
2376 break;
2377 case 'n': /* straiNed (heavy encumbrance) */
2378 flags.pickup_burden = HVY_ENCUMBER;
2379 break;
2380 case 'o': /* OverTaxed (extreme encumbrance) */
2381 case 't':
2382 flags.pickup_burden = EXT_ENCUMBER;
2383 break;
2384 case 'l': /* overLoaded */
2385 flags.pickup_burden = OVERLOADED;
2386 break;
2387 default:
2388 config_error_add("Unknown %s parameter '%s'",
2389 allopt[optidx].name, op);
2390 return optn_err;
2391 }
2392 } else
2393 return optn_err;
2394 return optn_ok;
2395 }
2396 if (req == get_val) {
2397 if (!opts)
2398 return optn_err;
2399 Sprintf(opts, "%s", burdentype[flags.pickup_burden]);
2400 return optn_ok;
2401 }
2402 if (req == do_handler) {
2403 return handler_pickup_burden();
2404 }
2405 return optn_ok;
2406 }
2407
2408 static int
optfn_pickup_types(int optidx,int req,boolean negated,char * opts,char * op)2409 optfn_pickup_types(int optidx, int req, boolean negated, char *opts, char *op)
2410 {
2411 char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], qbuf[QBUFSZ],
2412 abuf[BUFSZ];
2413 int oc_sym;
2414 unsigned num;
2415 boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu;
2416
2417 if (req == do_init) {
2418 return optn_ok;
2419 }
2420 if (req == do_set) {
2421 /* types of objects to pick up automatically */
2422
2423 oc_to_str(flags.pickup_types, tbuf);
2424 flags.pickup_types[0] = '\0'; /* all */
2425 op = string_for_opt(opts, (compat || !g.opt_initial));
2426 if (op == empty_optstr) {
2427 if (compat || negated || g.opt_initial) {
2428 /* for backwards compatibility, "pickup" without a
2429 value is a synonym for autopickup of all types
2430 (and during initialization, we can't prompt yet) */
2431 flags.pickup = !negated;
2432 return optn_ok;
2433 }
2434 oc_to_str(flags.inv_order, ocl);
2435 use_menu = TRUE;
2436 if (flags.menu_style == MENU_TRADITIONAL
2437 || flags.menu_style == MENU_COMBINATION) {
2438 boolean wasspace;
2439
2440 use_menu = FALSE;
2441 Sprintf(qbuf, "New %s: [%s am] (%s)", allopt[optidx].name,
2442 ocl, *tbuf ? tbuf : "all");
2443 abuf[0] = '\0';
2444 getlin(qbuf, abuf);
2445 wasspace = (abuf[0] == ' '); /* before mungspaces */
2446 op = mungspaces(abuf);
2447 if (wasspace && !abuf[0])
2448 ; /* one or more spaces will remove old value */
2449 else if (!abuf[0] || abuf[0] == '\033')
2450 op = tbuf; /* restore */
2451 else if (abuf[0] == 'm')
2452 use_menu = TRUE;
2453 /* note: abuf[0]=='a' is already handled via clearing the
2454 the old value (above) as a default action */
2455 }
2456 if (use_menu) {
2457 if (wizard && !index(ocl, VENOM_SYM))
2458 strkitten(ocl, VENOM_SYM);
2459 (void) choose_classes_menu("Autopickup what?", 1, TRUE, ocl,
2460 tbuf);
2461 op = tbuf;
2462 }
2463 }
2464 if (negated) {
2465 bad_negation(allopt[optidx].name, TRUE);
2466 return optn_err;
2467 }
2468 while (*op == ' ')
2469 op++;
2470 if (*op != 'a' && *op != 'A') {
2471 num = 0;
2472 while (*op) {
2473 oc_sym = def_char_to_objclass(*op);
2474 /* make sure all are valid obj symbols occurring once */
2475 if (oc_sym != MAXOCLASSES
2476 && !index(flags.pickup_types, oc_sym)) {
2477 flags.pickup_types[num] = (char) oc_sym;
2478 flags.pickup_types[++num] = '\0';
2479 } else
2480 badopt = TRUE;
2481 op++;
2482 }
2483 if (badopt) {
2484 config_error_add("Unknown %s parameter '%s'",
2485 allopt[optidx].name, op);
2486 return optn_err;
2487 }
2488 }
2489 return optn_ok;
2490 }
2491 if (req == get_val) {
2492 if (!opts)
2493 return optn_err;
2494 oc_to_str(flags.pickup_types, ocl);
2495 Sprintf(opts, "%s", ocl[0] ? ocl : "all");
2496 return optn_ok;
2497 }
2498 if (req == do_handler) {
2499 return handler_pickup_types();
2500 }
2501 return optn_ok;
2502 }
2503
2504 static int
optfn_pile_limit(int optidx,int req,boolean negated,char * opts,char * op)2505 optfn_pile_limit(int optidx, int req, boolean negated, char *opts, char *op)
2506 {
2507 if (req == do_init) {
2508 return optn_ok;
2509 }
2510 if (req == do_set) {
2511 /* pile limit: when walking over objects, number which triggers
2512 "there are several/many objects here" instead of listing them
2513 */
2514
2515 op = string_for_opt(opts, negated);
2516 if ((negated && op == empty_optstr)
2517 || (!negated && op != empty_optstr))
2518 flags.pile_limit = negated ? 0 : atoi(op);
2519 else if (negated) {
2520 bad_negation(allopt[optidx].name, TRUE);
2521 return optn_err;
2522 } else /* op == empty_optstr */
2523 flags.pile_limit = PILE_LIMIT_DFLT;
2524 /* sanity check */
2525 if (flags.pile_limit < 0)
2526 flags.pile_limit = PILE_LIMIT_DFLT;
2527 return optn_ok;
2528 }
2529 if (req == get_val) {
2530 if (!opts)
2531 return optn_err;
2532 Sprintf(opts, "%d", flags.pile_limit);
2533 return optn_ok;
2534 }
2535 return optn_ok;
2536 }
2537
2538 static int
optfn_player_selection(int optidx,int req,boolean negated,char * opts,char * op)2539 optfn_player_selection(int optidx, int req, boolean negated, char *opts, char *op)
2540 {
2541 if (req == do_init) {
2542 return optn_ok;
2543 }
2544 if (req == do_set) {
2545 /* WINCAP player_selection: dialog | prompt/prompts/prompting */
2546
2547 op = string_for_opt(opts, negated);
2548 if (op != empty_optstr && !negated) {
2549 if (!strncmpi(op, "dialog", sizeof "dialog" - 1)) {
2550 iflags.wc_player_selection = VIA_DIALOG;
2551 } else if (!strncmpi(op, "prompt", sizeof "prompt" - 1)) {
2552 iflags.wc_player_selection = VIA_PROMPTS;
2553 } else {
2554 config_error_add("Unknown %s parameter '%s'",
2555 allopt[optidx].name, op);
2556 return optn_err;
2557 }
2558 }
2559 return optn_ok;
2560 }
2561 if (req == get_val) {
2562 if (!opts)
2563 return optn_err;
2564 Sprintf(opts, "%s", iflags.wc_player_selection ? "prompts" : "dialog");
2565 return optn_ok;
2566 }
2567 return optn_ok;
2568 }
2569
2570 static int
optfn_playmode(int optidx,int req,boolean negated,char * opts,char * op)2571 optfn_playmode(int optidx, int req, boolean negated, char *opts, char *op)
2572 {
2573 if (req == do_init) {
2574 return optn_ok;
2575 }
2576 if (req == do_set) {
2577 /* play mode: normal, explore/discovery, or debug/wizard */
2578
2579 if (duplicate || negated)
2580 return optn_err;
2581 if (op == empty_optstr)
2582 return optn_err;
2583 if (!strncmpi(op, "normal", 6) || !strcmpi(op, "play")) {
2584 wizard = discover = FALSE;
2585 } else if (!strncmpi(op, "explore", 6)
2586 || !strncmpi(op, "discovery", 6)) {
2587 wizard = FALSE, discover = TRUE;
2588 } else if (!strncmpi(op, "debug", 5) || !strncmpi(op, "wizard", 6)) {
2589 wizard = TRUE, discover = FALSE;
2590 } else {
2591 config_error_add("Invalid value for \"%s\":%s",
2592 allopt[optidx].name, op);
2593 return optn_err;
2594 }
2595 return optn_ok;
2596 }
2597 if (req == get_val) {
2598 if (!opts)
2599 return optn_err;
2600 Strcpy(opts, wizard ? "debug" : discover ? "explore" : "normal");
2601 return optn_ok;
2602 }
2603 return optn_ok;
2604 }
2605
2606 static int
optfn_polyinit(int optidx,int req,boolean negated,char * opts,char * op)2607 optfn_polyinit(int optidx, int req, boolean negated, char *opts, char *op)
2608 {
2609 nhUse(negated);
2610 if (req == do_init) {
2611 return optn_ok;
2612 }
2613 else if (req == do_set) {
2614 int monnum = name_to_mon(op, (int *) 0);
2615 struct permonst *mdat;
2616 if (monnum < LOW_PM) {
2617 config_error_add("%s: no such monster '%s'.",
2618 allopt[optidx].name, op);
2619 return optn_err;
2620 }
2621 mdat = &mons[monnum];
2622 if (unique_corpstat(mdat)) {
2623 config_error_add("cannot polyinit into unique monster '%s'.", op);
2624 return optn_err;
2625 }
2626 else if (is_mplayer(mdat)) {
2627 config_error_add("cannot polyinit into player monster '%s'.", op);
2628 return optn_err;
2629 }
2630 flags.polyinit_mnum = monnum;
2631 return optn_ok;
2632 }
2633 else if (req == get_val) {
2634 if (!opts)
2635 return optn_err;
2636 Sprintf(opts, "%s", mons[flags.polyinit_mnum].pmnames[NEUTRAL]);
2637 return optn_ok;
2638 }
2639 return optn_ok;
2640 }
2641
2642 static int
optfn_race(int optidx,int req,boolean negated,char * opts,char * op)2643 optfn_race(int optidx, int req, boolean negated, char *opts, char *op)
2644 {
2645 if (req == do_init) {
2646 return optn_ok;
2647 }
2648 if (req == do_set) {
2649 /* race:string */
2650 if (parse_role_opts(optidx, negated, allopt[optidx].name, opts, &op)) {
2651 if ((flags.initrace = str2race(op)) == ROLE_NONE) {
2652 config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
2653 return optn_err;
2654 } else /* Backwards compatibility */
2655 g.pl_race = *op;
2656 } else
2657 return optn_silenterr;
2658 return optn_ok;
2659 }
2660 if (req == get_val) {
2661 if (!opts)
2662 return optn_err;
2663 Sprintf(opts, "%s", rolestring(flags.initrace, races, noun));
2664 return optn_ok;
2665 }
2666 return optn_ok;
2667 }
2668
2669 static int
optfn_role(int optidx,int req,boolean negated,char * opts,char * op)2670 optfn_role(int optidx, int req, boolean negated, char *opts, char *op)
2671 {
2672 if (req == do_init) {
2673 return optn_ok;
2674 }
2675 if (req == do_set) {
2676 if (parse_role_opts(optidx, negated, allopt[optidx].name, opts, &op)) {
2677 if ((flags.initrole = str2role(op)) == ROLE_NONE) {
2678 config_error_add("Unknown %s '%s'", allopt[optidx].name, op);
2679 return optn_err;
2680 } else /* Backwards compatibility */
2681 nmcpy(g.pl_character, op, PL_NSIZ);
2682 } else
2683 return optn_silenterr;
2684 return optn_ok;
2685 }
2686 if (req == get_val) {
2687 if (!opts)
2688 return optn_err;
2689 Sprintf(opts, "%s", rolestring(flags.initrole, roles, name.m));
2690 return optn_ok;
2691 }
2692 return optn_ok;
2693 }
2694
2695 static int
optfn_runmode(int optidx,int req,boolean negated,char * opts,char * op)2696 optfn_runmode(int optidx, int req, boolean negated, char *opts, char *op)
2697 {
2698 if (req == do_init) {
2699 return optn_ok;
2700 }
2701 if (req == do_set) {
2702 if (negated) {
2703 flags.runmode = RUN_TPORT;
2704 } else if (op != empty_optstr) {
2705 if (!strncmpi(op, "teleport", strlen(op)))
2706 flags.runmode = RUN_TPORT;
2707 else if (!strncmpi(op, "run", strlen(op)))
2708 flags.runmode = RUN_LEAP;
2709 else if (!strncmpi(op, "walk", strlen(op)))
2710 flags.runmode = RUN_STEP;
2711 else if (!strncmpi(op, "crawl", strlen(op)))
2712 flags.runmode = RUN_CRAWL;
2713 else {
2714 config_error_add("Unknown %s parameter '%s'",
2715 allopt[optidx].name, op);
2716 return optn_err;
2717 }
2718 } else {
2719 config_error_add("Value is mandatory for %s", allopt[optidx].name);
2720 return optn_err;
2721 }
2722 return optn_ok;
2723 }
2724 if (req == get_val) {
2725 if (!opts)
2726 return optn_err;
2727 Sprintf(opts, "%s", runmodes[flags.runmode]);
2728 return optn_ok;
2729 }
2730 if (req == do_handler) {
2731 return handler_runmode();
2732 }
2733 return optn_ok;
2734 }
2735
2736 static int
optfn_scores(int optidx,int req,boolean negated,char * opts,char * op)2737 optfn_scores(int optidx, int req, boolean negated, char *opts, char *op)
2738 {
2739 if (req == do_init) {
2740 return optn_ok;
2741 }
2742 if (req == do_set) {
2743 /* scores:5t[op] 5a[round] o[wn] */
2744
2745 if ((op = string_for_opt(opts, FALSE)) == empty_optstr)
2746 return optn_err;
2747
2748 /* 3.7: earlier versions left old values for unspecified arguments
2749 if player's scores:foo option only specified some of the three;
2750 in particular, attempting to use 'scores:own' rather than
2751 'scores:0 top/0 around/own' didn't work as intended */
2752 flags.end_top = flags.end_around = 0, flags.end_own = FALSE;
2753
2754 if (negated)
2755 op = eos(op);
2756
2757 while (*op) {
2758 int inum = 1;
2759
2760 negated = (*op == '!');
2761 if (negated)
2762 op++;
2763
2764 if (digit(*op)) {
2765 inum = atoi(op);
2766 while (digit(*op))
2767 op++;
2768 }
2769 while (*op == ' ')
2770 op++;
2771
2772 switch (lowc(*op)) {
2773 case 't':
2774 flags.end_top = negated ? 0 : inum;
2775 break;
2776 case 'a':
2777 flags.end_around = negated ? 0 : inum;
2778 break;
2779 case 'o':
2780 flags.end_own = (negated || !inum) ? FALSE : TRUE;
2781 break;
2782 case 'n': /* none */
2783 flags.end_top = flags.end_around = 0, flags.end_own = FALSE;
2784 break;
2785 case '-':
2786 if (digit(*(op + 1))) {
2787 config_error_add(
2788 "Values for %s:top and %s:around must not be negative",
2789 allopt[optidx].name,
2790 allopt[optidx].name);
2791 return optn_silenterr;
2792 }
2793 /*FALLTHRU*/
2794 default:
2795 config_error_add("Unknown %s parameter '%s'",
2796 allopt[optidx].name, op);
2797 return optn_silenterr;
2798 }
2799 /* "3a" is sufficient but accept "3around" (or "3abracadabra") */
2800 while (letter(*op))
2801 op++;
2802 /* t, a, and o can be separated by space(s) or slash or both */
2803 while (*op == ' ')
2804 op++;
2805 if (*op == '/')
2806 op++;
2807 }
2808 return optn_ok;
2809 }
2810 if (req == get_val) {
2811 if (!opts)
2812 return optn_err;
2813 *opts = '\0';
2814 if (flags.end_top > 0)
2815 Sprintf(opts, "%d top", flags.end_top);
2816 if (flags.end_around > 0)
2817 Sprintf(eos(opts), "%s%d around",
2818 (flags.end_top > 0) ? "/" : "", flags.end_around);
2819 if (flags.end_own)
2820 Sprintf(eos(opts), "%sown",
2821 (flags.end_top > 0 || flags.end_around > 0) ? "/" : "");
2822 if (!*opts)
2823 Strcpy(opts, "none");
2824 return optn_ok;
2825 }
2826 return optn_ok;
2827 }
2828
2829 static int
optfn_scroll_amount(int optidx,int req,boolean negated,char * opts,char * op)2830 optfn_scroll_amount(int optidx, int req, boolean negated, char *opts, char *op)
2831 {
2832 if (req == do_init) {
2833 return optn_ok;
2834 }
2835 if (req == do_set) {
2836 /* WINCAP scroll_amount:nn */
2837
2838 op = string_for_opt(opts, negated);
2839 if ((negated && op == empty_optstr)
2840 || (!negated && op != empty_optstr)) {
2841 iflags.wc_scroll_amount = negated ? 1 : atoi(op);
2842 } else if (negated) {
2843 bad_negation(allopt[optidx].name, TRUE);
2844 return optn_err;
2845 }
2846 return optn_ok;
2847 }
2848 if (req == get_val) {
2849 if (!opts)
2850 return optn_err;
2851 if (iflags.wc_scroll_amount)
2852 Sprintf(opts, "%d", iflags.wc_scroll_amount);
2853 else
2854 Strcpy(opts, defopt);
2855 return optn_ok;
2856 }
2857 return optn_ok;
2858 }
2859
2860 static int
optfn_scroll_margin(int optidx,int req,boolean negated,char * opts,char * op)2861 optfn_scroll_margin(int optidx, int req, boolean negated, char *opts, char *op)
2862 {
2863 if (req == do_init) {
2864 return optn_ok;
2865 }
2866 if (req == do_set) {
2867 /* WINCAP scroll_margin:nn */
2868 op = string_for_opt(opts, negated);
2869 if ((negated && op == empty_optstr)
2870 || (!negated && op != empty_optstr)) {
2871 iflags.wc_scroll_margin = negated ? 5 : atoi(op);
2872 } else if (negated) {
2873 bad_negation(allopt[optidx].name, TRUE);
2874 return optn_err;
2875 }
2876 return optn_ok;
2877 }
2878 if (req == get_val) {
2879 if (!opts)
2880 return optn_err;
2881 if (iflags.wc_scroll_margin)
2882 Sprintf(opts, "%d", iflags.wc_scroll_margin);
2883 else
2884 Strcpy(opts, defopt);
2885 return optn_ok;
2886 }
2887 return optn_ok;
2888 }
2889
2890 static int
optfn_sortdiscoveries(int optidx,int req,boolean negated,char * opts,char * op)2891 optfn_sortdiscoveries(int optidx, int req, boolean negated, char *opts, char *op)
2892 {
2893 if (req == do_init) {
2894 flags.discosort = 'o';
2895 return optn_ok;
2896 }
2897 if (req == do_set) {
2898 op = string_for_env_opt(allopt[optidx].name, opts, FALSE);
2899 if (negated) {
2900 flags.discosort = 'o';
2901 } else if (op != empty_optstr) {
2902 switch (lowc(*op)) {
2903 case '0':
2904 case 'o': /* order of discovery */
2905 flags.discosort = 'o';
2906 break;
2907 case '1':
2908 case 's': /* sortloot order (subclasses for some classes) */
2909 flags.discosort = 's';
2910 break;
2911 case '2':
2912 case 'c': /* alphabetical within each class */
2913 flags.discosort = 'c';
2914 break;
2915 case '3':
2916 case 'a': /* alphabetical across all classes */
2917 flags.discosort = 'a';
2918 break;
2919 default:
2920 config_error_add("Unknown %s parameter '%s'",
2921 allopt[optidx].name, op);
2922 return optn_silenterr;
2923 }
2924 } else
2925 return optn_err;
2926 return optn_ok;
2927 }
2928 if (req == get_val) {
2929 extern const char *const disco_orders_descr[]; /* o_init.c */
2930 extern const char disco_order_let[];
2931 const char *p = index(disco_order_let, flags.discosort);
2932
2933 if (!p)
2934 flags.discosort = 'o', p = disco_order_let;
2935 Strcpy(opts, disco_orders_descr[p - disco_order_let]);
2936 return optn_ok;
2937 }
2938 if (req == do_handler) {
2939 /* return handler_sortdiscoveries(); */
2940 (void) choose_disco_sort(0); /* o_init.c */
2941 }
2942 return optn_ok;
2943 }
2944
2945 static int
optfn_sortloot(int optidx,int req,boolean negated UNUSED,char * opts,char * op)2946 optfn_sortloot(int optidx, int req, boolean negated UNUSED,
2947 char *opts, char *op)
2948 {
2949 int i;
2950
2951 if (req == do_init) {
2952 return optn_ok;
2953 }
2954 if (req == do_set) {
2955 op = string_for_env_opt(allopt[optidx].name, opts, FALSE);
2956 if (op != empty_optstr) {
2957 char c = lowc(*op);
2958
2959 switch (c) {
2960 case 'n': /* none */
2961 case 'l': /* loot (pickup) */
2962 case 'f': /* full (pickup + invent) */
2963 flags.sortloot = c;
2964 break;
2965 default:
2966 config_error_add("Unknown %s parameter '%s'",
2967 allopt[optidx].name, op);
2968 return optn_err;
2969 }
2970 } else
2971 return optn_err;
2972 return optn_ok;
2973 }
2974 if (req == get_val) {
2975 if (!opts)
2976 return optn_err;
2977 for (i = 0; i < SIZE(sortltype); i++)
2978 if (flags.sortloot == sortltype[i][0]) {
2979 Strcpy(opts, sortltype[i]);
2980 break;
2981 }
2982 return optn_ok;
2983 }
2984 if (req == do_handler) {
2985 return handler_sortloot();
2986 }
2987 return optn_ok;
2988 }
2989
2990 static int
optfn_statushilites(int optidx UNUSED,int req,boolean negated,char * opts,char * op)2991 optfn_statushilites(int optidx UNUSED, int req, boolean negated,
2992 char *opts, char *op)
2993 {
2994 if (req == do_init) {
2995 return optn_ok;
2996 }
2997 if (req == do_set) {
2998 /* control over whether highlights should be displayed, and for
2999 * how long */
3000
3001 #ifdef STATUS_HILITES
3002 if (negated) {
3003 iflags.hilite_delta = 0L;
3004 } else {
3005 op = string_for_opt(opts, TRUE);
3006 iflags.hilite_delta =
3007 (op == empty_optstr || !*op) ? 3L : atol(op);
3008 if (iflags.hilite_delta < 0L)
3009 iflags.hilite_delta = 1L;
3010 }
3011 if (!g.opt_from_file)
3012 reset_status_hilites();
3013 return optn_ok;
3014 #else
3015 config_error_add("'%s' is not supported", allopt[optidx].name);
3016 return optn_err;
3017 #endif
3018 }
3019 if (req == get_val) {
3020 if (!opts)
3021 return optn_err;
3022 #ifdef STATUS_HILITES
3023 if (!iflags.hilite_delta)
3024 Strcpy(opts, "0 (off: don't highlight status fields)");
3025 else
3026 Sprintf(opts, "%ld (on: highlight status for %ld turns)",
3027 iflags.hilite_delta, iflags.hilite_delta);
3028 #else
3029 Strcpy(opts, "unsupported");
3030 #endif
3031 return optn_ok;
3032 }
3033 return optn_ok;
3034 }
3035
3036 static int
optfn_statuslines(int optidx,int req,boolean negated,char * opts,char * op)3037 optfn_statuslines(int optidx, int req, boolean negated, char *opts, char *op)
3038 {
3039 int retval = optn_ok, itmp = 0;
3040
3041 if (req == do_init) {
3042 return optn_ok;
3043 }
3044 if (req == do_set) {
3045 /* WINCAP2
3046 * statuslines:n */
3047
3048 op = string_for_opt(opts, negated);
3049 if (negated) {
3050 bad_negation(allopt[optidx].name, TRUE);
3051 itmp = 2;
3052 retval = optn_err;
3053 } else if (op != empty_optstr) {
3054 itmp = atoi(op);
3055 }
3056 if (itmp < 2 || itmp > 3) {
3057 config_error_add("'%s:%s' is invalid; must be 2 or 3",
3058 allopt[optidx].name, op);
3059 retval = optn_silenterr;
3060 } else {
3061 iflags.wc2_statuslines = itmp;
3062 if (!g.opt_initial)
3063 g.opt_need_redraw = TRUE;
3064 }
3065 return retval;
3066 }
3067 if (req == get_val) {
3068 if (!opts)
3069 return optn_err;
3070 if (wc2_supported(allopt[optidx].name))
3071 Strcpy(opts, (iflags.wc2_statuslines < 3) ? "2" : "3");
3072 else
3073 Strcpy(opts, "unknown");
3074 return optn_ok;
3075 }
3076 return optn_ok;
3077 }
3078
3079 #ifdef WIN32
3080 static int
optfn_subkeyvalue(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)3081 optfn_subkeyvalue(int optidx UNUSED, int req, boolean negated UNUSED,
3082 char *opts, char *op UNUSED)
3083 {
3084 if (req == do_init) {
3085 return optn_ok;
3086 }
3087 if (req == do_set) {
3088 if (op == empty_optstr)
3089 return optn_err;
3090 #ifdef TTY_GRAPHICS
3091 map_subkeyvalue(op);
3092 #endif
3093 return optn_ok;
3094 }
3095 if (req == get_val) {
3096 if (!opts)
3097 return optn_err;
3098 opts[0] = '\0';
3099 return optn_ok;
3100 }
3101 return optn_ok;
3102 }
3103 #endif /* WIN32 */
3104
3105 static int
optfn_suppress_alert(int optidx,int req,boolean negated,char * opts,char * op)3106 optfn_suppress_alert(int optidx, int req, boolean negated,
3107 char *opts, char *op)
3108 {
3109 if (req == do_init) {
3110 return optn_ok;
3111 }
3112 if (req == do_set) {
3113 if (negated) {
3114 bad_negation(allopt[optidx].name, FALSE);
3115 return optn_err;
3116 } else if (op != empty_optstr)
3117 (void) feature_alert_opts(op, allopt[optidx].name);
3118 return optn_ok;
3119 }
3120 if (req == get_val) {
3121 if (!opts)
3122 return optn_err;
3123 if (flags.suppress_alert == 0L)
3124 Strcpy(opts, none);
3125 else
3126 Sprintf(opts, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
3127 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
3128 return optn_ok;
3129 }
3130 return optn_ok;
3131 }
3132
3133 static int
optfn_symset(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op)3134 optfn_symset(int optidx UNUSED, int req, boolean negated UNUSED,
3135 char *opts, char *op)
3136 {
3137 if (req == do_init) {
3138 return optn_ok;
3139 }
3140 if (req == do_set) {
3141 if (op != empty_optstr) {
3142 g.symset[PRIMARY].name = dupstr(op);
3143 if (!read_sym_file(PRIMARY)) {
3144 clear_symsetentry(PRIMARY, TRUE);
3145 config_error_add(
3146 "Unable to load symbol set \"%s\" from \"%s\"", op,
3147 SYMBOLS);
3148 return optn_err;
3149 } else {
3150 switch_symbols(g.symset[PRIMARY].name != (char *) 0);
3151 g.opt_need_redraw = TRUE;
3152 }
3153 } else
3154 return optn_err;
3155 return optn_ok;
3156 }
3157 if (req == get_val) {
3158 if (!opts)
3159 return optn_err;
3160 Sprintf(opts, "%s",
3161 g.symset[PRIMARY].name ? g.symset[PRIMARY].name : "default");
3162 if (g.currentgraphics == PRIMARY && g.symset[PRIMARY].name)
3163 Strcat(opts, ", active");
3164 return optn_ok;
3165 }
3166 return optn_ok;
3167 }
3168
3169 static int
optfn_term_cols(int optidx,int req,boolean negated,char * opts,char * op)3170 optfn_term_cols(int optidx, int req, boolean negated, char *opts, char *op)
3171 {
3172 int retval = optn_ok;
3173 long ltmp;
3174
3175 if (req == do_init) {
3176 return optn_ok;
3177 }
3178 if (req == do_set) {
3179 /* WINCAP2
3180 * term_cols:amount */
3181
3182 if ((op = string_for_opt(opts, negated)) != empty_optstr) {
3183 ltmp = atol(op);
3184 /* just checks atol() sanity, not logical window size sanity
3185 */
3186 if (ltmp <= 0L || ltmp >= (long) LARGEST_INT) {
3187 config_error_add("Invalid %s: %ld", allopt[optidx].name,
3188 ltmp);
3189 retval = optn_err;
3190 } else {
3191 iflags.wc2_term_cols = (int) ltmp;
3192 }
3193 }
3194 return retval;
3195 }
3196 if (req == get_val) {
3197 if (!opts)
3198 return optn_err;
3199 if (iflags.wc2_term_cols)
3200 Sprintf(opts, "%d", iflags.wc2_term_cols);
3201 else
3202 Strcpy(opts, defopt);
3203 return optn_ok;
3204 }
3205 return optn_ok;
3206 }
3207
3208 static int
optfn_term_rows(int optidx,int req,boolean negated,char * opts,char * op)3209 optfn_term_rows(int optidx, int req, boolean negated, char *opts, char *op)
3210 {
3211 int retval = optn_ok;
3212 long ltmp;
3213
3214 if (req == do_init) {
3215 return optn_ok;
3216 }
3217 if (req == do_set) {
3218 /* WINCAP2
3219 * term_rows:amount */
3220
3221 if ((op = string_for_opt(opts, negated)) != empty_optstr) {
3222 ltmp = atol(op);
3223 /* just checks atol() sanity, not logical window size sanity
3224 */
3225 if (ltmp <= 0L || ltmp >= (long) LARGEST_INT) {
3226 config_error_add("Invalid %s: %ld", allopt[optidx].name,
3227 ltmp);
3228 retval = optn_err;
3229 } else {
3230 iflags.wc2_term_rows = (int) ltmp;
3231 }
3232 }
3233 return retval;
3234 }
3235 if (req == get_val) {
3236 if (!opts)
3237 return optn_err;
3238 if (iflags.wc2_term_rows)
3239 Sprintf(opts, "%d", iflags.wc2_term_rows);
3240 else
3241 Strcpy(opts, defopt);
3242 return optn_ok;
3243 }
3244 return optn_ok;
3245 }
3246
3247 static int
optfn_tile_file(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op)3248 optfn_tile_file(int optidx UNUSED, int req, boolean negated UNUSED,
3249 char *opts, char *op)
3250 {
3251 if (req == do_init) {
3252 return optn_ok;
3253 }
3254 if (req == do_set) {
3255 /* WINCAP tile_file:name */
3256 if (op != empty_optstr) {
3257 if (iflags.wc_tile_file)
3258 free(iflags.wc_tile_file);
3259 iflags.wc_tile_file = dupstr(op);
3260 } else
3261 return optn_err;
3262 return optn_ok;
3263 }
3264 if (req == get_val) {
3265 if (!opts)
3266 return optn_err;
3267 Sprintf(opts, "%s",
3268 iflags.wc_tile_file ? iflags.wc_tile_file : defopt);
3269 return optn_ok;
3270 }
3271 return optn_ok;
3272 }
3273
3274 static int
optfn_tile_height(int optidx,int req,boolean negated,char * opts,char * op)3275 optfn_tile_height(int optidx, int req, boolean negated, char *opts, char *op)
3276 {
3277 if (req == do_init) {
3278 return optn_ok;
3279 }
3280 if (req == do_set) {
3281 /* WINCAP tile_height:nn */
3282 op = string_for_opt(opts, negated);
3283 if ((negated && op == empty_optstr)
3284 || (!negated && op != empty_optstr)) {
3285 iflags.wc_tile_height = negated ? 0 : atoi(op);
3286 } else if (negated) {
3287 bad_negation(allopt[optidx].name, TRUE);
3288 return optn_err;
3289 }
3290 return optn_ok;
3291 }
3292 if (req == get_val) {
3293 if (!opts)
3294 return optn_err;
3295 if (iflags.wc_tile_height)
3296 Sprintf(opts, "%d", iflags.wc_tile_height);
3297 else
3298 Strcpy(opts, defopt);
3299 return optn_ok;
3300 }
3301 return optn_ok;
3302 }
3303
3304 static int
optfn_tile_width(int optidx,int req,boolean negated,char * opts,char * op)3305 optfn_tile_width(int optidx, int req, boolean negated, char *opts, char *op)
3306 {
3307 if (req == do_init) {
3308 return optn_ok;
3309 }
3310 if (req == do_set) {
3311 /* WINCAP tile_width:nn */
3312 op = string_for_opt(opts, negated);
3313 if ((negated && op == empty_optstr)
3314 || (!negated && op != empty_optstr)) {
3315 iflags.wc_tile_width = negated ? 0 : atoi(op);
3316 } else if (negated) {
3317 bad_negation(allopt[optidx].name, TRUE);
3318 return optn_err;
3319 }
3320 return optn_ok;
3321 }
3322 if (req == get_val) {
3323 if (!opts)
3324 return optn_err;
3325 if (iflags.wc_tile_width)
3326 Sprintf(opts, "%d", iflags.wc_tile_width);
3327 else
3328 Strcpy(opts, defopt);
3329 return optn_ok;
3330 }
3331 return optn_ok;
3332 }
3333
3334 static int
optfn_traps(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)3335 optfn_traps(int optidx UNUSED, int req, boolean negated UNUSED,
3336 char *opts, char *op UNUSED)
3337 {
3338 if (req == do_init) {
3339 return optn_ok;
3340 }
3341 if (req == do_set) {
3342 return optn_ok;
3343 }
3344 if (req == get_val) {
3345 if (!opts)
3346 return optn_err;
3347 Sprintf(opts, "%s", to_be_done);
3348 return optn_ok;
3349 }
3350 return optn_ok;
3351 }
3352
3353 static int
optfn_vary_msgcount(int optidx,int req,boolean negated,char * opts,char * op)3354 optfn_vary_msgcount(int optidx, int req, boolean negated, char *opts, char *op)
3355 {
3356 if (req == do_init) {
3357 return optn_ok;
3358 }
3359 if (req == do_set) {
3360 /* WINCAP vary_msgcount:nn */
3361 op = string_for_opt(opts, negated);
3362 if ((negated && op == empty_optstr)
3363 || (!negated && op != empty_optstr)) {
3364 iflags.wc_vary_msgcount = negated ? 0 : atoi(op);
3365 } else if (negated) {
3366 bad_negation(allopt[optidx].name, TRUE);
3367 return optn_err;
3368 }
3369 return optn_ok;
3370 }
3371 if (req == get_val) {
3372 if (!opts)
3373 return optn_err;
3374 if (iflags.wc_vary_msgcount)
3375 Sprintf(opts, "%d", iflags.wc_vary_msgcount);
3376 else
3377 Strcpy(opts, defopt);
3378 return optn_ok;
3379 }
3380 return optn_ok;
3381 }
3382
3383 #ifdef VIDEOSHADES
3384 static int
optfn_videocolors(int optidx,int req,boolean negated UNUSED,char * opts,char * op UNUSED)3385 optfn_videocolors(int optidx, int req, boolean negated UNUSED,
3386 char *opts, char *op UNUSED)
3387 {
3388 if (req == do_init) {
3389 return optn_ok;
3390 }
3391 if (req == do_set) {
3392 /* videocolors:string */
3393
3394 if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
3395 == empty_optstr) {
3396 return optn_err;
3397 }
3398 if (!assign_videocolors(opts)) {
3399 config_error_add("Unknown error handling '%s'",
3400 allopt[optidx].name);
3401 return optn_err;
3402 }
3403 return optn_ok;
3404 }
3405 if (req == get_val) {
3406 if (!opts)
3407 return optn_err;
3408 Sprintf(opts, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d",
3409 ttycolors[CLR_RED], ttycolors[CLR_GREEN],
3410 ttycolors[CLR_BROWN], ttycolors[CLR_BLUE],
3411 ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN],
3412 ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN],
3413 ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE],
3414 ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]);
3415 return optn_ok;
3416 }
3417 return optn_ok;
3418 }
3419
3420 static int
optfn_videoshades(int optidx,int req,boolean negated UNUSED,char * opts,char * op UNUSED)3421 optfn_videoshades(int optidx, int req, boolean negated UNUSED,
3422 char *opts, char *op UNUSED)
3423 {
3424 if (req == do_init) {
3425 return optn_ok;
3426 }
3427 if (req == do_set) {
3428 /* videoshades:string */
3429
3430 if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
3431 == empty_optstr) {
3432 return optn_err;
3433 }
3434 if (!assign_videoshades(opts)) {
3435 config_error_add("Unknown error handling '%s'",
3436 allopt[optidx].name);
3437 return optn_err;
3438 }
3439 return optn_ok;
3440 }
3441 if (req == get_val) {
3442 if (!opts)
3443 return optn_err;
3444 Sprintf(opts, "%s-%s-%s", shade[0], shade[1], shade[2]);
3445 return optn_ok;
3446 }
3447 return optn_ok;
3448 }
3449 #endif /* VIDEOSHADES */
3450
3451 #ifdef MSDOS
3452 static int
optfn_video_width(int optidx UNUSED,int req,boolean negated,char * opts,char * op)3453 optfn_video_width(int optidx UNUSED, int req, boolean negated,
3454 char *opts, char *op)
3455 {
3456 if (req == do_init) {
3457 return optn_ok;
3458 }
3459 if (req == do_set) {
3460 if ((op = string_for_opt(opts, negated)) != empty_optstr)
3461 iflags.wc_video_width = strtol(op, NULL, 10);
3462 else
3463 return optn_err;
3464 return optn_ok;
3465 }
3466 if (req == get_val) {
3467 if (!opts)
3468 return optn_err;
3469 opts[0] = '\0';
3470 }
3471 return optn_ok;
3472 }
3473
3474 static int
optfn_video_height(int optidx UNUSED,int req,boolean negated,char * opts,char * op)3475 optfn_video_height(int optidx UNUSED, int req, boolean negated,
3476 char *opts, char *op)
3477 {
3478 if (req == do_init) {
3479 return optn_ok;
3480 }
3481 if (req == do_set) {
3482 if ((op = string_for_opt(opts, negated)) != empty_optstr)
3483 iflags.wc_video_height = strtol(op, NULL, 10);
3484 else
3485 return optn_err;
3486 return optn_ok;
3487 }
3488 if (req == get_val) {
3489 if (!opts)
3490 return optn_err;
3491 opts[0] = '\0';
3492 }
3493 return optn_ok;
3494 }
3495
3496 #ifdef NO_TERMS
3497 static int
optfn_video(int optidx,int req,boolean negated UNUSED,char * opts,char * op UNUSED)3498 optfn_video(int optidx, int req, boolean negated UNUSED,
3499 char *opts, char *op UNUSED)
3500 {
3501 if (req == do_init) {
3502 return optn_ok;
3503 }
3504 if (req == do_set) {
3505 /* video:string */
3506 if ((opts = string_for_env_opt(allopt[optidx].name, opts, FALSE))
3507 == empty_optstr) {
3508 return optn_err;
3509 }
3510 if (!assign_video(opts)) {
3511 config_error_add("Unknown error handling '%s'",
3512 allopt[optidx].name);
3513 return optn_err;
3514 }
3515 return optn_ok;
3516 }
3517 if (req == get_val) {
3518 if (!opts)
3519 return optn_err;
3520 Sprintf(opts, "%s", to_be_done);
3521 return optn_ok;
3522 }
3523 return optn_ok;
3524 }
3525
3526 #endif /* NO_TERMS */
3527 #endif /* MSDOS */
3528
3529 static int
optfn_warnings(int optidx,int req,boolean negated UNUSED,char * opts,char * op UNUSED)3530 optfn_warnings(int optidx, int req, boolean negated UNUSED,
3531 char *opts, char *op UNUSED)
3532 {
3533 int reslt;
3534
3535 if (req == do_init) {
3536 return optn_ok;
3537 }
3538 if (req == do_set) {
3539 reslt = warning_opts(opts, allopt[optidx].name);
3540 return reslt ? optn_ok : optn_err;
3541 }
3542 if (req == get_val) {
3543 if (!opts)
3544 return optn_err;
3545 opts[0] = '\0';
3546 return optn_ok;
3547 }
3548 return optn_ok;
3549 }
3550
3551 static int
optfn_whatis_coord(int optidx,int req,boolean negated,char * opts,char * op)3552 optfn_whatis_coord(int optidx, int req, boolean negated, char *opts, char *op)
3553 {
3554 if (req == do_init) {
3555 return optn_ok;
3556 }
3557 if (req == do_set) {
3558 if (negated) {
3559 iflags.getpos_coords = GPCOORDS_NONE;
3560 return optn_ok;
3561 } else if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
3562 != empty_optstr) {
3563 static char gpcoords[] = { GPCOORDS_NONE, GPCOORDS_COMPASS,
3564 GPCOORDS_COMFULL, GPCOORDS_MAP,
3565 GPCOORDS_SCREEN, '\0' };
3566 char c = lowc(*op);
3567
3568 if (c && index(gpcoords, c))
3569 iflags.getpos_coords = c;
3570 else {
3571 config_error_add("Unknown %s parameter '%s'",
3572 allopt[optidx].name, op);
3573 return optn_err;
3574 }
3575 } else
3576 return optn_err;
3577 return optn_ok;
3578 }
3579 if (req == get_val) {
3580 if (!opts)
3581 return optn_err;
3582 Sprintf(opts, "%s",
3583 (iflags.getpos_coords == GPCOORDS_MAP) ? "map"
3584 : (iflags.getpos_coords == GPCOORDS_COMPASS) ? "compass"
3585 : (iflags.getpos_coords == GPCOORDS_COMFULL) ? "full compass"
3586 : (iflags.getpos_coords == GPCOORDS_SCREEN) ? "screen"
3587 : "none");
3588 return optn_ok;
3589 }
3590 if (req == do_handler) {
3591 return handler_whatis_coord();
3592 }
3593 return optn_ok;
3594 }
3595
3596 static int
optfn_whatis_filter(int optidx,int req,boolean negated,char * opts,char * op)3597 optfn_whatis_filter(int optidx, int req, boolean negated, char *opts, char *op)
3598 {
3599 if (req == do_init) {
3600 return optn_ok;
3601 }
3602 if (req == do_set) {
3603 if (negated) {
3604 iflags.getloc_filter = GFILTER_NONE;
3605 return optn_ok;
3606 } else if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
3607 != empty_optstr) {
3608 char c = lowc(*op);
3609
3610 switch (c) {
3611 case 'n':
3612 iflags.getloc_filter = GFILTER_NONE;
3613 break;
3614 case 'v':
3615 iflags.getloc_filter = GFILTER_VIEW;
3616 break;
3617 case 'a':
3618 iflags.getloc_filter = GFILTER_AREA;
3619 break;
3620 default: {
3621 config_error_add("Unknown %s parameter '%s'",
3622 allopt[optidx].name, op);
3623 return optn_err;
3624 }
3625 }
3626 } else
3627 return optn_err;
3628 return optn_ok;
3629 }
3630 if (req == get_val) {
3631 if (!opts)
3632 return optn_err;
3633 Sprintf(opts, "%s",
3634 (iflags.getloc_filter == GFILTER_VIEW) ? "view"
3635 : (iflags.getloc_filter == GFILTER_AREA) ? "area"
3636 : "none");
3637 return optn_ok;
3638 }
3639 if (req == do_handler) {
3640 return handler_whatis_filter();
3641 }
3642 return optn_ok;
3643 }
3644
3645 static int
optfn_windowborders(int optidx,int req,boolean negated,char * opts,char * op)3646 optfn_windowborders(int optidx, int req, boolean negated, char *opts, char *op)
3647 {
3648 int retval = optn_ok;
3649
3650 if (req == do_init) {
3651 return optn_ok;
3652 }
3653 if (req == do_set) {
3654 op = string_for_opt(opts, negated);
3655 if (negated && op != empty_optstr) {
3656 bad_negation(allopt[optidx].name, TRUE);
3657 retval = optn_err;
3658 } else {
3659 int itmp;
3660
3661 if (negated)
3662 itmp = 0; /* Off */
3663 else if (op == empty_optstr)
3664 itmp = 1; /* On */
3665 else /* Value supplied; expect 0 (off), 1 (on), 2 (auto)
3666 * or 3 (on for most windows, off for perm_invent)
3667 * or 4 (auto for most windows, off for perm_invent) */
3668 itmp = atoi(op);
3669
3670 if (itmp < 0 || itmp > 4) {
3671 config_error_add("Invalid %s (should be within 0 to 4): %s",
3672 allopt[optidx].name, opts);
3673 retval = optn_silenterr;
3674 } else {
3675 iflags.wc2_windowborders = itmp;
3676 }
3677 }
3678 return retval;
3679 }
3680 if (req == get_val) {
3681 if (!opts)
3682 return optn_err;
3683 Sprintf(opts, "%s",
3684 (iflags.wc2_windowborders == 0) ? "0=off"
3685 : (iflags.wc2_windowborders == 1) ? "1=on"
3686 : (iflags.wc2_windowborders == 2) ? "2=auto"
3687 : (iflags.wc2_windowborders == 3)
3688 ? "3=on, except off for perm_invent"
3689 : (iflags.wc2_windowborders == 4)
3690 ? "4=auto, except off for perm_invent"
3691 : defopt);
3692 return optn_ok;
3693 }
3694 return optn_ok;
3695 }
3696
3697 #ifdef WINCHAIN
3698 static int
optfn_windowchain(int optidx,int req,boolean negated,char * opts,char * op)3699 optfn_windowchain(int optidx, int req, boolean negated, char *opts, char *op)
3700 {
3701 if (req == do_init) {
3702 return optn_ok;
3703 }
3704 if (req == do_set) {
3705 if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
3706 != empty_optstr) {
3707 char buf[WINTYPELEN];
3708
3709 nmcpy(buf, op, WINTYPELEN);
3710 addto_windowchain(buf);
3711 } else
3712 return optn_err;
3713 return optn_ok;
3714 }
3715 if (req == get_val) {
3716 if (!opts)
3717 return optn_err;
3718 opts[0] = '\0';
3719 return optn_ok;
3720 }
3721 return optn_ok;
3722 }
3723 #endif
3724
3725 static int
optfn_windowcolors(int optidx,int req,boolean negated UNUSED,char * opts,char * op)3726 optfn_windowcolors(int optidx, int req, boolean negated UNUSED,
3727 char *opts, char *op)
3728 {
3729 if (req == do_init) {
3730 return optn_ok;
3731 }
3732 if (req == do_set) {
3733 /* WINCAP
3734 * setting window colors
3735 * syntax: windowcolors=menu foregrnd/backgrnd text
3736 * foregrnd/backgrnd
3737 */
3738 if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
3739 if (!wc_set_window_colors(op)) {
3740 config_error_add("Could not set %s '%s'", allopt[optidx].name,
3741 op);
3742 return optn_err;
3743 }
3744 }
3745 return optn_ok;
3746 }
3747 if (req == get_val) {
3748 if (!opts)
3749 return optn_err;
3750 Sprintf(
3751 opts, "%s/%s %s/%s %s/%s %s/%s",
3752 iflags.wc_foregrnd_menu ? iflags.wc_foregrnd_menu : defbrief,
3753 iflags.wc_backgrnd_menu ? iflags.wc_backgrnd_menu : defbrief,
3754 iflags.wc_foregrnd_message ? iflags.wc_foregrnd_message
3755 : defbrief,
3756 iflags.wc_backgrnd_message ? iflags.wc_backgrnd_message
3757 : defbrief,
3758 iflags.wc_foregrnd_status ? iflags.wc_foregrnd_status : defbrief,
3759 iflags.wc_backgrnd_status ? iflags.wc_backgrnd_status : defbrief,
3760 iflags.wc_foregrnd_text ? iflags.wc_foregrnd_text : defbrief,
3761 iflags.wc_backgrnd_text ? iflags.wc_backgrnd_text : defbrief);
3762 return optn_ok;
3763 }
3764 return optn_ok;
3765 }
3766
3767 static int
optfn_windowtype(int optidx,int req,boolean negated UNUSED,char * opts,char * op)3768 optfn_windowtype(int optidx, int req, boolean negated UNUSED,
3769 char *opts, char *op)
3770 {
3771 if (req == do_init) {
3772 return optn_ok;
3773 }
3774 if (req == do_set) {
3775 /*
3776 * windowtype: option to choose the interface for binaries built
3777 * with support for more than one interface (tty + X11, for
3778 * instance).
3779 *
3780 * Ideally, 'windowtype' should be processed first, because it
3781 * causes the wc_ and wc2_ flags to be set up.
3782 * For user, making it be first in a config file is trivial, use
3783 * OPTIONS=windowtype:Foo
3784 * as the first non-comment line of the file.
3785 * Making it first in NETHACKOPTIONS requires it to be at the
3786 * _end_ because comma-separated option strings are processed from
3787 * right to left.
3788 */
3789 if (iflags.windowtype_locked)
3790 return optn_ok;
3791
3792 if ((op = string_for_env_opt(allopt[optidx].name, opts, FALSE))
3793 != empty_optstr) {
3794 nmcpy(g.chosen_windowtype, op, WINTYPELEN);
3795 if (!iflags.windowtype_deferred) {
3796 choose_windows(g.chosen_windowtype);
3797 }
3798 } else
3799 return optn_err;
3800 return optn_ok;
3801 }
3802 if (req == get_val) {
3803 if (!opts)
3804 return optn_err;
3805 Sprintf(opts, "%s", windowprocs.name);
3806 return optn_ok;
3807 }
3808 return optn_ok;
3809 }
3810
3811 /*
3812 * Prefix-handling functions
3813 */
3814
3815 int
pfxfn_cond_(int optidx UNUSED,int req,boolean negated,char * opts,char * op UNUSED)3816 pfxfn_cond_(int optidx UNUSED, int req, boolean negated,
3817 char *opts, char *op UNUSED)
3818 {
3819 int reslt;
3820
3821 if (req == do_init) {
3822 condopt(0, (boolean *) 0, 0); /* make the choices match defaults */
3823 return optn_ok;
3824 }
3825 if (req == do_set) {
3826 if ((reslt = parse_cond_option(negated, opts)) != 0) {
3827 switch (reslt) {
3828 case 3:
3829 config_error_add("Ambiguous condition option %s", opts);
3830 break;
3831 case 1:
3832 case 2:
3833 default:
3834 config_error_add("Unknown condition option %s (%d)", opts,
3835 reslt);
3836 break;
3837 }
3838 return optn_err;
3839 }
3840 g.opt_need_redraw = TRUE;
3841 return optn_ok;
3842 }
3843 if (req == get_val) {
3844 if (!opts)
3845 return optn_err;
3846 opts[0] = '\0';
3847 return optn_ok;
3848 }
3849 if (req == do_handler) {
3850 cond_menu(); /* in botl.c */
3851 return optn_ok;
3852 }
3853 return optn_ok;
3854 }
3855
3856 int
pfxfn_font(int optidx,int req,boolean negated,char * opts,char * op)3857 pfxfn_font(int optidx, int req, boolean negated, char *opts, char *op)
3858 {
3859 int opttype = -1;
3860
3861 if (req == do_init) {
3862 return optn_ok;
3863 }
3864
3865 if (req == do_set) {
3866 /* WINCAP setting font options */
3867 if (optidx == opt_font_map)
3868 opttype = MAP_OPTION;
3869 else if (optidx == opt_font_message)
3870 opttype = MESSAGE_OPTION;
3871 else if (optidx == opt_font_text)
3872 opttype = TEXT_OPTION;
3873 else if (optidx == opt_font_menu)
3874 opttype = MENU_OPTION;
3875 else if (optidx == opt_font_status)
3876 opttype = STATUS_OPTION;
3877 else if (optidx == opt_font_size_map
3878 || optidx == opt_font_size_message
3879 || optidx == opt_font_size_text
3880 || optidx == opt_font_size_menu
3881 || optidx == opt_font_size_status) {
3882 if (optidx == opt_font_size_map)
3883 opttype = MAP_OPTION;
3884 else if (optidx == opt_font_size_message)
3885 opttype = MESSAGE_OPTION;
3886 else if (optidx == opt_font_size_text)
3887 opttype = TEXT_OPTION;
3888 else if (optidx == opt_font_size_menu)
3889 opttype = MENU_OPTION;
3890 else if (optidx == opt_font_size_status)
3891 opttype = STATUS_OPTION;
3892 else {
3893 config_error_add("Unknown %s parameter '%s'",
3894 allopt[optidx].name, opts);
3895 return optn_err;
3896 }
3897 if (duplicate)
3898 complain_about_duplicate(optidx);
3899 if (opttype > 0 && !negated
3900 && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
3901 switch (opttype) {
3902 case MAP_OPTION:
3903 iflags.wc_fontsiz_map = atoi(op);
3904 break;
3905 case MESSAGE_OPTION:
3906 iflags.wc_fontsiz_message = atoi(op);
3907 break;
3908 case TEXT_OPTION:
3909 iflags.wc_fontsiz_text = atoi(op);
3910 break;
3911 case MENU_OPTION:
3912 iflags.wc_fontsiz_menu = atoi(op);
3913 break;
3914 case STATUS_OPTION:
3915 iflags.wc_fontsiz_status = atoi(op);
3916 break;
3917 }
3918 }
3919 return optn_ok;
3920 } else {
3921 config_error_add("Unknown %s parameter '%s'",
3922 "font", opts);
3923 return FALSE;
3924 }
3925 if (opttype > 0
3926 && (op = string_for_opt(opts, FALSE)) != empty_optstr) {
3927 wc_set_font_name(opttype, op);
3928 #ifdef MAC
3929 set_font_name(opttype, op);
3930 #endif
3931 return optn_ok;
3932 } else if (negated) {
3933 bad_negation(allopt[optidx].name, TRUE);
3934 return optn_err;
3935 }
3936 return optn_ok;
3937 }
3938 if (req == get_val) {
3939 if (!opts)
3940 return optn_err;
3941
3942 if (optidx == opt_font_map) {
3943 Sprintf(opts, "%s", iflags.wc_font_map ? iflags.wc_font_map : defopt);
3944 } else if (optidx == opt_font_message) {
3945 Sprintf(opts, "%s",
3946 iflags.wc_font_message ? iflags.wc_font_message : defopt);
3947 } else if (optidx == opt_font_status) {
3948 Sprintf(opts, "%s",
3949 iflags.wc_font_status ? iflags.wc_font_status : defopt);
3950 } else if (optidx == opt_font_menu) {
3951 Sprintf(opts, "%s",
3952 iflags.wc_font_menu ? iflags.wc_font_menu : defopt);
3953 } else if (optidx == opt_font_text) {
3954 Sprintf(opts, "%s",
3955 iflags.wc_font_text ? iflags.wc_font_text : defopt);
3956 } else if (optidx == opt_font_size_map) {
3957 if (iflags.wc_fontsiz_map)
3958 Sprintf(opts, "%d", iflags.wc_fontsiz_map);
3959 else
3960 Strcpy(opts, defopt);
3961 } else if (optidx == opt_font_size_message) {
3962 if (iflags.wc_fontsiz_message)
3963 Sprintf(opts, "%d", iflags.wc_fontsiz_message);
3964 else
3965 Strcpy(opts, defopt);
3966 } else if (optidx == opt_font_size_status) {
3967 if (iflags.wc_fontsiz_status)
3968 Sprintf(opts, "%d", iflags.wc_fontsiz_status);
3969 else
3970 Strcpy(opts, defopt);
3971 } else if (optidx == opt_font_size_menu) {
3972 if (iflags.wc_fontsiz_menu)
3973 Sprintf(opts, "%d", iflags.wc_fontsiz_menu);
3974 else
3975 Strcpy(opts, defopt);
3976 } else if (optidx == opt_font_size_text) {
3977 if (iflags.wc_fontsiz_text)
3978 Sprintf(opts, "%d", iflags.wc_fontsiz_text);
3979 else
3980 Strcpy(opts, defopt);
3981 }
3982 return optn_ok;
3983 }
3984 return optn_ok;
3985 }
3986
3987 #if defined(MICRO) && !defined(AMIGA)
3988 int
pfxfn_IBM_(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)3989 pfxfn_IBM_(int optidx UNUSED, int req, boolean negated UNUSED,
3990 char *opts, char *op UNUSED)
3991 {
3992 if (req == do_init) {
3993 return optn_ok;
3994 }
3995 if (req == do_set) {
3996 return optn_ok;
3997 }
3998 if (req == get_val) {
3999 if (!opts)
4000 return optn_err;
4001 opts[0] = '\0';
4002 return optn_ok;
4003 }
4004 return optn_ok;
4005 }
4006 #endif
4007
4008 /*
4009 * General boolean option handler
4010 * (Use optidx to reference the specific option)
4011 */
4012
4013 static int
optfn_boolean(int optidx,int req,boolean negated,char * opts,char * op)4014 optfn_boolean(int optidx, int req, boolean negated, char *opts, char *op)
4015 {
4016 if (req == do_init) {
4017 return optn_ok;
4018 }
4019 if (req == do_set) {
4020 boolean nosexchange = FALSE;
4021
4022 if (!allopt[optidx].addr)
4023 return optn_ok; /* silent retreat */
4024
4025 /* option that must come from config file? */
4026 if (!g.opt_initial && (allopt[optidx].setwhere == set_in_config))
4027 return optn_err;
4028
4029 op = string_for_opt(opts, TRUE);
4030 if (op != empty_optstr) {
4031 int ln;
4032
4033 if (negated) {
4034 config_error_add(
4035 "Negated boolean '%s' should not have a parameter",
4036 allopt[optidx].name);
4037 return optn_silenterr;
4038 }
4039 /* length is greater than 0 or we wouldn't have gotten here */
4040 ln = (int) strlen(op);
4041 if (!strncmpi(op, "true", ln)
4042 || !strncmpi(op, "yes", ln)
4043 || !strcmpi(op, "on")
4044 || (digit(*op) && atoi(op) == 1)) {
4045 negated = FALSE;
4046 } else if (!strncmpi(op, "false", ln)
4047 || !strncmpi(op, "no", ln)
4048 || !strcmpi(op, "off")
4049 || (digit(*op) && atoi(op) == 0)) {
4050 negated = TRUE;
4051 } else if (!allopt[optidx].valok) {
4052 config_error_add("'%s' is not valid for a boolean", opts);
4053 return optn_silenterr;
4054 }
4055 }
4056 if (iflags.debug_fuzzer && !g.opt_initial) {
4057 /* don't randomly toggle this/these */
4058 if (optidx == opt_silent)
4059 return optn_ok;
4060 }
4061 /* Before the change */
4062 switch (optidx) {
4063 case opt_female:
4064 if (!strncmpi(opts, "female", 3)) {
4065 if (!g.opt_initial && flags.female == negated) {
4066 nosexchange = TRUE;
4067 } else {
4068 flags.initgend = flags.female = !negated;
4069 return optn_ok;
4070 }
4071 }
4072 if (!strncmpi(opts, "male", 3)) {
4073 if (!g.opt_initial && flags.female != negated) {
4074 nosexchange = TRUE;
4075 } else {
4076 flags.initgend = flags.female = negated;
4077 return optn_ok;
4078 }
4079 }
4080 break;
4081 }
4082 /* this dates from when 'O' prompted for a line of options text
4083 rather than use a menu to control access to which options can
4084 be modified during play; it was possible to attempt to use
4085 'O' to specify female or negate male when playing as male or
4086 to specify male or negate female when playing as female;
4087 options processing rejects that for !opt_initial; not possible
4088 now but kept in case someone brings the old 'O' behavior back */
4089 if (nosexchange) {
4090 /* can't arbitrarily change sex after game has started;
4091 magic (amulet or polymorph) is required for that */
4092 config_error_add("'%s' is not anatomically possible.", opts);
4093 return optn_silenterr;
4094 }
4095
4096 *(allopt[optidx].addr) = !negated; /* <==== SET IT HERE */
4097
4098 /* After the change */
4099 switch (optidx) {
4100 case opt_ascii_map:
4101 iflags.wc_tiled_map = negated;
4102 break;
4103 case opt_tiled_map:
4104 iflags.wc_ascii_map = negated;
4105 break;
4106 }
4107
4108 /* only do processing below if setting with doset() */
4109
4110 if (g.opt_initial)
4111 return optn_ok;
4112
4113 switch (optidx) {
4114 case opt_time:
4115 #ifdef SCORE_ON_BOTL
4116 case opt_showscore:
4117 #endif
4118 case opt_showexp:
4119 if (VIA_WINDOWPORT())
4120 status_initialize(REASSESS_ONLY);
4121 g.context.botl = TRUE;
4122 break;
4123 case opt_fixinv:
4124 case opt_sortpack:
4125 case opt_implicit_uncursed:
4126 if (!flags.invlet_constant)
4127 reassign();
4128 update_inventory();
4129 break;
4130 case opt_lit_corridor:
4131 case opt_dark_room:
4132 /*
4133 * All corridor squares seen via night vision or
4134 * candles & lamps change. Update them by calling
4135 * newsym() on them. Don't do this if we are
4136 * initializing the options --- the vision system
4137 * isn't set up yet.
4138 */
4139 vision_recalc(2); /* shut down vision */
4140 g.vision_full_recalc = 1; /* delayed recalc */
4141 if (iflags.use_color)
4142 g.opt_need_redraw = TRUE; /* darkroom refresh */
4143 break;
4144 case opt_wizmgender:
4145 case opt_showrace:
4146 case opt_use_inverse:
4147 case opt_hilite_pile:
4148 case opt_perm_invent:
4149 case opt_ascii_map:
4150 case opt_tiled_map:
4151 g.opt_need_redraw = TRUE;
4152 break;
4153 case opt_hilite_pet:
4154 #ifdef CURSES_GRAPHICS
4155 if (WINDOWPORT("curses")) {
4156 /* if we're enabling hilite_pet and petattr isn't set,
4157 set it to Inverse; if we're disabling, leave petattr
4158 alone so that re-enabling will get current value back
4159 */
4160 if (iflags.hilite_pet && !iflags.wc2_petattr)
4161 iflags.wc2_petattr = curses_read_attrs("I");
4162 }
4163 #endif
4164 g.opt_need_redraw = TRUE;
4165 break;
4166 case opt_hitpointbar:
4167 if (VIA_WINDOWPORT()) {
4168 /* [is reassessment really needed here?] */
4169 status_initialize(REASSESS_ONLY);
4170 g.opt_need_redraw = TRUE;
4171 #ifdef QT_GRAPHICS
4172 } else if (WINDOWPORT("Qt")) {
4173 /* Qt doesn't support HILITE_STATUS or FLUSH_STATUS so fails
4174 VIA_WINDOWPORT(), but it does support WC2_HITPOINTBAR */
4175 g.context.botlx = TRUE;
4176 #endif
4177 }
4178 break;
4179 case opt_color:
4180 #ifdef TOS
4181 if (iflags.BIOS) {
4182 if (colors_changed)
4183 restore_colors();
4184 else
4185 set_colors();
4186 }
4187 #endif
4188 g.opt_need_redraw = TRUE;
4189 break;
4190 case opt_menucolors:
4191 case opt_guicolor:
4192 update_inventory();
4193 break;
4194 case opt_mention_decor:
4195 iflags.prev_decor = STONE;
4196 break;
4197 }
4198
4199 /* boolean value has been toggled but some option changes can
4200 still be pending at this point (mainly for opt_need_redraw);
4201 give the toggled message now regardless */
4202 pline("'%s' option toggled %s.", allopt[optidx].name,
4203 !negated ? "on" : "off");
4204
4205 return optn_ok;
4206 }
4207 if (req == get_val) {
4208 if (!opts)
4209 return optn_err;
4210 opts[0] = '\0';
4211 return optn_ok;
4212 }
4213 return optn_ok;
4214 }
4215
4216 static int
spcfn_misc_menu_cmd(int midx,int req,boolean negated,char * opts,char * op)4217 spcfn_misc_menu_cmd(int midx, int req, boolean negated, char *opts, char *op)
4218 {
4219 if (req == do_init) {
4220 return optn_ok;
4221 }
4222 if (req == do_set) {
4223 if (negated) {
4224 bad_negation(default_menu_cmd_info[midx].name, FALSE);
4225 return optn_err;
4226 } else if ((op = string_for_opt(opts, FALSE)) != empty_optstr) {
4227 char c = txt2key(op);
4228
4229 if (illegal_menu_cmd_key((uchar) c))
4230 return optn_err;
4231 add_menu_cmd_alias(c, default_menu_cmd_info[midx].cmd);
4232 }
4233 return optn_ok;
4234 }
4235 if (req == get_val) {
4236 if (!opts)
4237 return optn_err;
4238 opts[0] = '\0';
4239 return optn_ok;
4240 }
4241 return optn_ok;
4242 }
4243
4244
4245 /*
4246 **********************************
4247 *
4248 * Special per-option handlers
4249 *
4250 **********************************
4251 */
4252
4253 static int
handler_menustyle(void)4254 handler_menustyle(void)
4255 {
4256 winid tmpwin;
4257 anything any;
4258 int i;
4259 const char *style_name;
4260 menu_item *style_pick = (menu_item *) 0;
4261
4262 tmpwin = create_nhwindow(NHW_MENU);
4263 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4264 any = cg.zeroany;
4265 for (i = 0; i < SIZE(menutype); i++) {
4266 style_name = menutype[i];
4267 /* note: separate `style_name' variable used
4268 to avoid an optimizer bug in VAX C V2.3 */
4269 any.a_int = i + 1;
4270 add_menu(tmpwin, &nul_glyphinfo, &any, *style_name, 0,
4271 ATR_NONE, style_name, MENU_ITEMFLAGS_NONE);
4272 }
4273 end_menu(tmpwin, "Select menustyle:");
4274 if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) {
4275 flags.menu_style = style_pick->item.a_int - 1;
4276 free((genericptr_t) style_pick);
4277 }
4278 destroy_nhwindow(tmpwin);
4279 return optn_ok;
4280 }
4281
4282 static int
handler_align_misc(int optidx)4283 handler_align_misc(int optidx)
4284 {
4285 winid tmpwin;
4286 anything any;
4287 menu_item *window_pick = (menu_item *) 0;
4288 char abuf[BUFSZ];
4289
4290 tmpwin = create_nhwindow(NHW_MENU);
4291 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4292 any = cg.zeroany;
4293 any.a_int = ALIGN_TOP;
4294 add_menu(tmpwin, &nul_glyphinfo, &any, 't', 0, ATR_NONE, "top",
4295 MENU_ITEMFLAGS_NONE);
4296 any.a_int = ALIGN_BOTTOM;
4297 add_menu(tmpwin, &nul_glyphinfo, &any, 'b', 0, ATR_NONE, "bottom",
4298 MENU_ITEMFLAGS_NONE);
4299 any.a_int = ALIGN_LEFT;
4300 add_menu(tmpwin, &nul_glyphinfo, &any, 'l', 0, ATR_NONE, "left",
4301 MENU_ITEMFLAGS_NONE);
4302 any.a_int = ALIGN_RIGHT;
4303 add_menu(tmpwin, &nul_glyphinfo, &any, 'r', 0, ATR_NONE, "right",
4304 MENU_ITEMFLAGS_NONE);
4305 Sprintf(abuf, "Select %s window placement relative to the map:",
4306 (optidx == opt_align_message) ? "message" : "status");
4307 end_menu(tmpwin, abuf);
4308 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
4309 if (optidx == opt_align_message)
4310 iflags.wc_align_message = window_pick->item.a_int;
4311 else
4312 iflags.wc_align_status = window_pick->item.a_int;
4313 free((genericptr_t) window_pick);
4314 }
4315 destroy_nhwindow(tmpwin);
4316 return optn_ok;
4317 }
4318
4319 static int
handler_disclose(void)4320 handler_disclose(void)
4321 {
4322 winid tmpwin;
4323 anything any;
4324 int i, n;
4325 char buf[BUFSZ];
4326 /* order of disclose_names[] must correspond to
4327 disclosure_options in decl.c */
4328 static const char *disclosure_names[] = {
4329 "inventory", "attributes", "vanquished",
4330 "genocides", "conduct", "overview",
4331 };
4332 int disc_cat[NUM_DISCLOSURE_OPTIONS];
4333 int pick_cnt, pick_idx, opt_idx;
4334 char c;
4335 menu_item *disclosure_pick = (menu_item *) 0;
4336
4337 tmpwin = create_nhwindow(NHW_MENU);
4338 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4339 any = cg.zeroany;
4340 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4341 Sprintf(buf, "%-12s[%c%c]", disclosure_names[i],
4342 flags.end_disclose[i], disclosure_options[i]);
4343 any.a_int = i + 1;
4344 add_menu(tmpwin, &nul_glyphinfo, &any, disclosure_options[i],
4345 0, ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
4346 disc_cat[i] = 0;
4347 }
4348 end_menu(tmpwin, "Change which disclosure options categories:");
4349 pick_cnt = select_menu(tmpwin, PICK_ANY, &disclosure_pick);
4350 if (pick_cnt > 0) {
4351 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
4352 opt_idx = disclosure_pick[pick_idx].item.a_int - 1;
4353 disc_cat[opt_idx] = 1;
4354 }
4355 free((genericptr_t) disclosure_pick);
4356 disclosure_pick = (menu_item *) 0;
4357 }
4358 destroy_nhwindow(tmpwin);
4359
4360 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++) {
4361 if (disc_cat[i]) {
4362 c = flags.end_disclose[i];
4363 Sprintf(buf, "Disclosure options for %s:",
4364 disclosure_names[i]);
4365 tmpwin = create_nhwindow(NHW_MENU);
4366 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4367 any = cg.zeroany;
4368 /* 'y','n',and '+' work as alternate selectors; '-' doesn't */
4369 any.a_char = DISCLOSE_NO_WITHOUT_PROMPT;
4370 add_menu(tmpwin, &nul_glyphinfo, &any, 0,
4371 any.a_char, ATR_NONE,
4372 "Never disclose, without prompting",
4373 (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
4374 : MENU_ITEMFLAGS_NONE);
4375 any.a_char = DISCLOSE_YES_WITHOUT_PROMPT;
4376 add_menu(tmpwin, &nul_glyphinfo, &any, 0,
4377 any.a_char, ATR_NONE,
4378 "Always disclose, without prompting",
4379 (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
4380 : MENU_ITEMFLAGS_NONE);
4381 if (*disclosure_names[i] == 'v') {
4382 any.a_char = DISCLOSE_SPECIAL_WITHOUT_PROMPT; /* '#' */
4383 add_menu(tmpwin, &nul_glyphinfo, &any, 0,
4384 any.a_char, ATR_NONE,
4385 "Always disclose, pick sort order from menu",
4386 (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
4387 : MENU_ITEMFLAGS_NONE);
4388 }
4389 any.a_char = DISCLOSE_PROMPT_DEFAULT_NO;
4390 add_menu(tmpwin, &nul_glyphinfo, &any, 0,
4391 any.a_char, ATR_NONE,
4392 "Prompt, with default answer of \"No\"",
4393 (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
4394 : MENU_ITEMFLAGS_NONE);
4395 any.a_char = DISCLOSE_PROMPT_DEFAULT_YES;
4396 add_menu(tmpwin, &nul_glyphinfo, &any, 0,
4397 any.a_char, ATR_NONE,
4398 "Prompt, with default answer of \"Yes\"",
4399 (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
4400 : MENU_ITEMFLAGS_NONE);
4401 if (*disclosure_names[i] == 'v') {
4402 any.a_char = DISCLOSE_PROMPT_DEFAULT_SPECIAL; /* '?' */
4403 add_menu(tmpwin, &nul_glyphinfo, &any, 0,
4404 any.a_char, ATR_NONE,
4405 "Prompt, with default answer of \"Ask\" to request sort menu",
4406 (c == any.a_char) ? MENU_ITEMFLAGS_SELECTED
4407 : MENU_ITEMFLAGS_NONE);
4408 }
4409 end_menu(tmpwin, buf);
4410 n = select_menu(tmpwin, PICK_ONE, &disclosure_pick);
4411 if (n > 0) {
4412 flags.end_disclose[i] = disclosure_pick[0].item.a_char;
4413 if (n > 1 && flags.end_disclose[i] == c)
4414 flags.end_disclose[i] = disclosure_pick[1].item.a_char;
4415 free((genericptr_t) disclosure_pick);
4416 }
4417 destroy_nhwindow(tmpwin);
4418 }
4419 }
4420 return optn_ok;
4421 }
4422
4423 static int
handler_menu_headings(void)4424 handler_menu_headings(void)
4425 {
4426 int mhattr = query_attr("How to highlight menu headings:");
4427
4428 if (mhattr != -1) {
4429 iflags.menu_headings = mhattr;
4430 /* header highlighting affects persistent inventory display */
4431 if (iflags.perm_invent)
4432 update_inventory();
4433 }
4434 return optn_ok;
4435 }
4436
4437 static int
handler_msg_window(void)4438 handler_msg_window(void)
4439 {
4440 #if defined(TTY_GRAPHICS) || defined(CURSES_GRAPHICS)
4441 winid tmpwin;
4442 anything any;
4443
4444 if (WINDOWPORT("tty") || WINDOWPORT("curses")) {
4445 /* by Christian W. Cooper */
4446 menu_item *window_pick = (menu_item *) 0;
4447
4448 tmpwin = create_nhwindow(NHW_MENU);
4449 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4450 any = cg.zeroany;
4451 if (!WINDOWPORT("curses")) {
4452 any.a_char = 's';
4453 add_menu(tmpwin, &nul_glyphinfo, &any, 's',
4454 0, ATR_NONE, "single", MENU_ITEMFLAGS_NONE);
4455 any.a_char = 'c';
4456 add_menu(tmpwin, &nul_glyphinfo, &any, 'c',
4457 0, ATR_NONE, "combination", MENU_ITEMFLAGS_NONE);
4458 }
4459 any.a_char = 'f';
4460 add_menu(tmpwin, &nul_glyphinfo, &any, 'f',
4461 0, ATR_NONE, "full", MENU_ITEMFLAGS_NONE);
4462 any.a_char = 'r';
4463 add_menu(tmpwin, &nul_glyphinfo, &any, 'r',
4464 0, ATR_NONE, "reversed", MENU_ITEMFLAGS_NONE);
4465 end_menu(tmpwin, "Select message history display type:");
4466 if (select_menu(tmpwin, PICK_ONE, &window_pick) > 0) {
4467 iflags.prevmsg_window = window_pick->item.a_char;
4468 free((genericptr_t) window_pick);
4469 }
4470 destroy_nhwindow(tmpwin);
4471 } else
4472 #endif /* msg_window for tty or curses */
4473 pline("'%s' option is not supported for '%s'.",
4474 allopt[opt_msg_window].name, windowprocs.name);
4475 return optn_ok;
4476 }
4477
4478 static int
handler_number_pad(void)4479 handler_number_pad(void)
4480 {
4481 winid tmpwin;
4482 anything any;
4483 int i;
4484 static const char *npchoices[] = {
4485 " 0 (off)", " 1 (on)", " 2 (on, MSDOS compatible)",
4486 " 3 (on, phone-style digit layout)",
4487 " 4 (on, phone-style layout, MSDOS compatible)",
4488 "-1 (off, 'z' to move upper-left, 'y' to zap wands)"
4489 };
4490 menu_item *mode_pick = (menu_item *) 0;
4491
4492 tmpwin = create_nhwindow(NHW_MENU);
4493 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4494 any = cg.zeroany;
4495 for (i = 0; i < SIZE(npchoices); i++) {
4496 any.a_int = i + 1;
4497 add_menu(tmpwin, &nul_glyphinfo, &any, 'a' + i,
4498 0, ATR_NONE, npchoices[i], MENU_ITEMFLAGS_NONE);
4499 }
4500 end_menu(tmpwin, "Select number_pad mode:");
4501 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4502 switch (mode_pick->item.a_int - 1) {
4503 case 0:
4504 iflags.num_pad = FALSE;
4505 iflags.num_pad_mode = 0;
4506 break;
4507 case 1:
4508 iflags.num_pad = TRUE;
4509 iflags.num_pad_mode = 0;
4510 break;
4511 case 2:
4512 iflags.num_pad = TRUE;
4513 iflags.num_pad_mode = 1;
4514 break;
4515 case 3:
4516 iflags.num_pad = TRUE;
4517 iflags.num_pad_mode = 2;
4518 break;
4519 case 4:
4520 iflags.num_pad = TRUE;
4521 iflags.num_pad_mode = 3;
4522 break;
4523 /* last menu choice: number_pad == -1 */
4524 case 5:
4525 iflags.num_pad = FALSE;
4526 iflags.num_pad_mode = 1;
4527 break;
4528 }
4529 reset_commands(FALSE);
4530 number_pad(iflags.num_pad ? 1 : 0);
4531 free((genericptr_t) mode_pick);
4532 }
4533 destroy_nhwindow(tmpwin);
4534 return optn_ok;
4535 }
4536
4537 static int
handler_paranoid_confirmation(void)4538 handler_paranoid_confirmation(void)
4539 {
4540 winid tmpwin;
4541 anything any;
4542 int i;
4543 menu_item *paranoia_picks = (menu_item *) 0;
4544
4545 tmpwin = create_nhwindow(NHW_MENU);
4546 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4547 any = cg.zeroany;
4548 for (i = 0; paranoia[i].flagmask != 0; ++i) {
4549 if (paranoia[i].flagmask == PARANOID_BONES && !wizard)
4550 continue;
4551 any.a_int = paranoia[i].flagmask;
4552 add_menu(tmpwin, &nul_glyphinfo, &any, *paranoia[i].argname,
4553 0, ATR_NONE, paranoia[i].explain,
4554 (flags.paranoia_bits & paranoia[i].flagmask)
4555 ? MENU_ITEMFLAGS_SELECTED
4556 : MENU_ITEMFLAGS_NONE);
4557 }
4558 end_menu(tmpwin, "Actions requiring extra confirmation:");
4559 i = select_menu(tmpwin, PICK_ANY, ¶noia_picks);
4560 if (i >= 0) {
4561 /* player didn't cancel; we reset all the paranoia options
4562 here even if there were no items picked, since user
4563 could have toggled off preselected ones to end up with 0 */
4564 flags.paranoia_bits = 0;
4565 if (i > 0) {
4566 /* at least 1 item set, either preselected or newly picked */
4567 while (--i >= 0)
4568 flags.paranoia_bits |= paranoia_picks[i].item.a_int;
4569 free((genericptr_t) paranoia_picks);
4570 }
4571 }
4572 destroy_nhwindow(tmpwin);
4573 return optn_ok;
4574 }
4575
4576 static int
handler_pickup_burden(void)4577 handler_pickup_burden(void)
4578 {
4579 winid tmpwin;
4580 anything any;
4581 int i;
4582 const char *burden_name, *burden_letters = "ubsntl";
4583 menu_item *burden_pick = (menu_item *) 0;
4584
4585 tmpwin = create_nhwindow(NHW_MENU);
4586 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4587 any = cg.zeroany;
4588 for (i = 0; i < SIZE(burdentype); i++) {
4589 burden_name = burdentype[i];
4590 any.a_int = i + 1;
4591 add_menu(tmpwin, &nul_glyphinfo, &any, burden_letters[i],
4592 0, ATR_NONE, burden_name, MENU_ITEMFLAGS_NONE);
4593 }
4594 end_menu(tmpwin, "Select encumbrance level:");
4595 if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) {
4596 flags.pickup_burden = burden_pick->item.a_int - 1;
4597 free((genericptr_t) burden_pick);
4598 }
4599 destroy_nhwindow(tmpwin);
4600 return optn_ok;
4601 }
4602
4603 static int
handler_pickup_types(void)4604 handler_pickup_types(void)
4605 {
4606 char buf[BUFSZ];
4607
4608 /* parseoptions will prompt for the list of types */
4609 (void) parseoptions(strcpy(buf, "pickup_types"), FALSE, FALSE);
4610 return optn_ok;
4611 }
4612
4613 static int
handler_runmode(void)4614 handler_runmode(void)
4615 {
4616 winid tmpwin;
4617 anything any;
4618 int i;
4619 const char *mode_name;
4620 menu_item *mode_pick = (menu_item *) 0;
4621
4622 tmpwin = create_nhwindow(NHW_MENU);
4623 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4624 any = cg.zeroany;
4625 for (i = 0; i < SIZE(runmodes); i++) {
4626 mode_name = runmodes[i];
4627 any.a_int = i + 1;
4628 add_menu(tmpwin, &nul_glyphinfo, &any, *mode_name,
4629 0, ATR_NONE, mode_name, MENU_ITEMFLAGS_NONE);
4630 }
4631 end_menu(tmpwin, "Select run/travel display mode:");
4632 if (select_menu(tmpwin, PICK_ONE, &mode_pick) > 0) {
4633 flags.runmode = mode_pick->item.a_int - 1;
4634 free((genericptr_t) mode_pick);
4635 }
4636 destroy_nhwindow(tmpwin);
4637 return optn_ok;
4638 }
4639
4640 static int
handler_sortloot(void)4641 handler_sortloot(void)
4642 {
4643 winid tmpwin;
4644 anything any;
4645 int i, n;
4646 const char *sortl_name;
4647 menu_item *sortl_pick = (menu_item *) 0;
4648
4649 tmpwin = create_nhwindow(NHW_MENU);
4650 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4651 any = cg.zeroany;
4652 for (i = 0; i < SIZE(sortltype); i++) {
4653 sortl_name = sortltype[i];
4654 any.a_char = *sortl_name;
4655 add_menu(tmpwin, &nul_glyphinfo, &any, *sortl_name,
4656 0, ATR_NONE,
4657 sortl_name, (flags.sortloot == *sortl_name)
4658 ? MENU_ITEMFLAGS_SELECTED
4659 : MENU_ITEMFLAGS_NONE);
4660 }
4661 end_menu(tmpwin, "Select loot sorting type:");
4662 n = select_menu(tmpwin, PICK_ONE, &sortl_pick);
4663 if (n > 0) {
4664 char c = sortl_pick[0].item.a_char;
4665
4666 if (n > 1 && c == flags.sortloot)
4667 c = sortl_pick[1].item.a_char;
4668 flags.sortloot = c;
4669 /* changing to or from 'f' affects persistent inventory display */
4670 if (iflags.perm_invent)
4671 update_inventory();
4672 free((genericptr_t) sortl_pick);
4673 }
4674 destroy_nhwindow(tmpwin);
4675 return optn_ok;
4676 }
4677
4678 static int
handler_whatis_coord(void)4679 handler_whatis_coord(void)
4680 {
4681 winid tmpwin;
4682 anything any;
4683 char buf[BUFSZ];
4684 menu_item *window_pick = (menu_item *) 0;
4685 int pick_cnt;
4686 char gp = iflags.getpos_coords;
4687
4688 tmpwin = create_nhwindow(NHW_MENU);
4689 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4690 any = cg.zeroany;
4691 any.a_char = GPCOORDS_COMPASS;
4692 add_menu(tmpwin, &nul_glyphinfo, &any, GPCOORDS_COMPASS,
4693 0, ATR_NONE,
4694 "compass ('east' or '3s' or '2n,4w')",
4695 (gp == GPCOORDS_COMPASS)
4696 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4697 any.a_char = GPCOORDS_COMFULL;
4698 add_menu(tmpwin, &nul_glyphinfo, &any, GPCOORDS_COMFULL,
4699 0, ATR_NONE,
4700 "full compass ('east' or '3south' or '2north,4west')",
4701 (gp == GPCOORDS_COMFULL)
4702 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4703 any.a_char = GPCOORDS_MAP;
4704 add_menu(tmpwin, &nul_glyphinfo, &any, GPCOORDS_MAP,
4705 0, ATR_NONE, "map <x,y>",
4706 (gp == GPCOORDS_MAP)
4707 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4708 any.a_char = GPCOORDS_SCREEN;
4709 add_menu(tmpwin, &nul_glyphinfo, &any, GPCOORDS_SCREEN,
4710 0, ATR_NONE, "screen [row,column]",
4711 (gp == GPCOORDS_SCREEN)
4712 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4713 any.a_char = GPCOORDS_NONE;
4714 add_menu(tmpwin, &nul_glyphinfo, &any, GPCOORDS_NONE,
4715 0, ATR_NONE, "none (no coordinates displayed)",
4716 (gp == GPCOORDS_NONE)
4717 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4718 any.a_long = 0L;
4719 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
4720 "", MENU_ITEMFLAGS_NONE);
4721 Sprintf(buf, "map: upper-left: <%d,%d>, lower-right: <%d,%d>%s",
4722 1, 0, COLNO - 1, ROWNO - 1,
4723 flags.verbose ? "; column 0 unused, off left edge" : "");
4724 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
4725 ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
4726 if (strcmp(windowprocs.name, "tty")) /* only show for non-tty */
4727 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
4728 "screen: row is offset to accommodate tty interface's use of top line",
4729 MENU_ITEMFLAGS_NONE);
4730 #if COLNO == 80
4731 #define COL80ARG flags.verbose ? "; column 80 is not used" : ""
4732 #else
4733 #define COL80ARG ""
4734 #endif
4735 Sprintf(buf, "screen: upper-left: [%02d,%02d], lower-right: [%d,%d]%s",
4736 0 + 2, 1, ROWNO - 1 + 2, COLNO - 1, COL80ARG);
4737 #undef COL80ARG
4738 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
4739 buf, MENU_ITEMFLAGS_NONE);
4740 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, ATR_NONE,
4741 "", MENU_ITEMFLAGS_NONE);
4742 end_menu(tmpwin,
4743 "Select coordinate display when auto-describing a map position:");
4744 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
4745 iflags.getpos_coords = window_pick[0].item.a_char;
4746 /* PICK_ONE doesn't unselect preselected entry when
4747 selecting another one */
4748 if (pick_cnt > 1 && iflags.getpos_coords == gp)
4749 iflags.getpos_coords = window_pick[1].item.a_char;
4750 free((genericptr_t) window_pick);
4751 }
4752 destroy_nhwindow(tmpwin);
4753 return optn_ok;
4754 }
4755
4756 static int
handler_whatis_filter(void)4757 handler_whatis_filter(void)
4758 {
4759 winid tmpwin;
4760 anything any;
4761 menu_item *window_pick = (menu_item *) 0;
4762 int pick_cnt;
4763 char gf = iflags.getloc_filter;
4764
4765 tmpwin = create_nhwindow(NHW_MENU);
4766 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4767 any = cg.zeroany;
4768 any.a_char = (GFILTER_NONE + 1);
4769 add_menu(tmpwin, &nul_glyphinfo, &any, 'n',
4770 0, ATR_NONE, "no filtering",
4771 (gf == GFILTER_NONE)
4772 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4773 any.a_char = (GFILTER_VIEW + 1);
4774 add_menu(tmpwin, &nul_glyphinfo, &any, 'v',
4775 0, ATR_NONE, "in view only",
4776 (gf == GFILTER_VIEW)
4777 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4778 any.a_char = (GFILTER_AREA + 1);
4779 add_menu(tmpwin, &nul_glyphinfo, &any, 'a',
4780 0, ATR_NONE, "in same area",
4781 (gf == GFILTER_AREA)
4782 ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
4783 end_menu(tmpwin,
4784 "Select location filtering when going for next/previous map position:");
4785 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &window_pick)) > 0) {
4786 iflags.getloc_filter = (window_pick[0].item.a_char - 1);
4787 /* PICK_ONE doesn't unselect preselected entry when
4788 selecting another one */
4789 if (pick_cnt > 1 && iflags.getloc_filter == gf)
4790 iflags.getloc_filter = (window_pick[1].item.a_char - 1);
4791 free((genericptr_t) window_pick);
4792 }
4793 destroy_nhwindow(tmpwin);
4794 return optn_ok;
4795 }
4796
4797 static int
handler_autopickup_exception(void)4798 handler_autopickup_exception(void)
4799 {
4800 winid tmpwin;
4801 anything any;
4802 int i;
4803 int opt_idx, numapes = 0;
4804 char apebuf[2 + BUFSZ]; /* so &apebuf[1] is BUFSZ long for getlin() */
4805 struct autopickup_exception *ape;
4806
4807 ape_again:
4808 numapes = count_apes();
4809 opt_idx = handle_add_list_remove("autopickup exception", numapes);
4810 if (opt_idx == 3) { /* done */
4811 return TRUE;
4812 } else if (opt_idx == 0) { /* add new */
4813 /* EDIT_GETLIN: assume user doesn't user want previous
4814 exception used as default input string for this one... */
4815 apebuf[0] = apebuf[1] = '\0';
4816 getlin("What new autopickup exception pattern?", &apebuf[1]);
4817 mungspaces(&apebuf[1]); /* regularize whitespace */
4818 if (apebuf[1] == '\033')
4819 return TRUE;
4820 if (apebuf[1]) {
4821 apebuf[0] = '\"';
4822 /* guarantee room for \" prefix and \"\0 suffix;
4823 -2 is good enough for apebuf[] but -3 makes
4824 sure the whole thing fits within normal BUFSZ */
4825 apebuf[sizeof apebuf - 2] = '\0';
4826 Strcat(apebuf, "\"");
4827 add_autopickup_exception(apebuf);
4828 }
4829 goto ape_again;
4830 } else { /* list (1) or remove (2) */
4831 int pick_idx, pick_cnt;
4832 menu_item *pick_list = (menu_item *) 0;
4833
4834 tmpwin = create_nhwindow(NHW_MENU);
4835 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4836 if (numapes) {
4837 ape = g.apelist;
4838 any = cg.zeroany;
4839 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
4840 iflags.menu_headings,
4841 "Always pickup '<'; never pickup '>'",
4842 MENU_ITEMFLAGS_NONE);
4843 for (i = 0; i < numapes && ape; i++) {
4844 any.a_void = (opt_idx == 1) ? 0 : ape;
4845 /* length of pattern plus quotes (plus '<'/'>') is
4846 less than BUFSZ */
4847 Sprintf(apebuf, "\"%c%s\"", ape->grab ? '<' : '>',
4848 ape->pattern);
4849 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
4850 ATR_NONE, apebuf, MENU_ITEMFLAGS_NONE);
4851 ape = ape->next;
4852 }
4853 }
4854 Sprintf(apebuf, "%s autopickup exceptions",
4855 (opt_idx == 1) ? "List of" : "Remove which");
4856 end_menu(tmpwin, apebuf);
4857 pick_cnt = select_menu(tmpwin,
4858 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4859 &pick_list);
4860 if (pick_cnt > 0) {
4861 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4862 remove_autopickup_exception(
4863 (struct autopickup_exception *)
4864 pick_list[pick_idx].item.a_void);
4865 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4866 }
4867 destroy_nhwindow(tmpwin);
4868 if (pick_cnt >= 0)
4869 goto ape_again;
4870 }
4871 return optn_ok;
4872 }
4873
4874 static int
handler_menu_colors(void)4875 handler_menu_colors(void)
4876 {
4877 winid tmpwin;
4878 anything any;
4879 char buf[BUFSZ];
4880 int opt_idx, nmc, mcclr, mcattr;
4881 char mcbuf[BUFSZ];
4882
4883 menucolors_again:
4884 nmc = count_menucolors();
4885 opt_idx = handle_add_list_remove("menucolor", nmc);
4886 if (opt_idx == 3) { /* done */
4887 menucolors_done:
4888 /* in case we've made a change which impacts current persistent
4889 inventory window; we don't track whether an actual changed
4890 occurred, so just assume there was one and that it matters;
4891 if we're wrong, a redundant update is cheap... */
4892 if (iflags.use_menu_color && iflags.perm_invent)
4893 update_inventory();
4894
4895 /* menu colors aren't being used; if any are defined, remind
4896 player how to use them */
4897 else if (nmc > 0)
4898 pline(
4899 "To have menu colors become active, toggle 'menucolors' option to True.");
4900 return optn_ok;
4901
4902 } else if (opt_idx == 0) { /* add new */
4903 mcbuf[0] = '\0';
4904 getlin("What new menucolor pattern?", mcbuf);
4905 if (*mcbuf == '\033')
4906 goto menucolors_done;
4907 if (*mcbuf
4908 && test_regex_pattern(mcbuf, "MENUCOLORS regex")
4909 && (mcclr = query_color((char *) 0)) != -1
4910 && (mcattr = query_attr((char *) 0)) != -1
4911 && !add_menu_coloring_parsed(mcbuf, mcclr, mcattr)) {
4912 pline("Error adding the menu color.");
4913 wait_synch();
4914 }
4915 goto menucolors_again;
4916
4917 } else { /* list (1) or remove (2) */
4918 int pick_idx, pick_cnt;
4919 int mc_idx;
4920 unsigned ln;
4921 const char *sattr, *sclr;
4922 menu_item *pick_list = (menu_item *) 0;
4923 struct menucoloring *tmp = g.menu_colorings;
4924 char clrbuf[QBUFSZ];
4925
4926 tmpwin = create_nhwindow(NHW_MENU);
4927 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
4928 any = cg.zeroany;
4929 mc_idx = 0;
4930 while (tmp) {
4931 sattr = attr2attrname(tmp->attr);
4932 sclr = strcpy(clrbuf, clr2colorname(tmp->color));
4933 (void) strNsubst(clrbuf, " ", "-", 0);
4934 any.a_int = ++mc_idx;
4935 /* construct suffix */
4936 Sprintf(buf, "\"\"=%s%s%s", sclr,
4937 (tmp->attr != ATR_NONE) ? "&" : "",
4938 (tmp->attr != ATR_NONE) ? sattr : "");
4939 /* now main string */
4940 ln = sizeof buf - strlen(buf) - 1; /* length available */
4941 Strcpy(mcbuf, "\"");
4942 if (strlen(tmp->origstr) > ln)
4943 Strcat(strncat(mcbuf, tmp->origstr, ln - 3), "...");
4944 else
4945 Strcat(mcbuf, tmp->origstr);
4946 /* combine main string and suffix */
4947 Strcat(mcbuf, &buf[1]); /* skip buf[]'s initial quote */
4948 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
4949 ATR_NONE, mcbuf, MENU_ITEMFLAGS_NONE);
4950 tmp = tmp->next;
4951 }
4952 Sprintf(mcbuf, "%s menu colors",
4953 (opt_idx == 1) ? "List of" : "Remove which");
4954 end_menu(tmpwin, mcbuf);
4955 pick_cnt = select_menu(tmpwin,
4956 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
4957 &pick_list);
4958 if (pick_cnt > 0) {
4959 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
4960 free_one_menu_coloring(pick_list[pick_idx].item.a_int - 1
4961 - pick_idx);
4962 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
4963 }
4964 destroy_nhwindow(tmpwin);
4965 if (pick_cnt >= 0)
4966 goto menucolors_again;
4967 }
4968 return optn_ok;
4969 }
4970
4971 static int
handler_msgtype(void)4972 handler_msgtype(void)
4973 {
4974 winid tmpwin;
4975 anything any;
4976 int opt_idx, nmt, mttyp;
4977 char mtbuf[BUFSZ];
4978
4979 msgtypes_again:
4980 nmt = msgtype_count();
4981 opt_idx = handle_add_list_remove("message type", nmt);
4982 if (opt_idx == 3) { /* done */
4983 return TRUE;
4984 } else if (opt_idx == 0) { /* add new */
4985 mtbuf[0] = '\0';
4986 getlin("What new message pattern?", mtbuf);
4987 if (*mtbuf == '\033')
4988 return TRUE;
4989 if (*mtbuf
4990 && test_regex_pattern(mtbuf, "MSGTYPE regex")
4991 && (mttyp = query_msgtype()) != -1
4992 && !msgtype_add(mttyp, mtbuf)) {
4993 pline("Error adding the message type.");
4994 wait_synch();
4995 }
4996 goto msgtypes_again;
4997 } else { /* list (1) or remove (2) */
4998 int pick_idx, pick_cnt;
4999 int mt_idx;
5000 unsigned ln;
5001 const char *mtype;
5002 menu_item *pick_list = (menu_item *) 0;
5003 struct plinemsg_type *tmp = g.plinemsg_types;
5004
5005 tmpwin = create_nhwindow(NHW_MENU);
5006 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
5007 any = cg.zeroany;
5008 mt_idx = 0;
5009 while (tmp) {
5010 mtype = msgtype2name(tmp->msgtype);
5011 any.a_int = ++mt_idx;
5012 Sprintf(mtbuf, "%-5s \"", mtype);
5013 ln = sizeof mtbuf - strlen(mtbuf) - sizeof "\"";
5014 if (strlen(tmp->pattern) > ln)
5015 Strcat(strncat(mtbuf, tmp->pattern, ln - 3), "...\"");
5016 else
5017 Strcat(strcat(mtbuf, tmp->pattern), "\"");
5018 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
5019 ATR_NONE, mtbuf, MENU_ITEMFLAGS_NONE);
5020 tmp = tmp->next;
5021 }
5022 Sprintf(mtbuf, "%s message types",
5023 (opt_idx == 1) ? "List of" : "Remove which");
5024 end_menu(tmpwin, mtbuf);
5025 pick_cnt = select_menu(tmpwin,
5026 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
5027 &pick_list);
5028 if (pick_cnt > 0) {
5029 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx)
5030 free_one_msgtype(pick_list[pick_idx].item.a_int - 1
5031 - pick_idx);
5032 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
5033 }
5034 destroy_nhwindow(tmpwin);
5035 if (pick_cnt >= 0)
5036 goto msgtypes_again;
5037 }
5038 return optn_ok;
5039 }
5040
5041 static int
handler_monstercolor(void)5042 handler_monstercolor(void)
5043 {
5044 winid tmpwin;
5045 anything any;
5046 int opt_idx, nummoncolors = 0;
5047 int mndx = NON_PM, color = MONSTERCOLOR_DEFAULT;
5048 char buf[BUFSZ];
5049 do {
5050 nummoncolors = count_monstercolors();
5051 opt_idx = handle_add_list_remove("monster color", nummoncolors);
5052 if (opt_idx == 3) { /* done */
5053 break;
5054 } else if (opt_idx == 0) { /* add new */
5055 /* EDIT_GETLIN: assume user doesn't user want previous
5056 exception used as default input string for this one... */
5057 buf[0] = '\0';
5058 getlin("What monster do you want to recolor?", buf);
5059 mungspaces(buf); /* regularize whitespace */
5060 if (buf[0] == '\033')
5061 break;
5062
5063 /* don't bother parsing the monster name here, it will be done in
5064 * add_monstercolor() */
5065
5066 /* note: NO_COLOR is valid, despite not being default on any
5067 * monsters */
5068 color = query_color((const char *) 0);
5069 if (color < 0) { /* escaped out of the prompt */
5070 break;
5071 }
5072
5073 /* prepare str in config-file format to pass to add_monstercolor */
5074 Strcat(buf, ":");
5075 Strcat(buf, clr2colorname(color));
5076 add_monstercolor(buf);
5077
5078 continue;
5079 } else { /* list (1) or remove (2) */
5080 int pick_idx, pick_cnt;
5081 menu_item *pick_list = (menu_item *) 0;
5082
5083 /* replace user patterns with color name ones and force 'menucolors'
5084 * On */
5085 basic_menu_colors(TRUE);
5086
5087 tmpwin = create_nhwindow(NHW_MENU);
5088 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
5089 if (nummoncolors) {
5090 any = cg.zeroany;
5091 for (mndx = LOW_PM; mndx < NUMMONS; ++mndx) {
5092 if (g.monstercolors[mndx] != MONSTERCOLOR_DEFAULT) {
5093 glyph_info info;
5094 map_glyphinfo(0, 0, monnum_to_glyph(mndx), 0, &info);
5095 any.a_int = mndx;
5096 Sprintf(buf, "%s (%s)", mons[mndx].pmnames[NEUTRAL],
5097 clr2colorname(g.monstercolors[mndx]));
5098 add_menu(tmpwin, &info, &any, 0, 0,
5099 ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
5100 }
5101 }
5102 }
5103 Sprintf(buf, "%s monster colors",
5104 (opt_idx == 1) ? "List of" : "Remove which");
5105 end_menu(tmpwin, buf);
5106 pick_cnt = select_menu(tmpwin,
5107 (opt_idx == 1) ? PICK_NONE : PICK_ANY,
5108 &pick_list);
5109 if (pick_cnt > 0) {
5110 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
5111 mndx = pick_list[pick_idx].item.a_int;
5112 g.monstercolors[mndx] = MONSTERCOLOR_DEFAULT;
5113 }
5114 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
5115 }
5116
5117 /* remove temporary color name patterns and restore user-specified
5118 * ones; reset 'menucolors' option to its previous value */
5119 basic_menu_colors(FALSE);
5120
5121 destroy_nhwindow(tmpwin);
5122 if (pick_cnt < 0)
5123 break;
5124 }
5125 } while (opt_idx != 3); /* until something breaks out */
5126 docrt();
5127 return optn_ok;
5128 }
5129
5130
5131 /*
5132 **********************************
5133 *
5134 * Parsing Support Functions
5135 *
5136 **********************************
5137 */
5138
5139 static char *
string_for_opt(char * opts,boolean val_optional)5140 string_for_opt(char *opts, boolean val_optional)
5141 {
5142 char *colon, *equals;
5143
5144 colon = index(opts, ':');
5145 equals = index(opts, '=');
5146 if (!colon || (equals && equals < colon))
5147 colon = equals;
5148
5149 if (!colon || !*++colon) {
5150 if (!val_optional)
5151 config_error_add("Missing parameter for '%s'", opts);
5152 return empty_optstr;
5153 }
5154 return colon;
5155 }
5156
5157 static char *
string_for_env_opt(const char * optname,char * opts,boolean val_optional)5158 string_for_env_opt(const char *optname, char *opts, boolean val_optional)
5159 {
5160 if (!g.opt_initial) {
5161 rejectoption(optname);
5162 return empty_optstr;
5163 }
5164 return string_for_opt(opts, val_optional);
5165 }
5166
5167 static void
bad_negation(const char * optname,boolean with_parameter)5168 bad_negation(const char *optname, boolean with_parameter)
5169 {
5170 pline_The("%s option may not %sbe negated.", optname,
5171 with_parameter ? "both have a value and " : "");
5172 }
5173
5174 /* go through all of the options and set the minmatch value
5175 based on what is needed for uniqueness of each individual
5176 option. Set a minimum of 3 characters. */
5177 void
determine_ambiguities(void)5178 determine_ambiguities(void)
5179 {
5180 int i, j, len, tmpneeded, needed[SIZE(allopt)];
5181 const char *p1, *p2;
5182
5183 for (i = 0; i < SIZE(allopt) - 1; ++i) {
5184 needed[i] = 0;
5185 }
5186
5187 for (i = 0; i < SIZE(allopt) - 1; ++i) {
5188 for (j = 0; j < SIZE(allopt) - 1; ++j) {
5189 if (j == i)
5190 continue;
5191
5192 p1 = allopt[i].name; /* back to the start */
5193 p2 = allopt[j].name;
5194 tmpneeded = 1;
5195 while (*p1 && *p2 && lowc(*p1) == lowc(*p2)) {
5196 ++tmpneeded;
5197 ++p1;
5198 ++p2;
5199 }
5200 if (tmpneeded > needed[i])
5201 needed[i] = tmpneeded;
5202 if (tmpneeded > needed[j])
5203 needed[j] = tmpneeded;
5204 }
5205 }
5206 for (i = 0; i < SIZE(allopt) - 1; ++i) {
5207 len = strlen(allopt[i].name);
5208 allopt[i].minmatch = (needed[i] < 3) ? 3
5209 : (needed[i] <= len) ? needed[i] : len;
5210 }
5211 }
5212
5213 int
length_without_val(const char * user_string,int len)5214 length_without_val(const char *user_string, int len)
5215 {
5216 const char *p = index(user_string, ':'),
5217 *q = index(user_string, '=');
5218
5219 if (!p || (q && q < p))
5220 p = q;
5221 if (p) {
5222 /* 'user_string' hasn't necessarily been through mungspaces()
5223 so might have tabs or consecutive spaces */
5224 while (p > user_string && isspace((uchar) *(p - 1)))
5225 p--;
5226 len = (int) (p - user_string);
5227 }
5228 return len;
5229 }
5230
5231 /* check whether a user-supplied option string is a proper leading
5232 substring of a particular option name; option string might have
5233 a colon or equals sign and arbitrary value appended to it */
5234 boolean
match_optname(const char * user_string,const char * optn_name,int min_length,boolean val_allowed)5235 match_optname(const char *user_string, const char *optn_name,
5236 int min_length, boolean val_allowed)
5237 {
5238 int len = (int) strlen(user_string);
5239
5240 if (val_allowed)
5241 len = length_without_val(user_string, len);
5242
5243 return (boolean) (len >= min_length
5244 && !strncmpi(optn_name, user_string, len));
5245 }
5246
5247 void
reset_duplicate_opt_detection(void)5248 reset_duplicate_opt_detection(void)
5249 {
5250 int k;
5251
5252 for (k = 0; k < OPTCOUNT; ++k)
5253 allopt[k].dupdetected = 0;
5254 }
5255
5256 static boolean
duplicate_opt_detection(int optidx)5257 duplicate_opt_detection(int optidx)
5258 {
5259 if (g.opt_initial && g.opt_from_file)
5260 return allopt[optidx].dupdetected++;
5261 return FALSE;
5262 }
5263
5264 static void
complain_about_duplicate(int optidx)5265 complain_about_duplicate(int optidx)
5266 {
5267 char buf[BUFSZ];
5268
5269 #ifdef MAC
5270 /* the Mac has trouble dealing with the output of messages while
5271 * processing the config file. That should get fixed one day.
5272 * For now just return.
5273 */
5274 #else /* !MAC */
5275 buf[0] = '\0';
5276 if (using_alias)
5277 Sprintf(buf, " (via alias: %s)", allopt[optidx].alias);
5278 config_error_add("%s option specified multiple times: %s%s",
5279 (allopt[optidx].opttyp == CompOpt) ? "compound"
5280 : "boolean",
5281 allopt[optidx].name, buf);
5282 #endif /* ?MAC */
5283 return;
5284 }
5285
5286 static void
rejectoption(const char * optname)5287 rejectoption(const char *optname)
5288 {
5289 #ifdef MICRO
5290 pline("\"%s\" settable only from %s.", optname, configfile);
5291 #else
5292 pline("%s can be set only from NETHACKOPTIONS or %s.", optname,
5293 configfile);
5294 #endif
5295 }
5296
5297 /*
5298
5299 # errors:
5300 OPTIONS=aaaaaaaaaa[ more than 247 (255 - 8 for 'OPTIONS=') total ]aaaaaaaaaa
5301 OPTIONS
5302 OPTIONS=
5303 MSGTYPE=stop"You swap places with "
5304 MSGTYPE=st.op "You swap places with "
5305 MSGTYPE=stop "You swap places with \"
5306 MENUCOLOR=" blessed "green&none
5307 MENUCOLOR=" holy " = green&reverse
5308 MENUCOLOR=" cursed " = red&uline
5309 MENUCOLOR=" unholy " = reed
5310 OPTIONS=!legacy:true,fooo
5311 OPTIONS=align:!pin
5312 OPTIONS=gender
5313
5314 */
5315 /* most environment variables will eventually be printed in an error
5316 * message if they don't work, and most error message paths go through
5317 * BUFSZ buffers, which could be overflowed by a maliciously long
5318 * environment variable. If a variable can legitimately be long, or
5319 * if it's put in a smaller buffer, the responsible code will have to
5320 * bounds-check itself.
5321 */
5322 char *
nh_getenv(const char * ev)5323 nh_getenv(const char *ev)
5324 {
5325 char *getev = getenv(ev);
5326
5327 if (getev && strlen(getev) <= (BUFSZ / 2))
5328 return getev;
5329 else
5330 return (char *) 0;
5331 }
5332
5333 /* copy up to maxlen-1 characters; 'dest' must be able to hold maxlen;
5334 treat comma as alternate end of 'src' */
5335 static void
nmcpy(char * dest,const char * src,int maxlen)5336 nmcpy(char *dest, const char *src, int maxlen)
5337 {
5338 int count;
5339
5340 for (count = 1; count < maxlen; count++) {
5341 if (*src == ',' || *src == '\0')
5342 break; /*exit on \0 terminator*/
5343 *dest++ = *src++;
5344 }
5345 *dest = '\0';
5346 }
5347
5348 /*
5349 * escapes(): escape expansion for showsyms. C-style escapes understood
5350 * include \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal).
5351 * (Note: unlike in C, leading digit 0 is not used to indicate octal;
5352 * the letter o (either upper or lower case) is used for that.
5353 * The ^-prefix for control characters is also understood, and \[mM]
5354 * has the effect of 'meta'-ing the value which follows (so that the
5355 * alternate character set will be enabled).
5356 *
5357 * X normal key X
5358 * ^X control-X
5359 * \mX meta-X
5360 *
5361 * For 3.4.3 and earlier, input ending with "\M", backslash, or caret
5362 * prior to terminating '\0' would pull that '\0' into the output and then
5363 * keep processing past it, potentially overflowing the output buffer.
5364 * Now, trailing \ or ^ will act like \\ or \^ and add '\\' or '^' to the
5365 * output and stop there; trailing \M will fall through to \<other> and
5366 * yield 'M', then stop. Any \X or \O followed by something other than
5367 * an appropriate digit will also fall through to \<other> and yield 'X'
5368 * or 'O', plus stop if the non-digit is end-of-string.
5369 */
5370 static void
escapes(const char * cp,char * tp)5371 escapes(const char *cp, /* might be 'tp', updating in place */
5372 char *tp) /* result is never longer than 'cp' */
5373 {
5374 static NEARDATA const char oct[] = "01234567", dec[] = "0123456789",
5375 hex[] = "00112233445566778899aAbBcCdDeEfF";
5376 const char *dp;
5377 int cval, meta, dcount;
5378
5379 while (*cp) {
5380 /* \M has to be followed by something to do meta conversion,
5381 otherwise it will just be \M which ultimately yields 'M' */
5382 meta = (*cp == '\\' && (cp[1] == 'm' || cp[1] == 'M') && cp[2]);
5383 if (meta)
5384 cp += 2;
5385
5386 cval = dcount = 0; /* for decimal, octal, hexadecimal cases */
5387 if ((*cp != '\\' && *cp != '^') || !cp[1]) {
5388 /* simple character, or nothing left for \ or ^ to escape */
5389 cval = *cp++;
5390 } else if (*cp == '^') { /* expand control-character syntax */
5391 cval = (*++cp & 0x1f);
5392 ++cp;
5393
5394 /* remaining cases are all for backslash; we know cp[1] is not \0 */
5395 } else if (index(dec, cp[1])) {
5396 ++cp; /* move past backslash to first digit */
5397 do {
5398 cval = (cval * 10) + (*cp - '0');
5399 } while (*++cp && index(dec, *cp) && ++dcount < 3);
5400 } else if ((cp[1] == 'o' || cp[1] == 'O') && cp[2]
5401 && index(oct, cp[2])) {
5402 cp += 2; /* move past backslash and 'O' */
5403 do {
5404 cval = (cval * 8) + (*cp - '0');
5405 } while (*++cp && index(oct, *cp) && ++dcount < 3);
5406 } else if ((cp[1] == 'x' || cp[1] == 'X') && cp[2]
5407 && (dp = index(hex, cp[2])) != 0) {
5408 cp += 2; /* move past backslash and 'X' */
5409 do {
5410 cval = (cval * 16) + ((int) (dp - hex) / 2);
5411 } while (*++cp && (dp = index(hex, *cp)) != 0 && ++dcount < 2);
5412 } else { /* C-style character escapes */
5413 switch (*++cp) {
5414 case '\\':
5415 cval = '\\';
5416 break;
5417 case 'n':
5418 cval = '\n';
5419 break;
5420 case 't':
5421 cval = '\t';
5422 break;
5423 case 'b':
5424 cval = '\b';
5425 break;
5426 case 'r':
5427 cval = '\r';
5428 break;
5429 default:
5430 cval = *cp;
5431 }
5432 ++cp;
5433 }
5434
5435 if (meta)
5436 cval |= 0x80;
5437 *tp++ = (char) cval;
5438 }
5439 *tp = '\0';
5440 }
5441
5442 /* returns a one-byte character from the text; may change txt[];
5443 moved from cmd.c in order to get access to escapes() */
5444 uchar
txt2key(char * txt)5445 txt2key(char *txt)
5446 {
5447 uchar uc;
5448 boolean makemeta = FALSE;
5449
5450 txt = trimspaces(txt);
5451 if (!*txt)
5452 return '\0';
5453
5454 /* simple character */
5455 if (!txt[1])
5456 return (uchar) txt[0];
5457
5458 /* a few special entries */
5459 if (!strcmp(txt, "<enter>"))
5460 return '\n';
5461 if (!strcmp(txt, "<space>"))
5462 return ' ';
5463 if (!strcmp(txt, "<esc>"))
5464 return '\033';
5465
5466 /* handle things like \b and \7 and \mX */
5467 if (*txt == '\\') {
5468 char tbuf[QBUFSZ];
5469
5470 if (strlen(txt) >= sizeof tbuf)
5471 txt[sizeof tbuf - 1] = '\0';
5472 escapes(txt, tbuf);
5473 return *tbuf;
5474 }
5475
5476 /* control and meta keys */
5477 if (highc(*txt) == 'M') {
5478 /*
5479 * M <nothing> return 'M'
5480 * M - <nothing> return M-'-'
5481 * M <other><nothing> return M-<other>
5482 * otherwise M is pending until after ^/C- processing.
5483 * Since trailing spaces are discarded, the only way to
5484 * specify M-' ' is via "160".
5485 */
5486 if (!txt[1])
5487 return (uchar) *txt;
5488 /* skip past 'M' or 'm' and maybe '-' */
5489 ++txt;
5490 if (*txt == '-' && txt[1])
5491 ++txt;
5492 if (!txt[1])
5493 return M((uchar) *txt);
5494 makemeta = TRUE;
5495 }
5496 if (*txt == '^' || highc(*txt) == 'C') {
5497 /*
5498 * C <nothing> return 'C' or M-'C'
5499 * C - <nothing> return '-' or M-'-'
5500 * C [-] <other><nothing> return C-<other> or M-C-<other>
5501 * C [-] ? return <rubout>
5502 * otherwise return C-<other> or M-C-<other>
5503 */
5504 uc = (uchar) *txt;
5505 if (!txt[1])
5506 return makemeta ? M(uc) : uc;
5507 ++txt;
5508 /* unlike M-x, lots of values of x are invalid for C-x;
5509 checking and rejecting them is not worthwhile; GIGO;
5510 we do accept "^-x" as synonym for "^x" or "C-x" */
5511 if (*txt == '-' && txt[1])
5512 ++txt;
5513 /* and accept ^?, which gets used despite not being a control char */
5514 if (*txt == '?')
5515 return (uchar) (makemeta ? '\377' : '\177'); /* rubout/delete */
5516 uc = C((uchar) *txt);
5517 return makemeta ? M(uc) : uc;
5518 }
5519 if (makemeta && *txt)
5520 return M((uchar) *txt);
5521
5522 /* FIXME: should accept single-quote single-character single-quote
5523 and probably single-quote backslash octal-digits single-quote;
5524 if we do that, the M- and C- results should be pending until
5525 after, so that C-'X' becomes valid for ^X */
5526
5527 /* ascii codes: must be three-digit decimal */
5528 if (*txt >= '0' && *txt <= '9') {
5529 uchar key = 0;
5530 int i;
5531
5532 for (i = 0; i < 3; i++) {
5533 if (txt[i] < '0' || txt[i] > '9')
5534 return '\0';
5535 key = 10 * key + txt[i] - '0';
5536 }
5537 return key;
5538 }
5539
5540 return '\0';
5541 }
5542
5543 /*
5544 **********************************
5545 *
5546 * Options Initialization
5547 *
5548 **********************************
5549 */
5550
5551 /* process options, possibly including SYSCF */
5552 void
initoptions(void)5553 initoptions(void)
5554 {
5555 int i;
5556
5557 initoptions_init();
5558 #ifdef SYSCF
5559 /* someday there may be other SYSCF alternatives besides text file */
5560 #ifdef SYSCF_FILE
5561 /* If SYSCF_FILE is specified, it _must_ exist... */
5562 assure_syscf_file();
5563 config_error_init(TRUE, SYSCF_FILE, FALSE);
5564
5565 /* Call each option function with an init flag and give it a chance
5566 to make any preparations that it might require. We do this
5567 whether or not the option itself is ever specified; that's
5568 irrelevant for the init call. Doing this allows the prep code for
5569 option settings to remain adjacent to, and in the same function as,
5570 the code that processes those options */
5571
5572 for (i = 0; i < OPTCOUNT; ++i) {
5573 if (allopt[i].optfn)
5574 (*allopt[i].optfn)(i, do_init, FALSE, empty_optstr, empty_optstr);
5575 }
5576
5577 /* ... and _must_ parse correctly. */
5578 if (!read_config_file(SYSCF_FILE, set_in_sysconf)) {
5579 if (config_error_done() && !iflags.initoptions_noterminate)
5580 nh_terminate(EXIT_FAILURE);
5581 }
5582 config_error_done();
5583 /*
5584 * TODO [maybe]: parse the sysopt entries which are space-separated
5585 * lists of usernames into arrays with one name per element.
5586 */
5587 #endif
5588 #endif /* SYSCF */
5589 initoptions_finish();
5590 }
5591
5592 void
initoptions_init(void)5593 initoptions_init(void)
5594 {
5595 #if (defined(UNIX) || defined(VMS)) && defined(TTY_GRAPHICS)
5596 char *opts;
5597 #endif
5598 int i;
5599
5600 memcpy(allopt, allopt_init, sizeof(allopt));
5601 determine_ambiguities();
5602
5603 /* set up the command parsing */
5604 reset_commands(TRUE); /* init */
5605
5606 /* initialize the random number generator(s) */
5607 init_random(rn2);
5608 init_random(rn2_on_display_rng);
5609
5610 for (i = 0; allopt[i].name; i++) {
5611 if (allopt[i].addr)
5612 *(allopt[i].addr) = allopt[i].initval;
5613 }
5614
5615 flags.end_own = FALSE;
5616 flags.end_top = 3;
5617 flags.end_around = 2;
5618 flags.paranoia_bits = PARANOID_PRAY; /* old prayconfirm=TRUE */
5619 flags.pile_limit = PILE_LIMIT_DFLT; /* 5 */
5620 flags.runmode = RUN_LEAP;
5621 iflags.msg_history = 20;
5622 /* msg_window has conflicting defaults for multi-interface binary */
5623 #ifdef TTY_GRAPHICS
5624 iflags.prevmsg_window = 's';
5625 #else
5626 #ifdef CURSES_GRAPHICS
5627 iflags.prevmsg_window = 'r';
5628 #endif
5629 #endif
5630
5631 iflags.menu_headings = ATR_INVERSE;
5632 iflags.getpos_coords = GPCOORDS_NONE;
5633 iflags.msg_is_alert = FALSE;
5634
5635 /* hero's role, race, &c haven't been chosen yet */
5636 flags.initrole = flags.initrace = flags.initgend = flags.initalign
5637 = ROLE_NONE;
5638
5639 /* polyinit hasn't been configured yet */
5640 flags.polyinit_mnum = NON_PM;
5641
5642 init_ov_primary_symbols();
5643 /* Set the default monster and object class symbols. */
5644 init_symbols();
5645 for (i = 0; i < WARNCOUNT; i++)
5646 g.warnsyms[i] = def_warnsyms[i].sym;
5647
5648 /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */
5649 (void) memcpy((genericptr_t) flags.inv_order,
5650 (genericptr_t) def_inv_order, sizeof flags.inv_order);
5651 flags.pickup_types[0] = '\0';
5652 flags.pickup_burden = MOD_ENCUMBER;
5653 flags.sortloot = 'l'; /* sort only loot by default */
5654
5655 for (i = 0; i < NUM_DISCLOSURE_OPTIONS; i++)
5656 flags.end_disclose[i] = DISCLOSE_PROMPT_DEFAULT_NO;
5657 switch_symbols(FALSE); /* set default characters */
5658 #if defined(UNIX) && defined(TTY_GRAPHICS)
5659 /*
5660 * Set defaults for some options depending on what we can
5661 * detect about the environment's capabilities.
5662 * This has to be done after the global initialization above
5663 * and before reading user-specific initialization via
5664 * config file/environment variable below.
5665 */
5666 /* this detects the IBM-compatible console on most 386 boxes */
5667 if ((opts = nh_getenv("TERM")) && !strncmp(opts, "AT", 2)) {
5668 if (!g.symset[PRIMARY].explicitly)
5669 load_symset("IBMGraphics", PRIMARY);
5670 switch_symbols(TRUE);
5671 #ifdef TEXTCOLOR
5672 iflags.use_color = TRUE;
5673 #endif
5674 }
5675 #endif /* UNIX && TTY_GRAPHICS */
5676 #if defined(UNIX) || defined(VMS)
5677 #ifdef TTY_GRAPHICS
5678 /* detect whether a "vt" terminal can handle alternate charsets */
5679 if ((opts = nh_getenv("TERM"))
5680 /* [could also check "xterm" which emulates vtXXX by default] */
5681 && !strncmpi(opts, "vt", 2)
5682 && AS && AE && index(AS, '\016') && index(AE, '\017')) {
5683 if (!g.symset[PRIMARY].explicitly)
5684 load_symset("DECGraphics", PRIMARY);
5685 switch_symbols(TRUE);
5686 }
5687 #endif
5688 #endif /* UNIX || VMS */
5689
5690 #if defined(MSDOS) || defined(WIN32)
5691 /* Use IBM defaults. Can be overridden via config file */
5692 if (!g.symset[PRIMARY].explicitly)
5693 load_symset("IBMGraphics_2", PRIMARY);
5694 #endif
5695 #ifdef MAC_GRAPHICS_ENV
5696 if (!g.symset[PRIMARY].explicitly)
5697 load_symset("MACGraphics", PRIMARY);
5698 switch_symbols(TRUE);
5699 #endif /* MAC_GRAPHICS_ENV */
5700 flags.menu_style = MENU_FULL;
5701
5702 iflags.wc_align_message = ALIGN_TOP;
5703 iflags.wc_align_status = ALIGN_BOTTOM;
5704 /* used by tty and curses */
5705 iflags.wc2_statuslines = 2;
5706 /* only used by curses */
5707 iflags.wc2_windowborders = 2; /* 'Auto' */
5708
5709 /* since this is done before init_objects(), do partial init here */
5710 objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD;
5711 nmcpy(g.pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ);
5712 }
5713
5714 void
initoptions_finish(void)5715 initoptions_finish(void)
5716 {
5717 nhsym sym = 0;
5718 #ifndef MAC
5719 char *opts = getenv("NETHACKOPTIONS");
5720
5721 if (!opts)
5722 opts = getenv("HACKOPTIONS");
5723 if (opts) {
5724 if (*opts == '/' || *opts == '\\' || *opts == '@') {
5725 if (*opts == '@')
5726 opts++; /* @filename */
5727 /* looks like a filename */
5728 if (strlen(opts) < BUFSZ / 2) {
5729 config_error_init(TRUE, opts, CONFIG_ERROR_SECURE);
5730 read_config_file(opts, set_in_config);
5731 config_error_done();
5732 }
5733 } else {
5734 config_error_init(TRUE, (char *) 0, FALSE);
5735 read_config_file((char *) 0, set_in_config);
5736 config_error_done();
5737 /* let the total length of options be long;
5738 * parseoptions() will check each individually
5739 */
5740 config_error_init(FALSE, "NETHACKOPTIONS", FALSE);
5741 (void) parseoptions(opts, TRUE, FALSE);
5742 config_error_done();
5743 }
5744 } else
5745 #endif /* !MAC */
5746 /*else*/ {
5747 config_error_init(TRUE, (char *) 0, FALSE);
5748 read_config_file((char *) 0, set_in_config);
5749 config_error_done();
5750 }
5751
5752 (void) fruitadd(g.pl_fruit, (struct fruit *) 0);
5753 /*
5754 * Remove "slime mold" from list of object names. This will
5755 * prevent it from being wished unless it's actually present
5756 * as a named (or default) fruit. Wishing for "fruit" will
5757 * result in the player's preferred fruit [better than "\033"].
5758 */
5759 obj_descr[SLIME_MOLD].oc_name = "fruit";
5760
5761 sym = get_othersym(SYM_BOULDER, PRIMARY);
5762 if (sym)
5763 g.showsyms[SYM_BOULDER + SYM_OFF_X] = sym;
5764 reglyph_darkroom();
5765
5766 #ifdef STATUS_HILITES
5767 /*
5768 * A multi-interface binary might only support status highlighting
5769 * for some of the interfaces; check whether we asked for it but are
5770 * using one which doesn't.
5771 *
5772 * Option processing can take place before a user-decided WindowPort
5773 * is even initialized, so check for that too.
5774 */
5775 if (!WINDOWPORT("safe-startup")) {
5776 if (iflags.hilite_delta && !wc2_supported("statushilites")) {
5777 raw_printf("Status highlighting not supported for %s interface.",
5778 windowprocs.name);
5779 iflags.hilite_delta = 0;
5780 }
5781 }
5782 #endif
5783 g.opt_initial = FALSE;
5784 return;
5785 }
5786
5787 /*
5788 *******************************************
5789 *
5790 * Support Functions for Individual Options
5791 *
5792 *******************************************
5793 */
5794
5795 /*
5796 * Change the inventory order, using the given string as the new order.
5797 * Missing characters in the new order are filled in at the end from
5798 * the current inv_order, except for gold, which is forced to be first
5799 * if not explicitly present.
5800 *
5801 * This routine returns 1 unless there is a duplicate or bad char in
5802 * the string.
5803 *
5804 * Used by: optfn_packorder()
5805 *
5806 */
5807 static int
change_inv_order(char * op)5808 change_inv_order(char *op)
5809 {
5810 int oc_sym, num;
5811 char *sp, buf[QBUFSZ];
5812 int retval = 1;
5813
5814 num = 0;
5815 if (!index(op, GOLD_SYM))
5816 buf[num++] = COIN_CLASS;
5817
5818 for (sp = op; *sp; sp++) {
5819 boolean fail = FALSE;
5820 oc_sym = def_char_to_objclass(*sp);
5821 /* reject bad or duplicate entries */
5822 if (oc_sym == MAXOCLASSES) { /* not an object class char */
5823 config_error_add("Not an object class '%c'", *sp);
5824 retval = 0;
5825 fail = TRUE;
5826 } else if (!index(flags.inv_order, oc_sym)) {
5827 /* VENOM_CLASS, RANDOM_CLASS, and ILLOBJ_CLASS are excluded
5828 because they aren't in def_inv_order[] so don't make it
5829 into flags.inv_order, hence always fail this index() test */
5830 config_error_add("Object class '%c' not allowed", *sp);
5831 retval = 0;
5832 fail = TRUE;
5833 } else if (index(sp + 1, *sp)) {
5834 config_error_add("Duplicate object class '%c'", *sp);
5835 retval = 0;
5836 fail = TRUE;
5837 }
5838 /* retain good ones */
5839 if (!fail)
5840 buf[num++] = (char) oc_sym;
5841 }
5842 buf[num] = '\0';
5843
5844 /* fill in any omitted classes, using previous ordering */
5845 for (sp = flags.inv_order; *sp; sp++)
5846 if (!index(buf, *sp))
5847 (void) strkitten(&buf[num++], *sp);
5848 buf[MAXOCLASSES - 1] = '\0';
5849
5850 Strcpy(flags.inv_order, buf);
5851 return retval;
5852 }
5853
5854
5855 /*
5856 * Support functions for "warning"
5857 *
5858 * Used by: optfn_warnings()
5859 *
5860 */
5861
5862 static boolean
warning_opts(char * opts,const char * optype)5863 warning_opts(char *opts, const char *optype)
5864 {
5865 uchar translate[WARNCOUNT];
5866 int length, i;
5867
5868 if ((opts = string_for_env_opt(optype, opts, FALSE)) == empty_optstr)
5869 return FALSE;
5870 escapes(opts, opts);
5871
5872 length = (int) strlen(opts);
5873 /* match the form obtained from PC configuration files */
5874 for (i = 0; i < WARNCOUNT; i++)
5875 translate[i] = (i >= length) ? 0
5876 : opts[i] ? (uchar) opts[i]
5877 : def_warnsyms[i].sym;
5878 assign_warnings(translate);
5879 return TRUE;
5880 }
5881
5882 void
assign_warnings(uchar * graph_chars)5883 assign_warnings(uchar *graph_chars)
5884 {
5885 int i;
5886
5887 for (i = 0; i < WARNCOUNT; i++)
5888 if (graph_chars[i])
5889 g.warnsyms[i] = graph_chars[i];
5890 }
5891
5892 /*
5893 * Support functions for "suppress_alert"
5894 *
5895 * Used by: optfn_suppress_alert()
5896 *
5897 */
5898
5899 static int
feature_alert_opts(char * op,const char * optn)5900 feature_alert_opts(char *op, const char *optn)
5901 {
5902 char buf[BUFSZ];
5903 unsigned long fnv = get_feature_notice_ver(op); /* version.c */
5904
5905 if (fnv == 0L)
5906 return 0;
5907 if (fnv > get_current_feature_ver()) {
5908 if (!g.opt_initial) {
5909 You_cant("disable new feature alerts for future versions.");
5910 } else {
5911 config_error_add(
5912 "%s=%s Invalid reference to a future version ignored",
5913 optn, op);
5914 }
5915 return 0;
5916 }
5917
5918 flags.suppress_alert = fnv;
5919 if (!g.opt_initial) {
5920 Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ,
5921 FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH);
5922 pline(
5923 "Feature change alerts disabled for NetHack %s features and prior.",
5924 buf);
5925 }
5926 return 1;
5927 }
5928
5929 /*
5930 * This is used by parse_config_line() in files.c
5931 *
5932 */
5933
5934 /* parse key:command */
5935 boolean
parsebindings(char * bindings)5936 parsebindings(char *bindings)
5937 {
5938 char *bind;
5939 uchar key;
5940 int i;
5941 boolean ret = FALSE;
5942
5943 /* break off first binding from the rest; parse the rest */
5944 if ((bind = index(bindings, ',')) != 0) {
5945 *bind++ = 0;
5946 ret |= parsebindings(bind);
5947 }
5948
5949 /* parse a single binding: first split around : */
5950 if (! (bind = index(bindings, ':')))
5951 return FALSE; /* it's not a binding */
5952 *bind++ = 0;
5953
5954 /* read the key to be bound */
5955 key = txt2key(bindings);
5956 if (!key) {
5957 config_error_add("Unknown key binding key '%s'", bindings);
5958 return FALSE;
5959 }
5960
5961 bind = trimspaces(bind);
5962
5963 /* is it a special key? */
5964 if (bind_specialkey(key, bind))
5965 return TRUE;
5966
5967 /* is it a menu command? */
5968 for (i = 0; default_menu_cmd_info[i].name; i++) {
5969 if (!strcmp(default_menu_cmd_info[i].name, bind)) {
5970 if (illegal_menu_cmd_key(key)) {
5971 config_error_add("Bad menu key %s:%s", visctrl(key), bind);
5972 return FALSE;
5973 } else
5974 add_menu_cmd_alias((char) key, default_menu_cmd_info[i].cmd);
5975 return TRUE;
5976 }
5977 }
5978
5979 /* extended command? */
5980 if (!bind_key(key, bind)) {
5981 config_error_add("Unknown key binding command '%s'", bind);
5982 return FALSE;
5983 }
5984 return TRUE;
5985 }
5986
5987 /*
5988 * Color support functions and data for "color"
5989 *
5990 * Used by: optfn_()
5991 *
5992 */
5993
5994 static const struct color_names {
5995 const char *name;
5996 int color;
5997 } colornames[] = {
5998 { "black", CLR_BLACK },
5999 { "red", CLR_RED },
6000 { "green", CLR_GREEN },
6001 { "brown", CLR_BROWN },
6002 { "blue", CLR_BLUE },
6003 { "magenta", CLR_MAGENTA },
6004 { "cyan", CLR_CYAN },
6005 { "gray", CLR_GRAY },
6006 { "orange", CLR_ORANGE },
6007 { "light green", CLR_BRIGHT_GREEN },
6008 { "yellow", CLR_YELLOW },
6009 { "light blue", CLR_BRIGHT_BLUE },
6010 { "light magenta", CLR_BRIGHT_MAGENTA },
6011 { "light cyan", CLR_BRIGHT_CYAN },
6012 { "white", CLR_WHITE },
6013 { "no color", NO_COLOR },
6014 { NULL, CLR_BLACK }, /* everything after this is an alias */
6015 { "transparent", NO_COLOR },
6016 { "purple", CLR_MAGENTA },
6017 { "light purple", CLR_BRIGHT_MAGENTA },
6018 { "bright purple", CLR_BRIGHT_MAGENTA },
6019 { "grey", CLR_GRAY },
6020 { "bright red", CLR_ORANGE },
6021 { "bright green", CLR_BRIGHT_GREEN },
6022 { "bright blue", CLR_BRIGHT_BLUE },
6023 { "bright magenta", CLR_BRIGHT_MAGENTA },
6024 { "bright cyan", CLR_BRIGHT_CYAN }
6025 };
6026
6027 static const struct attr_names {
6028 const char *name;
6029 int attr;
6030 } attrnames[] = {
6031 { "none", ATR_NONE },
6032 { "bold", ATR_BOLD },
6033 { "dim", ATR_DIM },
6034 { "underline", ATR_ULINE },
6035 { "blink", ATR_BLINK },
6036 { "inverse", ATR_INVERSE },
6037 { NULL, ATR_NONE }, /* everything after this is an alias */
6038 { "normal", ATR_NONE },
6039 { "uline", ATR_ULINE },
6040 { "reverse", ATR_INVERSE },
6041 };
6042
6043 const char *
clr2colorname(int clr)6044 clr2colorname(int clr)
6045 {
6046 int i;
6047
6048 for (i = 0; i < SIZE(colornames); i++)
6049 if (colornames[i].name && colornames[i].color == clr)
6050 return colornames[i].name;
6051 return (char *) 0;
6052 }
6053
6054 int
match_str2clr(char * str)6055 match_str2clr(char *str)
6056 {
6057 int i, c = CLR_MAX;
6058
6059 /* allow "lightblue", "light blue", and "light-blue" to match "light blue"
6060 (also junk like "_l i-gh_t---b l u e" but we won't worry about that);
6061 also copes with trailing space; caller has removed any leading space */
6062 for (i = 0; i < SIZE(colornames); i++)
6063 if (colornames[i].name
6064 && fuzzymatch(str, colornames[i].name, " -_", TRUE)) {
6065 c = colornames[i].color;
6066 break;
6067 }
6068 if (i == SIZE(colornames) && digit(*str))
6069 c = atoi(str);
6070
6071 if (c < 0 || c >= CLR_MAX) {
6072 config_error_add("Unknown color '%.60s'", str);
6073 c = CLR_MAX; /* "none of the above" */
6074 }
6075 return c;
6076 }
6077
6078 static const char *
attr2attrname(int attr)6079 attr2attrname(int attr)
6080 {
6081 int i;
6082
6083 for (i = 0; i < SIZE(attrnames); i++)
6084 if (attrnames[i].attr == attr)
6085 return attrnames[i].name;
6086 return (char *) 0;
6087 }
6088
6089 int
match_str2attr(const char * str,boolean complain)6090 match_str2attr(const char *str, boolean complain)
6091 {
6092 int i, a = -1;
6093
6094 for (i = 0; i < SIZE(attrnames); i++)
6095 if (attrnames[i].name
6096 && fuzzymatch(str, attrnames[i].name, " -_", TRUE)) {
6097 a = attrnames[i].attr;
6098 break;
6099 }
6100
6101 if (a == -1 && complain)
6102 config_error_add("Unknown text attribute '%.50s'", str);
6103
6104 return a;
6105 }
6106
6107 extern const char regex_id[]; /* from sys/share/<various>regex.{c,cpp} */
6108
6109 DISABLE_WARNING_FORMAT_NONLITERAL
6110
6111 /* True: temporarily replace menu color entries with a fake set of menu
6112 colors, { "light blue"=light_blue, "blue"=blue, "red"=red, &c }, that
6113 illustrates most colors for use when the pick-a-color menu is rendered;
6114 suppresses black and white because one of those will likely be invisible
6115 due to matching the background; False: restore user-specified colorings */
6116 static void
basic_menu_colors(boolean load_colors)6117 basic_menu_colors(boolean load_colors)
6118 {
6119 if (load_colors) {
6120 /* replace normal menu colors with a set specifically for colors */
6121 g.save_menucolors = iflags.use_menu_color;
6122 g.save_colorings = g.menu_colorings;
6123
6124 iflags.use_menu_color = TRUE;
6125 if (g.color_colorings) {
6126 /* use the alternate colorings which were set up previously */
6127 g.menu_colorings = g.color_colorings;
6128 } else {
6129 /* create the alternate colorings once */
6130 char cnm[QBUFSZ];
6131 int i, c;
6132 boolean pmatchregex = !strcmpi(regex_id, "pmatchregex");
6133 const char *patternfmt = pmatchregex ? "*%s" : "%s";
6134
6135 /* menu_colorings pointer has been saved; clear it in order
6136 to add the alternate entries as if from scratch */
6137 g.menu_colorings = (struct menucoloring *) 0;
6138
6139 /* this orders the patterns last-in/first-out; that means
6140 that the "light <foo>" variations come before the basic
6141 "<foo>" ones, which is exactly what we want */
6142 for (i = 0; i < SIZE(colornames); ++i) {
6143 if (!colornames[i].name) /* first alias entry has no name */
6144 break;
6145 c = colornames[i].color;
6146 if (c == CLR_BLACK || c == CLR_WHITE || c == NO_COLOR)
6147 continue; /* skip these */
6148 Sprintf(cnm, patternfmt, colornames[i].name);
6149 add_menu_coloring_parsed(cnm, c, ATR_NONE);
6150 }
6151
6152 /* right now, menu_colorings contains the alternate color list;
6153 remember that list for future pick-a-color instances and
6154 also keep it as is for this instance */
6155 g.color_colorings = g.menu_colorings;
6156 }
6157 } else {
6158 /* restore normal user-specified menu colors */
6159 iflags.use_menu_color = g.save_menucolors;
6160 g.menu_colorings = g.save_colorings;
6161 }
6162 }
6163
6164 RESTORE_WARNING_FORMAT_NONLITERAL
6165
6166 int
query_color(const char * prompt)6167 query_color(const char *prompt)
6168 {
6169 winid tmpwin;
6170 anything any;
6171 int i, pick_cnt;
6172 menu_item *picks = (menu_item *) 0;
6173
6174 /* replace user patterns with color name ones and force 'menucolors' On */
6175 basic_menu_colors(TRUE);
6176
6177 tmpwin = create_nhwindow(NHW_MENU);
6178 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
6179 any = cg.zeroany;
6180 for (i = 0; i < SIZE(colornames); i++) {
6181 if (!colornames[i].name)
6182 break;
6183 any.a_int = i + 1;
6184 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
6185 ATR_NONE, colornames[i].name,
6186 (colornames[i].color == NO_COLOR) ? MENU_ITEMFLAGS_SELECTED
6187 : MENU_ITEMFLAGS_NONE);
6188 }
6189 end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick a color");
6190 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
6191 destroy_nhwindow(tmpwin);
6192
6193 /* remove temporary color name patterns and restore user-specified ones;
6194 reset 'menucolors' option to its previous value */
6195 basic_menu_colors(FALSE);
6196
6197 if (pick_cnt > 0) {
6198 i = colornames[picks[0].item.a_int - 1].color;
6199 /* pick_cnt==2: explicitly picked something other than the
6200 preselected entry */
6201 if (pick_cnt == 2 && i == NO_COLOR)
6202 i = colornames[picks[1].item.a_int - 1].color;
6203 free((genericptr_t) picks);
6204 return i;
6205 } else if (pick_cnt == 0) {
6206 /* pick_cnt==0: explicitly picking preselected entry toggled it off */
6207 return NO_COLOR;
6208 }
6209 return -1;
6210 }
6211
6212 /* ask about highlighting attribute; for menu headers and menu
6213 coloring patterns, only one attribute at a time is allowed;
6214 for status highlighting, multiple attributes are allowed [overkill;
6215 life would be much simpler if that were restricted to one also...] */
6216 int
query_attr(const char * prompt)6217 query_attr(const char *prompt)
6218 {
6219 winid tmpwin;
6220 anything any;
6221 int i, pick_cnt;
6222 menu_item *picks = (menu_item *) 0;
6223 boolean allow_many = (prompt && !strncmpi(prompt, "Choose", 6));
6224 int default_attr = ATR_NONE;
6225
6226 if (prompt && strstri(prompt, "menu headings"))
6227 default_attr = iflags.menu_headings;
6228 tmpwin = create_nhwindow(NHW_MENU);
6229 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
6230 any = cg.zeroany;
6231 for (i = 0; i < SIZE(attrnames); i++) {
6232 if (!attrnames[i].name)
6233 break;
6234 any.a_int = i + 1;
6235 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
6236 attrnames[i].attr, attrnames[i].name,
6237 (attrnames[i].attr == default_attr) ? MENU_ITEMFLAGS_SELECTED
6238 : MENU_ITEMFLAGS_NONE);
6239 }
6240 end_menu(tmpwin, (prompt && *prompt) ? prompt : "Pick an attribute");
6241 pick_cnt = select_menu(tmpwin, allow_many ? PICK_ANY : PICK_ONE, &picks);
6242 destroy_nhwindow(tmpwin);
6243 if (pick_cnt > 0) {
6244 int j, k = 0;
6245
6246 if (allow_many) {
6247 /* PICK_ANY, with one preselected entry (ATR_NONE) which
6248 should be excluded if any other choices were picked */
6249 for (i = 0; i < pick_cnt; ++i) {
6250 j = picks[i].item.a_int - 1;
6251 if (attrnames[j].attr != ATR_NONE || pick_cnt == 1) {
6252 switch (attrnames[j].attr) {
6253 case ATR_DIM:
6254 k |= HL_DIM;
6255 break;
6256 case ATR_BLINK:
6257 k |= HL_BLINK;
6258 break;
6259 case ATR_ULINE:
6260 k |= HL_ULINE;
6261 break;
6262 case ATR_INVERSE:
6263 k |= HL_INVERSE;
6264 break;
6265 case ATR_BOLD:
6266 k |= HL_BOLD;
6267 break;
6268 case ATR_NONE:
6269 k = HL_NONE;
6270 break;
6271 }
6272 }
6273 }
6274 } else {
6275 /* PICK_ONE, but might get 0 or 2 due to preselected entry */
6276 j = picks[0].item.a_int - 1;
6277 /* pick_cnt==2: explicitly picked something other than the
6278 preselected entry */
6279 if (pick_cnt == 2 && attrnames[j].attr == default_attr)
6280 j = picks[1].item.a_int - 1;
6281 k = attrnames[j].attr;
6282 }
6283 free((genericptr_t) picks);
6284 return k;
6285 } else if (pick_cnt == 0 && !allow_many) {
6286 /* PICK_ONE, preselected entry explicitly chosen */
6287 return default_attr;
6288 }
6289 /* either ESC to explicitly cancel (pick_cnt==-1) or
6290 PICK_ANY with preselected entry toggled off and nothing chosen */
6291 return -1;
6292 }
6293
6294 static const struct {
6295 const char *name;
6296 xchar msgtyp;
6297 const char *descr;
6298 } msgtype_names[] = {
6299 { "show", MSGTYP_NORMAL, "Show message normally" },
6300 { "hide", MSGTYP_NOSHOW, "Hide message" },
6301 { "noshow", MSGTYP_NOSHOW, NULL },
6302 { "stop", MSGTYP_STOP, "Prompt for more after the message" },
6303 { "more", MSGTYP_STOP, NULL },
6304 /* 'alert' will fallback to 'stop' behaviour if windowport does not support it */
6305 { "alert", MSGTYP_ALERT, "Force acknowlegement with <TAB>" },
6306 { "norep", MSGTYP_NOREP, "Do not repeat the message" }
6307 };
6308
6309 static const char *
msgtype2name(int typ)6310 msgtype2name(int typ)
6311 {
6312 int i;
6313
6314 for (i = 0; i < SIZE(msgtype_names); i++)
6315 if (msgtype_names[i].descr && msgtype_names[i].msgtyp == typ)
6316 return msgtype_names[i].name;
6317 return (char *) 0;
6318 }
6319
6320 static int
query_msgtype(void)6321 query_msgtype(void)
6322 {
6323 winid tmpwin;
6324 anything any;
6325 int i, pick_cnt;
6326 menu_item *picks = (menu_item *) 0;
6327
6328 tmpwin = create_nhwindow(NHW_MENU);
6329 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
6330 any = cg.zeroany;
6331 for (i = 0; i < SIZE(msgtype_names); i++)
6332 if (msgtype_names[i].descr) {
6333 any.a_int = msgtype_names[i].msgtyp + 1;
6334 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
6335 ATR_NONE, msgtype_names[i].descr, MENU_ITEMFLAGS_NONE);
6336 }
6337 end_menu(tmpwin, "How to show the message");
6338 pick_cnt = select_menu(tmpwin, PICK_ONE, &picks);
6339 destroy_nhwindow(tmpwin);
6340 if (pick_cnt > 0) {
6341 i = picks->item.a_int - 1;
6342 free((genericptr_t) picks);
6343 return i;
6344 }
6345 return -1;
6346 }
6347
6348 static boolean
msgtype_add(int typ,char * pattern)6349 msgtype_add(int typ, char *pattern)
6350 {
6351 static const char *re_error = "MSGTYPE regex error";
6352 struct plinemsg_type *tmp = (struct plinemsg_type *) alloc(sizeof *tmp);
6353
6354 tmp->msgtype = typ;
6355 tmp->regex = regex_init();
6356 /* test_regex_pattern() has already validated this regexp but parsing
6357 it again could conceivably run out of memory */
6358 if (!regex_compile(pattern, tmp->regex)) {
6359 const char *re_error_desc = regex_error_desc(tmp->regex);
6360
6361 /* free first in case reason for failure was insufficient memory */
6362 regex_free(tmp->regex);
6363 free((genericptr_t) tmp);
6364 config_error_add("%s: %s", re_error, re_error_desc);
6365 return FALSE;
6366 }
6367 tmp->pattern = dupstr(pattern);
6368 tmp->next = g.plinemsg_types;
6369 g.plinemsg_types = tmp;
6370 return TRUE;
6371 }
6372
6373 void
msgtype_free(void)6374 msgtype_free(void)
6375 {
6376 struct plinemsg_type *tmp, *tmp2 = 0;
6377
6378 for (tmp = g.plinemsg_types; tmp; tmp = tmp2) {
6379 tmp2 = tmp->next;
6380 free((genericptr_t) tmp->pattern);
6381 regex_free(tmp->regex);
6382 free((genericptr_t) tmp);
6383 }
6384 g.plinemsg_types = (struct plinemsg_type *) 0;
6385 }
6386
6387 static void
free_one_msgtype(int idx)6388 free_one_msgtype(int idx) /* 0 .. */
6389 {
6390 struct plinemsg_type *tmp = g.plinemsg_types;
6391 struct plinemsg_type *prev = NULL;
6392
6393 while (tmp) {
6394 if (idx == 0) {
6395 struct plinemsg_type *next = tmp->next;
6396
6397 regex_free(tmp->regex);
6398 free((genericptr_t) tmp->pattern);
6399 free((genericptr_t) tmp);
6400 if (prev)
6401 prev->next = next;
6402 else
6403 g.plinemsg_types = next;
6404 return;
6405 }
6406 idx--;
6407 prev = tmp;
6408 tmp = tmp->next;
6409 }
6410 }
6411
6412 int
msgtype_type(const char * msg,boolean norepeat)6413 msgtype_type(const char *msg,
6414 boolean norepeat) /* called from Norep(via pline) */
6415 {
6416 struct plinemsg_type *tmp = g.plinemsg_types;
6417
6418 while (tmp) {
6419 /* we don't exclude entries with negative msgtype values
6420 because then the msg might end up matching a later pattern */
6421 if (regex_match(msg, tmp->regex))
6422 return tmp->msgtype;
6423 tmp = tmp->next;
6424 }
6425 return norepeat ? MSGTYP_NOREP : MSGTYP_NORMAL;
6426 }
6427
6428 /* negate one or more types of messages so that their type handling will
6429 be disabled or re-enabled; MSGTYPE_NORMAL (value 0) is not affected */
6430 void
hide_unhide_msgtypes(boolean hide,int hide_mask)6431 hide_unhide_msgtypes(boolean hide, int hide_mask)
6432 {
6433 struct plinemsg_type *tmp;
6434 int mt;
6435
6436 /* negative msgtype value won't be recognized by pline, so does nothing */
6437 for (tmp = g.plinemsg_types; tmp; tmp = tmp->next) {
6438 mt = tmp->msgtype;
6439 if (!hide)
6440 mt = -mt; /* unhide: negate negative, yielding positive */
6441 if (mt > 0 && ((1 << mt) & hide_mask))
6442 tmp->msgtype = -tmp->msgtype;
6443 }
6444 }
6445
6446 static int
msgtype_count(void)6447 msgtype_count(void)
6448 {
6449 int c = 0;
6450 struct plinemsg_type *tmp = g.plinemsg_types;
6451
6452 while (tmp) {
6453 c++;
6454 tmp = tmp->next;
6455 }
6456 return c;
6457 }
6458
6459 boolean
msgtype_parse_add(char * str)6460 msgtype_parse_add(char *str)
6461 {
6462 char pattern[256];
6463 char msgtype[11];
6464
6465 if (sscanf(str, "%10s \"%255[^\"]\"", msgtype, pattern) == 2) {
6466 int typ = -1;
6467 int i;
6468
6469 for (i = 0; i < SIZE(msgtype_names); i++)
6470 if (!strncmpi(msgtype_names[i].name, msgtype, strlen(msgtype))) {
6471 typ = msgtype_names[i].msgtyp;
6472 break;
6473 }
6474 if (typ != -1)
6475 return msgtype_add(typ, pattern);
6476 else
6477 config_error_add("Unknown message type '%s'", msgtype);
6478 } else {
6479 config_error_add("Malformed MSGTYPE");
6480 }
6481 return FALSE;
6482 }
6483
6484 /* parse 'str' as a regular expression to check whether it's valid;
6485 compiled regexp gets thrown away regardless of the outcome */
6486 static boolean
test_regex_pattern(const char * str,const char * errmsg)6487 test_regex_pattern(const char *str, const char *errmsg)
6488 {
6489 static const char def_errmsg[] = "NHregex error";
6490 struct nhregex *match;
6491 const char *re_error_desc;
6492 boolean retval;
6493
6494 if (!str)
6495 return FALSE;
6496 if (!errmsg)
6497 errmsg = def_errmsg;
6498
6499 match = regex_init();
6500 if (!match) {
6501 config_error_add("%s", errmsg);
6502 return FALSE;
6503 }
6504
6505 retval = regex_compile(str, match);
6506 /* get potential error message before freeing regexp and free regexp
6507 before issuing message in case the error is "ran out of memory"
6508 since message delivery might need to allocate some memory */
6509 re_error_desc = !retval ? regex_error_desc(match) : 0;
6510 /* discard regexp; caller will re-parse it after validating other stuff */
6511 regex_free(match);
6512 /* if returning failure, tell player */
6513 if (!retval)
6514 config_error_add("%s: %s", errmsg, re_error_desc);
6515
6516 return retval;
6517 }
6518
6519 static boolean
add_menu_coloring_parsed(const char * str,int c,int a)6520 add_menu_coloring_parsed(const char *str, int c, int a)
6521 {
6522 static const char re_error[] = "Menucolor regex error";
6523 struct menucoloring *tmp;
6524
6525 if (!str)
6526 return FALSE;
6527 tmp = (struct menucoloring *) alloc(sizeof *tmp);
6528 tmp->match = regex_init();
6529 /* test_regex_pattern() has already validated this regexp but parsing
6530 it again could conceivably run out of memory */
6531 if (!regex_compile(str, tmp->match)) {
6532 const char *re_error_desc = regex_error_desc(tmp->match);
6533
6534 /* free first in case reason for regcomp failure was out-of-memory */
6535 regex_free(tmp->match);
6536 free((genericptr_t) tmp);
6537 config_error_add("%s: %s", re_error, re_error_desc);
6538 return FALSE;
6539 }
6540 tmp->next = g.menu_colorings;
6541 tmp->origstr = dupstr(str);
6542 tmp->color = c;
6543 tmp->attr = a;
6544 g.menu_colorings = tmp;
6545 return TRUE;
6546 }
6547
6548 /* parse '"regex_string"=color&attr' and add it to menucoloring */
6549 boolean
add_menu_coloring(char * tmpstr)6550 add_menu_coloring(char *tmpstr) /* never Null but could be empty */
6551 {
6552 int c = NO_COLOR, a = ATR_NONE;
6553 char *tmps, *cs, *amp;
6554 char str[BUFSZ];
6555
6556 (void) strncpy(str, tmpstr, sizeof str - 1);
6557 str[sizeof str - 1] = '\0';
6558
6559 if ((cs = index(str, '=')) == 0) {
6560 config_error_add("Malformed MENUCOLOR");
6561 return FALSE;
6562 }
6563
6564 tmps = cs + 1; /* advance past '=' */
6565 mungspaces(tmps);
6566 if ((amp = index(tmps, '&')) != 0)
6567 *amp = '\0';
6568
6569 c = match_str2clr(tmps);
6570 if (c >= CLR_MAX)
6571 return FALSE;
6572
6573 if (amp) {
6574 tmps = amp + 1; /* advance past '&' */
6575 a = match_str2attr(tmps, TRUE);
6576 if (a == -1)
6577 return FALSE;
6578 }
6579
6580 /* the regexp portion here has not been condensed by mungspaces() */
6581 *cs = '\0';
6582 tmps = str;
6583 if (*tmps == '"' || *tmps == '\'') {
6584 cs--;
6585 while (isspace((uchar) *cs))
6586 cs--;
6587 if (*cs == *tmps) {
6588 *cs = '\0';
6589 tmps++;
6590 }
6591 }
6592 return add_menu_coloring_parsed(tmps, c, a);
6593 }
6594
6595 boolean
get_menu_coloring(const char * str,int * color,int * attr)6596 get_menu_coloring(const char *str, int *color, int *attr)
6597 {
6598 struct menucoloring *tmpmc;
6599
6600 if (iflags.use_menu_color)
6601 for (tmpmc = g.menu_colorings; tmpmc; tmpmc = tmpmc->next)
6602 if (regex_match(str, tmpmc->match)) {
6603 *color = tmpmc->color;
6604 *attr = tmpmc->attr;
6605 return TRUE;
6606 }
6607 return FALSE;
6608 }
6609
6610 /* release all menu color patterns */
6611 void
free_menu_coloring(void)6612 free_menu_coloring(void)
6613 {
6614 /* either menu_colorings or color_colorings or both might need to
6615 be freed or already be Null; do-loop will iterate at most twice */
6616 do {
6617 struct menucoloring *tmp, *tmp2;
6618
6619 for (tmp = g.menu_colorings; tmp; tmp = tmp2) {
6620 tmp2 = tmp->next;
6621 regex_free(tmp->match);
6622 free((genericptr_t) tmp->origstr);
6623 free((genericptr_t) tmp);
6624 }
6625 g.menu_colorings = g.color_colorings;
6626 g.color_colorings = (struct menucoloring *) 0;
6627 } while (g.menu_colorings);
6628 }
6629
6630 /* release a specific menu color pattern; not used for color_colorings */
6631 static void
free_one_menu_coloring(int idx)6632 free_one_menu_coloring(int idx) /* 0 .. */
6633 {
6634 struct menucoloring *tmp = g.menu_colorings;
6635 struct menucoloring *prev = NULL;
6636
6637 while (tmp) {
6638 if (idx == 0) {
6639 struct menucoloring *next = tmp->next;
6640
6641 regex_free(tmp->match);
6642 free((genericptr_t) tmp->origstr);
6643 free((genericptr_t) tmp);
6644 if (prev)
6645 prev->next = next;
6646 else
6647 g.menu_colorings = next;
6648 return;
6649 }
6650 idx--;
6651 prev = tmp;
6652 tmp = tmp->next;
6653 }
6654 }
6655
6656 static int
count_menucolors(void)6657 count_menucolors(void)
6658 {
6659 struct menucoloring *tmp;
6660 int count = 0;
6661
6662 for (tmp = g.menu_colorings; tmp; tmp = tmp->next)
6663 count++;
6664 return count;
6665 }
6666
6667 /* Add a monster color specification.
6668 * The string is expected to be of the format "monster:color", either direct
6669 * from the config (we need to check syntax) or from the interactive option. */
6670 boolean
add_monstercolor(char * str)6671 add_monstercolor(char *str)
6672 {
6673 if (!str) {
6674 impossible("add_monstercolor with null str");
6675 return FALSE;
6676 }
6677 int mndx = NON_PM, color = MONSTERCOLOR_DEFAULT;
6678 char *mname = str;
6679 char *colon = index(str, ':');
6680 char *colorstr;
6681 if (colon == (char *) 0) {
6682 config_error_add("Malformed MONSTERCOLOR");
6683 return FALSE;
6684 }
6685 colorstr = colon + 1; /* color starts at next character */
6686 *colon = '\0'; /* terminate monster string */
6687
6688 mndx = name_to_mon(mname, (int *) 0); /* accept any gender */
6689 if (mndx == NON_PM) {
6690 config_error_add("Unrecognized monster species \"%s\"", mname);
6691 return FALSE;
6692 }
6693
6694 color = match_str2clr(colorstr); /* if invalid color, prints error and
6695 returns CLR_MAX */
6696 if (color == CLR_MAX) {
6697 return FALSE;
6698 }
6699
6700 /* now actually set the monster color - note that if there were ever a
6701 * monster whose identity was deliberately fudged among multiple species (as
6702 * happens with Riders in a couple variants), this would be a way to work
6703 * around that, and a countermeasure would need to be added here. */
6704 g.monstercolors[mndx] = color;
6705 return TRUE;
6706 }
6707
6708 static boolean
parse_role_opts(int optidx,boolean negated,const char * fullname,char * opts,char ** opp)6709 parse_role_opts(int optidx, boolean negated, const char *fullname,
6710 char *opts, char **opp)
6711 {
6712 char *op = *opp;
6713
6714 if (negated) {
6715 bad_negation(fullname, FALSE);
6716 } else if ((op = string_for_env_opt(fullname, opts, FALSE))
6717 != empty_optstr) {
6718 boolean val_negated = FALSE;
6719
6720 while ((*op == '!') || !strncmpi(op, "no", 2)) {
6721 if (*op == '!')
6722 op++;
6723 else
6724 op += 2;
6725 val_negated = !val_negated;
6726 }
6727 if (val_negated) {
6728 if (!setrolefilter(op)) {
6729 config_error_add("Unknown negated parameter '%s'", op);
6730 return FALSE;
6731 }
6732 } else {
6733 if (duplicate && !allopt[optidx].dupeok)
6734 complain_about_duplicate(optidx);
6735 *opp = op;
6736 return TRUE;
6737 }
6738 }
6739 return FALSE;
6740 }
6741
6742 /* Check if character c is illegal as a menu command key */
6743 boolean
illegal_menu_cmd_key(uchar c)6744 illegal_menu_cmd_key(uchar c)
6745 {
6746 if (c == 0 || c == '\r' || c == '\n' || c == '\033' || c == ' '
6747 || digit((char) c) || (letter((char) c) && c != '@')) {
6748 config_error_add("Reserved menu command key '%s'", visctrl((char) c));
6749 return TRUE;
6750 } else { /* reject default object class symbols */
6751 int j;
6752
6753 for (j = 1; j < MAXOCLASSES; j++)
6754 if (c == (uchar) def_oc_syms[j].sym) {
6755 config_error_add("Menu command key '%s' is an object class",
6756 visctrl((char) c));
6757 return TRUE;
6758 }
6759 }
6760 return FALSE;
6761 }
6762
6763
6764 /*
6765 * Convert the given string of object classes to a string of default object
6766 * symbols.
6767 */
6768 void
oc_to_str(char * src,char * dest)6769 oc_to_str(char *src, char *dest)
6770 {
6771 int i;
6772
6773 while ((i = (int) *src++) != 0) {
6774 if (i < 0 || i >= MAXOCLASSES)
6775 impossible("oc_to_str: illegal object class %d", i);
6776 else
6777 *dest++ = def_oc_syms[i].sym;
6778 }
6779 *dest = '\0';
6780 }
6781
6782 /*
6783 * Add the given mapping to the menu command map list. Always keep the
6784 * maps valid C strings.
6785 */
6786 void
add_menu_cmd_alias(char from_ch,char to_ch)6787 add_menu_cmd_alias(char from_ch, char to_ch)
6788 {
6789 if (g.n_menu_mapped >= MAX_MENU_MAPPED_CMDS) {
6790 pline("out of menu map space.");
6791 } else {
6792 g.mapped_menu_cmds[g.n_menu_mapped] = from_ch;
6793 g.mapped_menu_op[g.n_menu_mapped] = to_ch;
6794 g.n_menu_mapped++;
6795 g.mapped_menu_cmds[g.n_menu_mapped] = '\0';
6796 g.mapped_menu_op[g.n_menu_mapped] = '\0';
6797 }
6798 }
6799
6800 char
get_menu_cmd_key(char ch)6801 get_menu_cmd_key(char ch)
6802 {
6803 char *found = index(g.mapped_menu_op, ch);
6804
6805 if (found) {
6806 int idx = (int) (found - g.mapped_menu_op);
6807
6808 ch = g.mapped_menu_cmds[idx];
6809 }
6810 return ch;
6811 }
6812
6813 /*
6814 * Map the given character to its corresponding menu command. If it
6815 * doesn't match anything, just return the original.
6816 */
6817 char
map_menu_cmd(char ch)6818 map_menu_cmd(char ch)
6819 {
6820 char *found = index(g.mapped_menu_cmds, ch);
6821
6822 if (found) {
6823 int idx = (int) (found - g.mapped_menu_cmds);
6824
6825 ch = g.mapped_menu_op[idx];
6826 }
6827 return ch;
6828 }
6829
6830 /* get keystrokes that are used for menu scrolling operations which apply;
6831 printable: for use in a prompt, non-printable: for yn_function() choices */
6832 char *
collect_menu_keys(char * outbuf,unsigned scrollmask,boolean printable)6833 collect_menu_keys(
6834 char *outbuf, /* at least big enough for 6 "M-^X" sequences +'\0'*/
6835 unsigned scrollmask, /* 1: backwards, "^<"; 2: forwards, ">|";
6836 * 4: left, "{"; 8: right, "}"; */
6837 boolean printable) /* False: output is string of raw characters,
6838 * True: output is a string of visctrl() sequences;
6839 * matters iff user has mapped any menu scrolling
6840 * commands to control or meta characters */
6841 {
6842 struct menuscrollinfo {
6843 char cmdkey;
6844 uchar maskindx;
6845 };
6846 static const struct menuscrollinfo scroll_keys[] = {
6847 { MENU_FIRST_PAGE, 1 },
6848 { MENU_PREVIOUS_PAGE, 1 },
6849 { MENU_NEXT_PAGE, 2 },
6850 { MENU_LAST_PAGE, 2 },
6851 { MENU_SHIFT_LEFT, 4 },
6852 { MENU_SHIFT_RIGHT, 8 },
6853 };
6854 int i;
6855
6856 outbuf[0] = '\0';
6857 for (i = 0; i < SIZE(scroll_keys); ++i) {
6858 if (scrollmask & scroll_keys[i].maskindx) {
6859 char c = get_menu_cmd_key(scroll_keys[i].cmdkey);
6860
6861 if (printable)
6862 Strcat(outbuf, visctrl(c));
6863 else
6864 (void) strkitten(outbuf, c);
6865 }
6866 }
6867 return outbuf;
6868 }
6869
6870 /* Returns the fid of the fruit type; if that type already exists, it
6871 * returns the fid of that one; if it does not exist, it adds a new fruit
6872 * type to the chain and returns the new one.
6873 * If replace_fruit is sent in, replace the fruit in the chain rather than
6874 * adding a new entry--for user specified fruits only.
6875 */
6876 int
fruitadd(char * str,struct fruit * replace_fruit)6877 fruitadd(char *str, struct fruit *replace_fruit)
6878 {
6879 register int i;
6880 register struct fruit *f;
6881 int highest_fruit_id = 0, globpfx;
6882 char buf[PL_FSIZ], altname[PL_FSIZ];
6883 boolean user_specified = (str == g.pl_fruit);
6884 /* if not user-specified, then it's a fruit name for a fruit on
6885 * a bones level or from orctown raider's loot...
6886 */
6887
6888 /* Note: every fruit has an id (kept in obj->spe) of at least 1;
6889 * 0 is an error.
6890 */
6891 if (user_specified) {
6892 boolean found = FALSE, numeric = FALSE;
6893
6894 /* force fruit to be singular; this handling is not
6895 needed--or wanted--for fruits from bones because
6896 they already received it in their original game;
6897 str==pl_fruit but makesingular() creates a copy
6898 so we need to copy that back into pl_fruit */
6899 nmcpy(g.pl_fruit, makesingular(str), PL_FSIZ);
6900 /* (assertion doesn't matter; we use 'g.pl_fruit' from here on out) */
6901 /* assert( str == g.pl_fruit ); */
6902
6903 /* disallow naming after other foods (since it'd be impossible
6904 * to tell the difference); globs might have a size prefix which
6905 * needs to be skipped in order to match the object type name
6906 */
6907 globpfx = (!strncmp(g.pl_fruit, "small ", 6)
6908 || !strncmp(g.pl_fruit, "large ", 6)) ? 6
6909 : (!strncmp(g.pl_fruit, "very large ", 11)) ? 11
6910 : 0;
6911 for (i = g.bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) {
6912 if (!strcmp(OBJ_NAME(objects[i]), g.pl_fruit)
6913 || (globpfx > 0
6914 && !strcmp(OBJ_NAME(objects[i]), &g.pl_fruit[globpfx]))) {
6915 found = TRUE;
6916 break;
6917 }
6918 }
6919 if (!found) {
6920 char *c;
6921
6922 for (c = g.pl_fruit; *c >= '0' && *c <= '9'; c++)
6923 continue;
6924 if (!*c || isspace((uchar) *c))
6925 numeric = TRUE;
6926 }
6927 if (found || numeric
6928 /* these checks for applying food attributes to actual items
6929 are case sensitive; "glob of foo" is caught by 'found'
6930 if 'foo' is a valid glob; when not valid, allow it as-is */
6931 || !strncmp(g.pl_fruit, "cursed ", 7)
6932 || !strncmp(g.pl_fruit, "uncursed ", 9)
6933 || !strncmp(g.pl_fruit, "blessed ", 8)
6934 || !strncmp(g.pl_fruit, "partly eaten ", 13)
6935 || (!strncmp(g.pl_fruit, "tin of ", 7)
6936 && (!strcmp(g.pl_fruit + 7, "spinach")
6937 || name_to_mon(g.pl_fruit + 7, (int *) 0) >= LOW_PM))
6938 || !strcmp(g.pl_fruit, "empty tin")
6939 || (!strcmp(g.pl_fruit, "glob")
6940 || (globpfx > 0 && !strcmp("glob", &g.pl_fruit[globpfx])))
6941 || ((str_end_is(g.pl_fruit, " corpse")
6942 || str_end_is(g.pl_fruit, " egg"))
6943 && name_to_mon(g.pl_fruit, (int *) 0) >= LOW_PM)) {
6944 Strcpy(buf, g.pl_fruit);
6945 Strcpy(g.pl_fruit, "candied ");
6946 nmcpy(g.pl_fruit + 8, buf, PL_FSIZ - 8);
6947 }
6948 *altname = '\0';
6949 /* This flag indicates that a fruit has been made since the
6950 * last time the user set the fruit. If it hasn't, we can
6951 * safely overwrite the current fruit, preventing the user from
6952 * setting many fruits in a row and overflowing.
6953 * Possible expansion: check for specific fruit IDs, not for
6954 * any fruit.
6955 */
6956 flags.made_fruit = FALSE;
6957 if (replace_fruit) {
6958 /* replace_fruit is already part of the fruit chain;
6959 update it in place rather than looking it up again */
6960 f = replace_fruit;
6961 copynchars(f->fname, g.pl_fruit, PL_FSIZ - 1);
6962 goto nonew;
6963 }
6964 } else {
6965 /* not user_supplied, so assumed to be from bones (or orc gang) */
6966 copynchars(altname, str, PL_FSIZ - 1);
6967 sanitize_name(altname);
6968 flags.made_fruit = TRUE; /* for safety. Any fruit name added from a
6969 * bones level should exist anyway. */
6970 }
6971 f = fruit_from_name(*altname ? altname : str, FALSE, &highest_fruit_id);
6972 if (f)
6973 goto nonew;
6974
6975 /* Maximum number of named fruits is 127, even if obj->spe can
6976 handle bigger values. If adding another fruit would overflow,
6977 use a random fruit instead... we've got a lot to choose from.
6978 current_fruit remains as is. */
6979 if (highest_fruit_id >= 127)
6980 return rnd(127);
6981
6982 f = newfruit();
6983 (void) memset((genericptr_t) f, 0, sizeof (struct fruit));
6984 copynchars(f->fname, *altname ? altname : str, PL_FSIZ - 1);
6985 f->fid = ++highest_fruit_id;
6986 /* we used to go out of our way to add it at the end of the list,
6987 but the order is arbitrary so use simpler insertion at start */
6988 f->nextf = g.ffruit;
6989 g.ffruit = f;
6990 nonew:
6991 if (user_specified)
6992 g.context.current_fruit = f->fid;
6993 return f->fid;
6994 }
6995
6996 /*
6997 **********************************
6998 *
6999 * Option-setting, menus,
7000 * displaying option values
7001 *
7002 **********************************
7003 */
7004
7005
7006 #if defined(MICRO) || defined(MAC) || defined(WIN32)
7007 #define OPTIONS_HEADING "OPTIONS"
7008 #else
7009 #define OPTIONS_HEADING "NETHACKOPTIONS"
7010 #endif
7011
7012 static char fmtstr_doset[] = "%s%-15s [%s] ";
7013 static char fmtstr_doset_tab[] = "%s\t[%s]";
7014 static char n_currently_set[] = "(%d currently set)";
7015
7016 DISABLE_WARNING_FORMAT_NONLITERAL /* RESTORE is after show_menucontrols() */
7017
7018 static int
optfn_o_autopickup_exceptions(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)7019 optfn_o_autopickup_exceptions(int optidx UNUSED, int req, boolean negated UNUSED,
7020 char *opts, char *op UNUSED)
7021 {
7022 if (req == do_init) {
7023 return optn_ok;
7024 }
7025 if (req == do_set) {
7026 }
7027 if (req == get_val) {
7028 if (!opts)
7029 return optn_err;
7030 Sprintf(opts, n_currently_set, count_apes());
7031 return optn_ok;
7032 }
7033 if (req == do_handler) {
7034 return handler_autopickup_exception();
7035 }
7036 return optn_ok;
7037 }
7038
7039 static int
optfn_o_menu_colors(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)7040 optfn_o_menu_colors(int optidx UNUSED, int req, boolean negated UNUSED,
7041 char *opts, char *op UNUSED)
7042 {
7043 if (req == do_init) {
7044 return optn_ok;
7045 }
7046 if (req == do_set) {
7047 }
7048 if (req == get_val) {
7049 if (!opts)
7050 return optn_err;
7051 Sprintf(opts, n_currently_set, count_menucolors());
7052 return optn_ok;
7053 }
7054 if (req == do_handler) {
7055 return handler_menu_colors();
7056 }
7057 return optn_ok;
7058 }
7059
7060 static int
optfn_o_message_types(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)7061 optfn_o_message_types(int optidx UNUSED, int req, boolean negated UNUSED,
7062 char *opts, char *op UNUSED)
7063 {
7064 if (req == do_init) {
7065 return optn_ok;
7066 }
7067 if (req == do_set) {
7068 }
7069 if (req == get_val) {
7070 if (!opts)
7071 return optn_err;
7072 Sprintf(opts, n_currently_set, msgtype_count());
7073 return optn_ok;
7074 }
7075 if (req == do_handler) {
7076 return handler_msgtype();
7077 }
7078 return optn_ok;
7079 }
7080
7081 static int
optfn_o_status_cond(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)7082 optfn_o_status_cond(int optidx UNUSED, int req, boolean negated UNUSED,
7083 char *opts, char *op UNUSED)
7084 {
7085 if (req == do_init) {
7086 return optn_ok;
7087 }
7088 if (req == do_set) {
7089 }
7090 if (req == get_val) {
7091 if (!opts)
7092 return optn_err;
7093 Sprintf(opts, n_currently_set, count_cond());
7094 return optn_ok;
7095 }
7096 if (req == do_handler) {
7097 cond_menu();
7098 return optn_ok;
7099 }
7100 return optn_ok;
7101 }
7102
7103 static int
optfn_o_monstercolor(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)7104 optfn_o_monstercolor(int optidx UNUSED, int req, boolean negated UNUSED,
7105 char *opts, char *op UNUSED)
7106 {
7107 if (req == do_init) {
7108 return optn_ok;
7109 }
7110 if (req == do_set) {
7111 }
7112 if (req == get_val) {
7113 if (!opts)
7114 return optn_err;
7115 Sprintf(opts, n_currently_set, count_monstercolors());
7116 return optn_ok;
7117 }
7118 if (req == do_handler) {
7119 return handler_monstercolor();
7120 }
7121 return optn_ok;
7122 }
7123
7124 #ifdef STATUS_HILITES
7125 static int
optfn_o_status_hilites(int optidx UNUSED,int req,boolean negated UNUSED,char * opts,char * op UNUSED)7126 optfn_o_status_hilites(int optidx UNUSED, int req, boolean negated UNUSED,
7127 char *opts, char *op UNUSED)
7128 {
7129 if (req == do_init) {
7130 return optn_ok;
7131 }
7132 if (req == do_set) {
7133 }
7134 if (req == get_val) {
7135 if (!opts)
7136 return optn_err;
7137 Sprintf(opts, n_currently_set, count_status_hilites());
7138 return optn_ok;
7139 }
7140 if (req == do_handler) {
7141 if (!status_hilite_menu()) {
7142 return optn_err; /*pline("Bad status hilite(s) specified.");*/
7143 } else {
7144 if (wc2_supported("hilite_status"))
7145 preference_update("hilite_status");
7146 }
7147 return optn_ok;
7148 }
7149 return optn_ok;
7150 }
7151 #endif /*STATUS_HILITES*/
7152
7153 /* Get string value of configuration option.
7154 * Currently handles only boolean and compound options.
7155 */
7156 char *
get_option_value(const char * optname)7157 get_option_value(const char *optname)
7158 {
7159 static char retbuf[BUFSZ];
7160 boolean *bool_p;
7161 int i;
7162
7163 for (i = 0; allopt[i].name != 0; i++)
7164 if (!strcmp(optname, allopt[i].name)) {
7165 if (allopt[i].opttyp == BoolOpt && (bool_p = allopt[i].addr) != 0) {
7166 Sprintf(retbuf, "%s", *bool_p ? "true" : "false");
7167 return retbuf;
7168 } else if (allopt[i].opttyp == CompOpt && allopt[i].optfn) {
7169 int reslt = optn_err;
7170
7171 reslt = (*allopt[i].optfn)(allopt[i].idx, get_val,
7172 FALSE, retbuf, empty_optstr);
7173 if (reslt == optn_ok && retbuf[0])
7174 return retbuf;
7175 return (char *) 0;
7176 }
7177 }
7178 return (char *) 0;
7179 }
7180
7181 /* the 'O' command */
7182 int
doset(void)7183 doset(void) /* changing options via menu by Per Liboriussen */
7184 {
7185 static boolean made_fmtstr = FALSE;
7186 char buf[BUFSZ];
7187 const char *name;
7188 int i = 0, pass, pick_cnt, pick_idx, opt_indx;
7189 boolean *bool_p;
7190 winid tmpwin;
7191 anything any;
7192 menu_item *pick_list;
7193 int indexoffset, startpass, endpass, optflags;
7194 boolean setinitial = FALSE, fromfile = FALSE;
7195 unsigned longest_name_len;
7196
7197 tmpwin = create_nhwindow(NHW_MENU);
7198 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
7199
7200 #ifdef notyet /* SYSCF */
7201 /* XXX I think this is still fragile. Fixing initial/from_file and/or
7202 changing the SET_* etc to bitmaps will let me make this better. */
7203 if (wizard)
7204 startpass = set_in_sysconf;
7205 else
7206 #endif
7207 startpass = set_gameview;
7208 endpass = (wizard) ? set_wizonly : set_in_game;
7209
7210 if (!made_fmtstr && !iflags.menu_tab_sep) {
7211 /* spin through the options to find the longest name
7212 and adjust the format string accordingly */
7213 longest_name_len = 0;
7214 for (pass = 0; pass < 2; pass++)
7215 for (i = 0; (name = allopt[i].name) != 0; i++) {
7216 if (pass == 0 &&
7217 (allopt[i].opttyp != BoolOpt || !allopt[i].addr))
7218 continue;
7219 optflags = allopt[i].setwhere;
7220 if (optflags < startpass || optflags > endpass)
7221 continue;
7222 if ((is_wc_option(name) && !wc_supported(name))
7223 || (is_wc2_option(name) && !wc2_supported(name)))
7224 continue;
7225
7226 if (strlen(name) > longest_name_len)
7227 longest_name_len = strlen(name);
7228 }
7229 Sprintf(fmtstr_doset, "%%s%%-%us [%%s]", longest_name_len);
7230 made_fmtstr = TRUE;
7231 }
7232
7233 indexoffset = 1;
7234 any = cg.zeroany;
7235 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, iflags.menu_headings,
7236 "Booleans (selecting will toggle value):", MENU_ITEMFLAGS_NONE);
7237 any.a_int = 0;
7238 /* first list any other non-modifiable booleans, then modifiable ones */
7239 for (pass = 0; pass <= 1; pass++)
7240 for (i = 0; (name = allopt[i].name) != 0; i++)
7241 if (allopt[i].opttyp == BoolOpt
7242 && (bool_p = allopt[i].addr) != 0
7243 && ((allopt[i].setwhere <= set_gameview && pass == 0)
7244 || (allopt[i].setwhere >= set_in_game && pass == 1))) {
7245 if (bool_p == &flags.female)
7246 continue; /* obsolete */
7247 if (allopt[i].setwhere == set_wizonly && !wizard)
7248 continue;
7249 if ((is_wc_option(name) && !wc_supported(name))
7250 || (is_wc2_option(name) && !wc2_supported(name)))
7251 continue;
7252
7253 any.a_int = (pass == 0) ? 0 : i + 1 + indexoffset;
7254 if (!iflags.menu_tab_sep)
7255 Sprintf(buf, fmtstr_doset, (pass == 0) ? " " : "",
7256 name, *bool_p ? "true" : "false");
7257 else
7258 Sprintf(buf, fmtstr_doset_tab,
7259 name, *bool_p ? "true" : "false");
7260 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
7261 ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
7262 }
7263
7264 any = cg.zeroany;
7265 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
7266 ATR_NONE, "", MENU_ITEMFLAGS_NONE);
7267 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0, iflags.menu_headings,
7268 "Compounds (selecting will prompt for new value):",
7269 MENU_ITEMFLAGS_NONE);
7270
7271 /* deliberately put playmode, name, role+race+gender+align first */
7272 doset_add_menu(tmpwin, "playmode", opt_playmode, 0);
7273 doset_add_menu(tmpwin, "name", opt_name, 0);
7274 doset_add_menu(tmpwin, "role", opt_role, 0);
7275 doset_add_menu(tmpwin, "race", opt_race, 0);
7276 doset_add_menu(tmpwin, "gender", opt_gender, 0);
7277 doset_add_menu(tmpwin, "align", opt_align, 0);
7278
7279 for (pass = startpass; pass <= endpass; pass++)
7280 for (i = 0; (name = allopt[i].name) != 0; i++) {
7281 if (allopt[i].opttyp != CompOpt)
7282 continue;
7283 if ((int) allopt[i].setwhere == pass) {
7284 if (!strcmp(name, "playmode") || !strcmp(name, "name")
7285 || !strcmp(name, "role") || !strcmp(name, "race")
7286 || !strcmp(name, "gender") || !strcmp(name, "align"))
7287 continue;
7288 if ((is_wc_option(name) && !wc_supported(name))
7289 || (is_wc2_option(name) && !wc2_supported(name)))
7290 continue;
7291
7292 doset_add_menu(tmpwin, name, i,
7293 (pass == set_gameview) ? 0 : indexoffset);
7294 }
7295 }
7296
7297 any = cg.zeroany;
7298 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
7299 ATR_NONE, "", MENU_ITEMFLAGS_NONE);
7300 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
7301 iflags.menu_headings, "Other settings:", MENU_ITEMFLAGS_NONE);
7302
7303 for (pass = startpass; pass <= endpass; pass++)
7304 for (i = 0; (name = allopt[i].name) != 0; i++) {
7305 if (allopt[i].opttyp != OthrOpt)
7306 continue;
7307 if ((int) allopt[i].setwhere == pass) {
7308 if ((is_wc_option(name) && !wc_supported(name))
7309 || (is_wc2_option(name) && !wc2_supported(name)))
7310 continue;
7311
7312 doset_add_menu(tmpwin, name, i,
7313 (pass == set_gameview) ? 0 : indexoffset);
7314 }
7315 }
7316
7317 #ifdef PREFIXES_IN_USE
7318 any = cg.zeroany;
7319 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
7320 ATR_NONE, "", MENU_ITEMFLAGS_NONE);
7321 add_menu(tmpwin, &nul_glyphinfo, &any, 0, 0,
7322 iflags.menu_headings,
7323 "Variable playground locations:", MENU_ITEMFLAGS_NONE);
7324 for (i = 0; i < PREFIX_COUNT; i++)
7325 doset_add_menu(tmpwin, fqn_prefix_names[i], -1, 0);
7326 #endif
7327 end_menu(tmpwin, "Set what options?");
7328 g.opt_need_redraw = FALSE;
7329 if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) {
7330 /*
7331 * Walk down the selection list and either invert the booleans
7332 * or prompt for new values. In most cases, call parseoptions()
7333 * to take care of options that require special attention, like
7334 * redraws.
7335 */
7336 for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) {
7337 opt_indx = pick_list[pick_idx].item.a_int - 1;
7338 if (opt_indx < -1)
7339 opt_indx++; /* -1 offset for select_menu() */
7340 opt_indx -= indexoffset;
7341 if (allopt[opt_indx].opttyp == BoolOpt) {
7342 /* boolean option */
7343 Sprintf(buf, "%s%s", *allopt[opt_indx].addr ? "!" : "",
7344 allopt[opt_indx].name);
7345 (void) parseoptions(buf, setinitial, fromfile);
7346 } else {
7347 /* compound option */
7348 int k = opt_indx, reslt UNUSED;
7349
7350 if (allopt[k].has_handler && allopt[k].optfn) {
7351 reslt = (*allopt[k].optfn)(allopt[k].idx, do_handler,
7352 FALSE, empty_optstr, empty_optstr);
7353 } else {
7354 char abuf[BUFSZ];
7355
7356 Sprintf(buf, "Set %s to what?", allopt[opt_indx].name);
7357 abuf[0] = '\0';
7358 getlin(buf, abuf);
7359 if (abuf[0] == '\033')
7360 continue;
7361 Sprintf(buf, "%s:", allopt[opt_indx].name);
7362 (void) strncat(eos(buf), abuf,
7363 (sizeof buf - 1 - strlen(buf)));
7364 /* pass the buck */
7365 (void) parseoptions(buf, setinitial, fromfile);
7366 }
7367 }
7368 if (wc_supported(allopt[opt_indx].name)
7369 || wc2_supported(allopt[opt_indx].name))
7370 preference_update(allopt[opt_indx].name);
7371 }
7372 free((genericptr_t) pick_list), pick_list = (menu_item *) 0;
7373 }
7374
7375 destroy_nhwindow(tmpwin);
7376 if (g.opt_need_redraw) {
7377 check_gold_symbol();
7378 reglyph_darkroom();
7379 (void) doredraw();
7380 }
7381 if (g.context.botl || g.context.botlx) {
7382 bot();
7383 }
7384 return 0;
7385 }
7386
7387 /* doset('O' command) menu entries for compound options */
7388 static void
doset_add_menu(winid win,const char * option,int idx,int indexoffset)7389 doset_add_menu(winid win, /* window to add to */
7390 const char *option, /* option name */
7391 int idx, /* index in allopt[] */
7392 int indexoffset) /* value to add to index in allopt[],
7393 or zero if option cannot be changed */
7394 {
7395 const char *value = "unknown"; /* current value */
7396 char buf[BUFSZ], buf2[BUFSZ];
7397 anything any;
7398 int i = idx, reslt = optn_err;
7399 #ifdef PREFIXES_IN_USE
7400 int j;
7401 #endif
7402
7403 buf2[0] = '\0'; /* per opt functs may not guarantee this, so do it */
7404 any = cg.zeroany;
7405 if (i >= 0 && i < OPTCOUNT && allopt[i].name && allopt[i].optfn) {
7406 any.a_int = (indexoffset == 0) ? 0 : i + 1 + indexoffset;
7407 if (allopt[i].optfn)
7408 reslt = (*allopt[i].optfn)(allopt[i].idx, get_val,
7409 FALSE, buf2, empty_optstr);
7410 if (reslt == optn_ok && buf2[0])
7411 value = (const char *) buf2;
7412 } else {
7413 /* We are trying to add an option not found in allopt[].
7414 This is almost certainly bad, but we'll let it through anyway
7415 (with a zero value, so it can't be selected). */
7416 any.a_int = 0;
7417 #ifdef PREFIXES_IN_USE
7418 for (j = 0; j < PREFIX_COUNT; ++j)
7419 if (!strcmp(option, fqn_prefix_names[j]) && g.fqn_prefix[j])
7420 Sprintf(buf2, "%s", g.fqn_prefix[j]);
7421 #endif
7422 if (!buf2[0])
7423 Strcpy(buf2, "unknown");
7424 value = (const char *) buf2;
7425 }
7426
7427 /* " " replaces "a - " -- assumes menus follow that style */
7428 if (!iflags.menu_tab_sep)
7429 Sprintf(buf, fmtstr_doset, any.a_int ? "" : " ", option,
7430 value);
7431 else
7432 Sprintf(buf, fmtstr_doset_tab, option, value);
7433 add_menu(win, &nul_glyphinfo, &any, 0, 0,
7434 ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
7435 }
7436
7437
7438 /* display keys for menu actions; used by cmd.c '?i' and pager.c '?k' */
7439 void
show_menu_controls(winid win,boolean dolist)7440 show_menu_controls(winid win, boolean dolist)
7441 {
7442 struct xtra_cntrls {
7443 const char *key, *desc;
7444 };
7445 static const struct xtra_cntrls hardcoded[] = {
7446 { "Return", "Accept current choice(s) and dismiss menu" },
7447 { "Enter", "Same as Return" },
7448 { "Space", "If not on last page, advance one page;" },
7449 { " ", "when on last page, treat like Return" },
7450 { "Escape", "Cancel menu without making any choice(s)" },
7451 { (char *) 0, (char *) 0}
7452 };
7453 static const char mc_fmt[] = "%8s %-6s %s",
7454 mc_altfmt[] = "%9s %-6s %s";
7455 char buf[BUFSZ];
7456 const char *fmt, *arg;
7457 const struct xtra_cntrls *xcp;
7458 boolean has_menu_shift = wc2_supported("menu_shift");
7459
7460 /*
7461 * Relies on spaces to line things up in columns, so must be rendered
7462 * with a fixed-width font or will look dreadful.
7463 */
7464
7465 putstr(win, 0, "Menu control keys:");
7466 if (dolist) { /* key bindings help: '?i' */
7467 int i;
7468 char ch;
7469
7470 fmt = "%-7s %s";
7471 for (i = 0; default_menu_cmd_info[i].desc; i++) {
7472 ch = default_menu_cmd_info[i].cmd;
7473 if ((ch == MENU_SHIFT_RIGHT
7474 || ch == MENU_SHIFT_LEFT) && !has_menu_shift)
7475 continue;
7476 Sprintf(buf, fmt,
7477 visctrl(get_menu_cmd_key(ch)),
7478 default_menu_cmd_info[i].desc);
7479 putstr(win, 0, buf);
7480 }
7481 /* no separator before hardcoded */
7482 fmt = "%s%-7s %s"; /* extra specifier to absorb 'arg' */
7483 arg = ""; /* no extra prefix for 'dolist' */
7484 } else { /* menu controls help: '?k' */
7485 putstr(win, 0, "");
7486 Sprintf(buf, mc_altfmt, "", "Whole", "Current");
7487 putstr(win, 0, buf);
7488 Sprintf(buf, mc_altfmt, "", " Menu", " Page");
7489 putstr(win, 0, buf);
7490 Sprintf(buf, mc_fmt, "Select",
7491 visctrl(get_menu_cmd_key(MENU_SELECT_ALL)),
7492 visctrl(get_menu_cmd_key(MENU_SELECT_PAGE)));
7493 putstr(win, 0, buf);
7494 Sprintf(buf, mc_fmt, "Invert",
7495 visctrl(get_menu_cmd_key(MENU_INVERT_ALL)),
7496 visctrl(get_menu_cmd_key(MENU_INVERT_PAGE)));
7497 putstr(win, 0, buf);
7498 Sprintf(buf, mc_fmt, "Deselect",
7499 visctrl(get_menu_cmd_key(MENU_UNSELECT_ALL)),
7500 visctrl(get_menu_cmd_key(MENU_UNSELECT_PAGE)));
7501 putstr(win, 0, buf);
7502 putstr(win, 0, "");
7503 Sprintf(buf, mc_fmt, "Go to",
7504 visctrl(get_menu_cmd_key(MENU_NEXT_PAGE)),
7505 "Next page");
7506 putstr(win, 0, buf);
7507 Sprintf(buf, mc_fmt, "",
7508 visctrl(get_menu_cmd_key(MENU_PREVIOUS_PAGE)),
7509 "Previous page");
7510 putstr(win, 0, buf);
7511 Sprintf(buf, mc_fmt, "",
7512 visctrl(get_menu_cmd_key(MENU_FIRST_PAGE)),
7513 "First page");
7514 putstr(win, 0, buf);
7515 Sprintf(buf, mc_fmt, "",
7516 visctrl(get_menu_cmd_key(MENU_LAST_PAGE)),
7517 "Last page");
7518 putstr(win, 0, buf);
7519 if (has_menu_shift) {
7520 Sprintf(buf, mc_fmt, "Pan view",
7521 visctrl(get_menu_cmd_key(MENU_SHIFT_RIGHT)),
7522 "Right (perm_invent only)");
7523 putstr(win, 0, buf);
7524 Sprintf(buf, mc_fmt, "",
7525 visctrl(get_menu_cmd_key(MENU_SHIFT_LEFT)),
7526 "Left");
7527 putstr(win, 0, buf);
7528 }
7529 putstr(win, 0, "");
7530 Sprintf(buf, mc_fmt, "Search",
7531 visctrl(get_menu_cmd_key(MENU_SEARCH)),
7532 "Exter a target string and invert all matching entries");
7533 putstr(win, 0, buf);
7534 /* separator before hardcoded */
7535 putstr(win, 0, "");
7536 fmt = "%9s %-8s %s";
7537 arg = "Other "; /* prefix for first hardcoded[] entry, then reset */
7538 }
7539 for (xcp = hardcoded; xcp->key; ++xcp) {
7540 Sprintf(buf, fmt, arg, xcp->key, xcp->desc);
7541 putstr(win, 0, buf);
7542 arg = "";
7543 }
7544 }
7545
7546 RESTORE_WARNING_FORMAT_NONLITERAL
7547
7548 static int
count_cond(void)7549 count_cond(void)
7550 {
7551 int i, cnt = 0;
7552
7553 for (i = 0; i < CONDITION_COUNT; ++i) {
7554 if (condtests[i].enabled)
7555 cnt++;
7556 }
7557 return cnt;
7558 }
7559
7560 int
count_apes(void)7561 count_apes(void)
7562 {
7563 int numapes = 0;
7564 struct autopickup_exception *ape = g.apelist;
7565
7566 while (ape) {
7567 numapes++;
7568 ape = ape->next;
7569 }
7570
7571 return numapes;
7572 }
7573
7574 static int
count_monstercolors(void)7575 count_monstercolors(void)
7576 {
7577 int i, numcolors = 0;
7578 for (i = LOW_PM; i < NUMMONS; i++) {
7579 if (g.monstercolors[i] != MONSTERCOLOR_DEFAULT) {
7580 numcolors++;
7581 }
7582 }
7583 return numcolors;
7584 }
7585
7586 DISABLE_WARNING_FORMAT_NONLITERAL
7587
7588 /* common to msg-types, menu-colors, autopickup-exceptions, monster-colors */
7589 static int
handle_add_list_remove(const char * optname,int numtotal)7590 handle_add_list_remove(const char *optname, int numtotal)
7591 {
7592 winid tmpwin;
7593 anything any;
7594 int i, pick_cnt, opt_idx;
7595 menu_item *pick_list = (menu_item *) 0;
7596 static const struct action {
7597 char letr;
7598 const char *desc;
7599 } action_titles[] = {
7600 { 'a', "add new %s" }, /* [0] */
7601 { 'l', "list %s" }, /* [1] */
7602 { 'r', "remove existing %s" }, /* [2] */
7603 { 'x', "exit this menu" }, /* [3] */
7604 };
7605
7606 opt_idx = 0;
7607 tmpwin = create_nhwindow(NHW_MENU);
7608 start_menu(tmpwin, MENU_BEHAVE_STANDARD);
7609 any = cg.zeroany;
7610 for (i = 0; i < SIZE(action_titles); i++) {
7611 char tmpbuf[BUFSZ];
7612
7613 any.a_int++;
7614 /* omit list and remove if there aren't any yet */
7615 if (!numtotal && (i == 1 || i == 2))
7616 continue;
7617 Sprintf(tmpbuf, action_titles[i].desc,
7618 (i == 1) ? makeplural(optname) : optname);
7619 add_menu(tmpwin, &nul_glyphinfo,&any, action_titles[i].letr,
7620 0, ATR_NONE, tmpbuf,
7621 (i == 3) ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
7622 }
7623 end_menu(tmpwin, "Do what?");
7624 if ((pick_cnt = select_menu(tmpwin, PICK_ONE, &pick_list)) > 0) {
7625 opt_idx = pick_list[0].item.a_int - 1;
7626 if (pick_cnt > 1 && opt_idx == 3)
7627 opt_idx = pick_list[1].item.a_int - 1;
7628 free((genericptr_t) pick_list);
7629 } else
7630 opt_idx = 3; /* none selected, exit menu */
7631 destroy_nhwindow(tmpwin);
7632 return opt_idx;
7633 }
7634
7635 RESTORE_WARNING_FORMAT_NONLITERAL
7636
7637 int
dotogglepickup(void)7638 dotogglepickup(void)
7639 {
7640 char buf[BUFSZ], ocl[MAXOCLASSES + 1];
7641
7642 flags.pickup = !flags.pickup;
7643 if (flags.pickup) {
7644 oc_to_str(flags.pickup_types, ocl);
7645 Sprintf(buf, "ON, for %s objects%s", ocl[0] ? ocl : "all",
7646 (g.apelist)
7647 ? ((count_apes() == 1)
7648 ? ", with one exception"
7649 : ", with some exceptions")
7650 : "");
7651 } else {
7652 Strcpy(buf, "OFF");
7653 }
7654 pline("Autopickup: %s.", buf);
7655 return 0;
7656 }
7657
7658 int
add_autopickup_exception(const char * mapping)7659 add_autopickup_exception(const char *mapping)
7660 {
7661 static const char
7662 APE_regex_error[] = "regex error in AUTOPICKUP_EXCEPTION",
7663 APE_syntax_error[] = "syntax error in AUTOPICKUP_EXCEPTION";
7664
7665 struct autopickup_exception *ape;
7666 char text[256], end;
7667 int n;
7668 boolean grab = FALSE;
7669
7670 /* scan length limit used to be 255, but smaller size allows the
7671 quoted value to fit within BUFSZ, simplifying formatting elsewhere;
7672 this used to ignore the possibility of trailing junk but now checks
7673 for it, accepting whitespace but rejecting anything else unless it
7674 starts with '#" for a comment */
7675 end = '\0';
7676 if ((n = sscanf(mapping, "\"<%253[^\"]\" %c", text, &end)) == 1
7677 || (n == 2 && end == '#')) {
7678 grab = TRUE;
7679 } else if ((n = sscanf(mapping, "\">%253[^\"]\" %c", text, &end)) == 1
7680 || (n = sscanf(mapping, "\"%253[^\"]\" %c", text, &end)) == 1
7681 || (n == 2 && end == '#')) {
7682 grab = FALSE;
7683 } else {
7684 config_error_add("%s", APE_syntax_error);
7685 return 0;
7686 }
7687
7688 ape = (struct autopickup_exception *) alloc(sizeof *ape);
7689 ape->regex = regex_init();
7690 if (!regex_compile(text, ape->regex)) {
7691 const char *re_error_desc = regex_error_desc(ape->regex);
7692
7693 /* free first in case reason for failure was insufficient memory */
7694 regex_free(ape->regex);
7695 free((genericptr_t) ape);
7696 config_error_add("%s: %s", APE_regex_error, re_error_desc);
7697 return 0;
7698 }
7699 ape->pattern = dupstr(text);
7700 ape->grab = grab;
7701 ape->next = g.apelist;
7702 g.apelist = ape;
7703 return 1;
7704 }
7705
7706 static void
remove_autopickup_exception(struct autopickup_exception * whichape)7707 remove_autopickup_exception(struct autopickup_exception *whichape)
7708 {
7709 struct autopickup_exception *ape, *freeape, *prev = 0;
7710
7711 for (ape = g.apelist; ape;) {
7712 if (ape == whichape) {
7713 freeape = ape;
7714 ape = ape->next;
7715 if (prev)
7716 prev->next = ape;
7717 else
7718 g.apelist = ape;
7719 regex_free(freeape->regex);
7720 free((genericptr_t) freeape->pattern);
7721 free((genericptr_t) freeape);
7722 } else {
7723 prev = ape;
7724 ape = ape->next;
7725 }
7726 }
7727 }
7728
7729 void
free_autopickup_exceptions(void)7730 free_autopickup_exceptions(void)
7731 {
7732 struct autopickup_exception *ape;
7733
7734 while ((ape = g.apelist) != 0) {
7735 free((genericptr_t) ape->pattern);
7736 regex_free(ape->regex);
7737 g.apelist = ape->next;
7738 free((genericptr_t) ape);
7739 }
7740 }
7741
7742 /* bundle some common usage into one easy-to-use routine */
7743 int
load_symset(const char * s,int which_set)7744 load_symset(const char *s, int which_set)
7745 {
7746 clear_symsetentry(which_set, TRUE);
7747
7748 if (g.symset[which_set].name)
7749 free((genericptr_t) g.symset[which_set].name);
7750 g.symset[which_set].name = dupstr(s);
7751
7752 if (read_sym_file(which_set)) {
7753 switch_symbols(TRUE);
7754 } else {
7755 clear_symsetentry(which_set, TRUE);
7756 return 0;
7757 }
7758 return 1;
7759 }
7760
7761 void
free_symsets(void)7762 free_symsets(void)
7763 {
7764 clear_symsetentry(PRIMARY, TRUE);
7765
7766 /* symset_list is cleaned up as soon as it's used, so we shouldn't
7767 have to anything about it here */
7768 /* assert( symset_list == NULL ); */
7769 }
7770
7771 /* Parse the value of a SYMBOLS line from a config file */
7772 boolean
parsesymbols(register char * opts,int which_set)7773 parsesymbols(register char *opts, int which_set)
7774 {
7775 int val;
7776 char *op, *symname, *strval;
7777 struct symparse *symp;
7778
7779 if ((op = index(opts, ',')) != 0) {
7780 *op++ = 0;
7781 if (!parsesymbols(op, which_set))
7782 return FALSE;
7783 }
7784
7785 /* S_sample:string */
7786 symname = opts;
7787 strval = index(opts, ':');
7788 if (!strval)
7789 strval = index(opts, '=');
7790 if (!strval)
7791 return FALSE;
7792 *strval++ = '\0';
7793
7794 /* strip leading and trailing white space from symname and strval */
7795 mungspaces(symname);
7796 mungspaces(strval);
7797
7798 symp = match_sym(symname);
7799 if (!symp)
7800 return FALSE;
7801
7802 if (symp->range && symp->range != SYM_CONTROL) {
7803 val = sym_val(strval);
7804 update_ov_primary_symset(symp, val);
7805 }
7806 return TRUE;
7807 }
7808
7809 struct symparse *
match_sym(char * buf)7810 match_sym(char *buf)
7811 {
7812 size_t len = strlen(buf);
7813 const char *p = index(buf, ':'), *q = index(buf, '=');
7814 struct symparse *sp = loadsyms;
7815
7816 if (!p || (q && q < p))
7817 p = q;
7818 if (p) {
7819 /* note: there will be at most one space before the '='
7820 because caller has condensed buf[] with mungspaces() */
7821 if (p > buf && p[-1] == ' ')
7822 p--;
7823 len = (int) (p - buf);
7824 }
7825 while (sp->range) {
7826 if ((len >= strlen(sp->name)) && !strncmpi(buf, sp->name, len))
7827 return sp;
7828 sp++;
7829 }
7830 return (struct symparse *) 0;
7831 }
7832
7833 int
sym_val(const char * strval)7834 sym_val(const char *strval) /* up to 4*BUFSZ-1 long; only first few
7835 chars matter */
7836 {
7837 char buf[QBUFSZ], tmp[QBUFSZ]; /* to hold trucated copy of 'strval' */
7838
7839 buf[0] = '\0';
7840 if (!strval[0] || !strval[1]) { /* empty, or single character */
7841 /* if single char is space or tab, leave buf[0]=='\0' */
7842 if (!isspace((uchar) strval[0]))
7843 buf[0] = strval[0];
7844 } else if (strval[0] == '\'') { /* single quote */
7845 /* simple matching single quote; we know strval[1] isn't '\0' */
7846 if (strval[2] == '\'' && !strval[3]) {
7847 /* accepts '\' as backslash and ''' as single quote */
7848 buf[0] = strval[1];
7849
7850 /* if backslash, handle single or double quote or second backslash */
7851 } else if (strval[1] == '\\' && strval[2] && strval[3] == '\''
7852 && index("'\"\\", strval[2]) && !strval[4]) {
7853 buf[0] = strval[2];
7854
7855 /* not simple quote or basic backslash;
7856 strip closing quote and let escapes() deal with it */
7857 } else {
7858 char *p;
7859
7860 /* +1: skip opening single quote */
7861 (void) strncpy(tmp, strval + 1, sizeof tmp - 1);
7862 tmp[sizeof tmp - 1] = '\0';
7863 if ((p = rindex(tmp, '\'')) != 0) {
7864 *p = '\0';
7865 escapes(tmp, buf);
7866 } /* else buf[0] stays '\0' */
7867 }
7868 } else { /* not lone char nor single quote */
7869 (void) strncpy(tmp, strval, sizeof tmp - 1);
7870 tmp[sizeof tmp - 1] = '\0';
7871 escapes(tmp, buf);
7872 }
7873
7874 return (int) *buf;
7875 }
7876
7877 /* data for option_help() */
7878 static const char *opt_intro[] = {
7879 "",
7880 " NetHack Options Help:", "",
7881 #define CONFIG_SLOT 3 /* fill in next value at run-time */
7882 (char *) 0,
7883 #if !defined(MICRO) && !defined(MAC)
7884 "or use `NETHACKOPTIONS=\"<options>\"' in your environment",
7885 #endif
7886 "(<options> is a list of options separated by commas)",
7887 #ifdef VMS
7888 "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"",
7889 #endif
7890 "or press \"O\" while playing and use the menu.",
7891 "",
7892 "Boolean options (which can be negated by prefixing them with '!' or \"no\"):",
7893 (char *) 0
7894 };
7895
7896 static const char *opt_epilog[] = {
7897 "",
7898 "Some of the options can only be set before the game is started;",
7899 "those items will not be selectable in the 'O' command's menu.",
7900 "Some options are stored in a game's save file, and will keep saved",
7901 "values when restoring that game even if you have updated your config-",
7902 "uration file to change them. Such changes will matter for new games.",
7903 "The \"other settings\" can be set with 'O', but when set within the",
7904 "configuration file they use their own directives rather than OPTIONS.",
7905 "See NetHack's \"Guidebook\" for details.",
7906 (char *) 0
7907 };
7908
7909 void
option_help(void)7910 option_help(void)
7911 {
7912 char buf[BUFSZ], buf2[BUFSZ];
7913 const char *optname;
7914 register int i;
7915 winid datawin;
7916
7917 datawin = create_nhwindow(NHW_TEXT);
7918 Sprintf(buf, "Set options as OPTIONS=<options> in %s", configfile);
7919 opt_intro[CONFIG_SLOT] = (const char *) buf;
7920 for (i = 0; opt_intro[i]; i++)
7921 putstr(datawin, 0, opt_intro[i]);
7922
7923 /* Boolean options */
7924 for (i = 0; allopt[i].name; i++) {
7925 if ((allopt[i].opttyp != BoolOpt || !allopt[i].addr)
7926 || (allopt[i].setwhere == set_wizonly && !wizard))
7927 continue;
7928 optname = allopt[i].name;
7929 if ((is_wc_option(optname) && !wc_supported(optname))
7930 || (is_wc2_option(optname) && !wc2_supported(optname)))
7931 continue;
7932 next_opt(datawin, optname);
7933 }
7934 next_opt(datawin, "");
7935
7936 /* Compound options */
7937 putstr(datawin, 0, "Compound options:");
7938 for (i = 0; allopt[i].name; i++) {
7939 if (allopt[i].opttyp != CompOpt
7940 || (allopt[i].setwhere == set_wizonly && !wizard))
7941 continue;
7942 optname = allopt[i].name;
7943 if ((is_wc_option(optname) && !wc_supported(optname))
7944 || (is_wc2_option(optname) && !wc2_supported(optname)))
7945 continue;
7946 Sprintf(buf2, "`%s'", optname);
7947 Snprintf(buf, sizeof(buf), "%-20s - %s%c", buf2, allopt[i].descr,
7948 allopt[i + 1].name ? ',' : '.');
7949 putstr(datawin, 0, buf);
7950 }
7951 putstr(datawin, 0, "");
7952
7953 /* Compound options */
7954 putstr(datawin, 0, "Other settings:");
7955 for (i = 0; allopt[i].name; i++) {
7956 if (allopt[i].opttyp != OthrOpt)
7957 continue;
7958 Sprintf(buf, " %s", allopt[i].name);
7959 putstr(datawin, 0, buf);
7960 }
7961
7962 putstr(datawin, 0, "");
7963
7964 for (i = 0; opt_epilog[i]; i++)
7965 putstr(datawin, 0, opt_epilog[i]);
7966
7967 /*
7968 * TODO:
7969 * briefly describe interface-specific option-like settings for
7970 * the currently active interface:
7971 * X11 uses X-specific "application defaults" from NetHack.ad;
7972 * Qt has menu accessible "game -> Qt settings" (non-OSX) or
7973 * "nethack -> Preferences" (OSX) to maintain a few options
7974 * (font size, map tile size, paperdoll show/hide flag and
7975 * tile size) which persist across games;
7976 * Windows GUI also has some port-specific menus;
7977 * tty and curses: anything?
7978 * Best done via a new windowprocs function rather than plugging
7979 * in details here.
7980 *
7981 * Maybe:
7982 * switch from text window to pick-none menu so that user can
7983 * scroll back up. (Not necessary for Qt where text windows are
7984 * already scrollable.)
7985 */
7986
7987 display_nhwindow(datawin, FALSE);
7988 destroy_nhwindow(datawin);
7989 return;
7990 }
7991
7992 /*
7993 * prints the next boolean option, on the same line if possible, on a new
7994 * line if not. End with next_opt("").
7995 */
7996 void
next_opt(winid datawin,const char * str)7997 next_opt(winid datawin, const char *str)
7998 {
7999 static char *buf = 0;
8000 int i;
8001 char *s;
8002
8003 if (!buf)
8004 *(buf = (char *) alloc(COLBUFSZ)) = '\0';
8005
8006 if (!*str) {
8007 s = eos(buf);
8008 if (s > &buf[1] && s[-2] == ',')
8009 Strcpy(s - 2, "."); /* replace last ", " */
8010 i = COLNO; /* (greater than COLNO - 2) */
8011 } else {
8012 i = strlen(buf) + strlen(str) + 2;
8013 }
8014
8015 if (i > COLNO - 2) { /* rule of thumb */
8016 putstr(datawin, 0, buf);
8017 buf[0] = 0;
8018 }
8019 if (*str) {
8020 Strcat(buf, str);
8021 Strcat(buf, ", ");
8022 } else {
8023 putstr(datawin, 0, str);
8024 free((genericptr_t) buf), buf = 0;
8025 }
8026 return;
8027 }
8028
8029 /*
8030 * This is a somewhat generic menu for taking a list of NetHack style
8031 * class choices and presenting them via a description
8032 * rather than the traditional NetHack characters.
8033 * (Benefits users whose first exposure to NetHack is via tiles).
8034 *
8035 * prompt
8036 * The title at the top of the menu.
8037 *
8038 * category: 0 = monster class
8039 * 1 = object class
8040 *
8041 * way
8042 * FALSE = PICK_ONE, TRUE = PICK_ANY
8043 *
8044 * class_list
8045 * a null terminated string containing the list of choices.
8046 *
8047 * class_selection
8048 * a null terminated string containing the selected characters.
8049 *
8050 * Returns number selected.
8051 */
8052 int
choose_classes_menu(const char * prompt,int category,boolean way,char * class_list,char * class_select)8053 choose_classes_menu(const char *prompt,
8054 int category,
8055 boolean way,
8056 char *class_list,
8057 char *class_select)
8058 {
8059 menu_item *pick_list = (menu_item *) 0;
8060 winid win;
8061 anything any;
8062 char buf[BUFSZ];
8063 int i, n;
8064 int ret;
8065 int next_accelerator, accelerator;
8066
8067 if (class_list == (char *) 0 || class_select == (char *) 0)
8068 return 0;
8069 accelerator = 0;
8070 next_accelerator = 'a';
8071 any = cg.zeroany;
8072 win = create_nhwindow(NHW_MENU);
8073 start_menu(win, MENU_BEHAVE_STANDARD);
8074 while (*class_list) {
8075 const char *text;
8076 boolean selected;
8077
8078 text = (char *) 0;
8079 selected = FALSE;
8080 switch (category) {
8081 case 0:
8082 text = def_monsyms[def_char_to_monclass(*class_list)].explain;
8083 accelerator = *class_list;
8084 Sprintf(buf, "%s", text);
8085 break;
8086 case 1:
8087 text = def_oc_syms[def_char_to_objclass(*class_list)].explain;
8088 accelerator = next_accelerator;
8089 Sprintf(buf, "%c %s", *class_list, text);
8090 break;
8091 default:
8092 impossible("choose_classes_menu: invalid category %d", category);
8093 }
8094 if (way && *class_select) { /* Selections there already */
8095 if (index(class_select, *class_list)) {
8096 selected = TRUE;
8097 }
8098 }
8099 any.a_int = *class_list;
8100 add_menu(win, &nul_glyphinfo, &any, accelerator,
8101 category ? *class_list : 0, ATR_NONE, buf,
8102 selected ? MENU_ITEMFLAGS_SELECTED : MENU_ITEMFLAGS_NONE);
8103 ++class_list;
8104 if (category > 0) {
8105 ++next_accelerator;
8106 if (next_accelerator == ('z' + 1))
8107 next_accelerator = 'A';
8108 if (next_accelerator == ('Z' + 1))
8109 break;
8110 }
8111 }
8112 if (category == 1 && next_accelerator <= 'z') {
8113 /* for objects, add "A - ' ' all classes", after a separator */
8114 any = cg.zeroany;
8115 add_menu(win, &nul_glyphinfo, &any, 0, 0,
8116 ATR_NONE, "", MENU_ITEMFLAGS_NONE);
8117 any.a_int = (int) ' ';
8118 Sprintf(buf, "%c %s", (char) any.a_int, "all classes of objects");
8119 /* we won't preselect this even if the incoming list is empty;
8120 having it selected means that it would have to be explicitly
8121 de-selected in order to select anything else */
8122 add_menu(win, &nul_glyphinfo, &any, 'A', 0,
8123 ATR_NONE, buf, MENU_ITEMFLAGS_NONE);
8124 }
8125 end_menu(win, prompt);
8126 n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list);
8127 destroy_nhwindow(win);
8128 if (n > 0) {
8129 if (category == 1) {
8130 /* for object classes, first check for 'all'; it means 'use
8131 a blank list' rather than 'collect every possible choice' */
8132 for (i = 0; i < n; ++i)
8133 if (pick_list[i].item.a_int == ' ') {
8134 pick_list[0].item.a_int = ' ';
8135 n = 1; /* return 1; also an implicit 'break;' */
8136 }
8137 }
8138 for (i = 0; i < n; ++i)
8139 *class_select++ = (char) pick_list[i].item.a_int;
8140 free((genericptr_t) pick_list);
8141 ret = n;
8142 } else if (n == -1) {
8143 class_select = eos(class_select);
8144 ret = -1;
8145 } else
8146 ret = 0;
8147 *class_select = '\0';
8148 return ret;
8149 }
8150
8151 static struct wc_Opt wc_options[] = {
8152 { "ascii_map", WC_ASCII_MAP },
8153 { "color", WC_COLOR },
8154 { "eight_bit_tty", WC_EIGHT_BIT_IN },
8155 { "hilite_pet", WC_HILITE_PET },
8156 { "perm_invent", WC_PERM_INVENT },
8157 { "popup_dialog", WC_POPUP_DIALOG },
8158 { "player_selection", WC_PLAYER_SELECTION },
8159 { "preload_tiles", WC_PRELOAD_TILES },
8160 { "tiled_map", WC_TILED_MAP },
8161 { "tile_file", WC_TILE_FILE },
8162 { "tile_width", WC_TILE_WIDTH },
8163 { "tile_height", WC_TILE_HEIGHT },
8164 { "align_message", WC_ALIGN_MESSAGE },
8165 { "align_status", WC_ALIGN_STATUS },
8166 { "font_map", WC_FONT_MAP },
8167 { "font_menu", WC_FONT_MENU },
8168 { "font_message", WC_FONT_MESSAGE },
8169 { "font_size_map", WC_FONTSIZ_MAP },
8170 { "font_size_menu", WC_FONTSIZ_MENU },
8171 { "font_size_message", WC_FONTSIZ_MESSAGE },
8172 { "font_size_status", WC_FONTSIZ_STATUS },
8173 { "font_size_text", WC_FONTSIZ_TEXT },
8174 { "font_status", WC_FONT_STATUS },
8175 { "font_text", WC_FONT_TEXT },
8176 { "map_mode", WC_MAP_MODE },
8177 { "scroll_amount", WC_SCROLL_AMOUNT },
8178 { "scroll_margin", WC_SCROLL_MARGIN },
8179 { "splash_screen", WC_SPLASH_SCREEN },
8180 { "use_inverse", WC_INVERSE },
8181 { "vary_msgcount", WC_VARY_MSGCOUNT },
8182 { "windowcolors", WC_WINDOWCOLORS },
8183 { "mouse_support", WC_MOUSE_SUPPORT },
8184 { (char *) 0, 0L }
8185 };
8186 static struct wc_Opt wc2_options[] = {
8187 { "fullscreen", WC2_FULLSCREEN },
8188 { "guicolor", WC2_GUICOLOR },
8189 { "hilite_status", WC2_HILITE_STATUS },
8190 { "hitpointbar", WC2_HITPOINTBAR },
8191 { "menu_shift", WC2_MENU_SHIFT },
8192 { "petattr", WC2_PETATTR },
8193 { "softkeyboard", WC2_SOFTKEYBOARD },
8194 /* name shown in 'O' menu is different */
8195 { "status hilite rules", WC2_HILITE_STATUS },
8196 /* statushilites doesn't have its own bit */
8197 { "statushilites", WC2_HILITE_STATUS },
8198 { "statuslines", WC2_STATUSLINES },
8199 { "term_cols", WC2_TERM_SIZE },
8200 { "term_rows", WC2_TERM_SIZE },
8201 { "use_darkgray", WC2_DARKGRAY },
8202 { "windowborders", WC2_WINDOWBORDERS },
8203 { "wraptext", WC2_WRAPTEXT },
8204 { (char *) 0, 0L }
8205 };
8206
8207 /*
8208 * If a port wants to change or ensure that the set_in_sysconf,
8209 * set_in_config, set_gameview, or set_in_game status of an option is
8210 * correct (for controlling its display in the option menu) call
8211 * set_option_mod_status()
8212 * with the appropriate second argument.
8213 */
8214 void
set_option_mod_status(const char * optnam,int status)8215 set_option_mod_status(const char *optnam, int status)
8216 {
8217 int k;
8218
8219 if (SET__IS_VALUE_VALID(status)) {
8220 impossible("set_option_mod_status: status out of range %d.", status);
8221 return;
8222 }
8223 for (k = 0; allopt[k].name; k++) {
8224 if (!strncmpi(allopt[k].name, optnam, strlen(optnam))) {
8225 allopt[k].setwhere = status;
8226 return;
8227 }
8228 }
8229 for (k = 0; allopt[k].name; k++) {
8230 if (!strncmpi(allopt[k].name, optnam, strlen(optnam))) {
8231 allopt[k].setwhere = status;
8232 return;
8233 }
8234 }
8235 }
8236
8237 /*
8238 * You can set several wc_options in one call to
8239 * set_wc_option_mod_status() by setting
8240 * the appropriate bits for each option that you
8241 * are setting in the optmask argument
8242 * prior to calling.
8243 * example: set_wc_option_mod_status(WC_COLOR|WC_SCROLL_MARGIN,
8244 * set_in_game);
8245 */
8246 void
set_wc_option_mod_status(unsigned long optmask,int status)8247 set_wc_option_mod_status(unsigned long optmask, int status)
8248 {
8249 int k = 0;
8250
8251 if (SET__IS_VALUE_VALID(status)) {
8252 impossible("set_wc_option_mod_status: status out of range %d.",
8253 status);
8254 return;
8255 }
8256 while (wc_options[k].wc_name) {
8257 if (optmask & wc_options[k].wc_bit) {
8258 set_option_mod_status(wc_options[k].wc_name, status);
8259 }
8260 k++;
8261 }
8262 }
8263
8264 static boolean
is_wc_option(const char * optnam)8265 is_wc_option(const char *optnam)
8266 {
8267 int k = 0;
8268
8269 while (wc_options[k].wc_name) {
8270 if (strcmp(wc_options[k].wc_name, optnam) == 0)
8271 return TRUE;
8272 k++;
8273 }
8274 return FALSE;
8275 }
8276
8277 static boolean
wc_supported(const char * optnam)8278 wc_supported(const char *optnam)
8279 {
8280 int k;
8281
8282 for (k = 0; wc_options[k].wc_name; ++k) {
8283 if (!strcmp(wc_options[k].wc_name, optnam))
8284 return (windowprocs.wincap & wc_options[k].wc_bit) ? TRUE : FALSE;
8285 }
8286 return FALSE;
8287 }
8288
8289 /*
8290 * You can set several wc2_options in one call to
8291 * set_wc2_option_mod_status() by setting
8292 * the appropriate bits for each option that you
8293 * are setting in the optmask argument
8294 * prior to calling.
8295 * example:
8296 * set_wc2_option_mod_status(WC2_FULLSCREEN|WC2_SOFTKEYBOARD|WC2_WRAPTEXT,
8297 * set_in_config);
8298 */
8299
8300 void
set_wc2_option_mod_status(unsigned long optmask,int status)8301 set_wc2_option_mod_status(unsigned long optmask, int status)
8302 {
8303 int k = 0;
8304
8305 if (SET__IS_VALUE_VALID(status)) {
8306 impossible("set_wc2_option_mod_status: status out of range %d.",
8307 status);
8308 return;
8309 }
8310 while (wc2_options[k].wc_name) {
8311 if (optmask & wc2_options[k].wc_bit) {
8312 set_option_mod_status(wc2_options[k].wc_name, status);
8313 }
8314 k++;
8315 }
8316 }
8317
8318 static boolean
is_wc2_option(const char * optnam)8319 is_wc2_option(const char *optnam)
8320 {
8321 int k = 0;
8322
8323 while (wc2_options[k].wc_name) {
8324 if (strcmp(wc2_options[k].wc_name, optnam) == 0)
8325 return TRUE;
8326 k++;
8327 }
8328 return FALSE;
8329 }
8330
8331 static boolean
wc2_supported(const char * optnam)8332 wc2_supported(const char *optnam)
8333 {
8334 int k;
8335
8336 for (k = 0; wc2_options[k].wc_name; ++k) {
8337 if (!strcmp(wc2_options[k].wc_name, optnam))
8338 return (windowprocs.wincap2 & wc2_options[k].wc_bit) ? TRUE
8339 : FALSE;
8340 }
8341 return FALSE;
8342 }
8343
8344 static void
wc_set_font_name(int opttype,char * fontname)8345 wc_set_font_name(int opttype, char *fontname)
8346 {
8347 char **fn = (char **) 0;
8348
8349 if (!fontname)
8350 return;
8351 switch (opttype) {
8352 case MAP_OPTION:
8353 fn = &iflags.wc_font_map;
8354 break;
8355 case MESSAGE_OPTION:
8356 fn = &iflags.wc_font_message;
8357 break;
8358 case TEXT_OPTION:
8359 fn = &iflags.wc_font_text;
8360 break;
8361 case MENU_OPTION:
8362 fn = &iflags.wc_font_menu;
8363 break;
8364 case STATUS_OPTION:
8365 fn = &iflags.wc_font_status;
8366 break;
8367 default:
8368 return;
8369 }
8370 if (fn) {
8371 if (*fn)
8372 free((genericptr_t) *fn);
8373 *fn = dupstr(fontname);
8374 }
8375 return;
8376 }
8377
8378 static int
wc_set_window_colors(char * op)8379 wc_set_window_colors(char *op)
8380 {
8381 /* syntax:
8382 * menu white/black message green/yellow status white/blue text
8383 * white/black
8384 */
8385 int j;
8386 char buf[BUFSZ];
8387 char *wn, *tfg, *tbg, *newop;
8388 static const char *wnames[] = { "menu", "message", "status", "text" };
8389 static const char *shortnames[] = { "mnu", "msg", "sts", "txt" };
8390 static char **fgp[] = { &iflags.wc_foregrnd_menu,
8391 &iflags.wc_foregrnd_message,
8392 &iflags.wc_foregrnd_status,
8393 &iflags.wc_foregrnd_text };
8394 static char **bgp[] = { &iflags.wc_backgrnd_menu,
8395 &iflags.wc_backgrnd_message,
8396 &iflags.wc_backgrnd_status,
8397 &iflags.wc_backgrnd_text };
8398
8399 Strcpy(buf, op);
8400 newop = mungspaces(buf);
8401 while (newop && *newop) {
8402 wn = tfg = tbg = (char *) 0;
8403
8404 /* until first non-space in case there's leading spaces - before
8405 * colorname*/
8406 if (*newop == ' ')
8407 newop++;
8408 if (*newop)
8409 wn = newop;
8410 else
8411 return 0;
8412
8413 /* until first space - colorname*/
8414 while (*newop && *newop != ' ')
8415 newop++;
8416 if (*newop)
8417 *newop = '\0';
8418 else
8419 return 0;
8420 newop++;
8421
8422 /* until first non-space - before foreground*/
8423 if (*newop == ' ')
8424 newop++;
8425 if (*newop)
8426 tfg = newop;
8427 else
8428 return 0;
8429
8430 /* until slash - foreground */
8431 while (*newop && *newop != '/')
8432 newop++;
8433 if (*newop)
8434 *newop = '\0';
8435 else
8436 return 0;
8437 newop++;
8438
8439 /* until first non-space (in case there's leading space after slash) -
8440 * before background */
8441 if (*newop == ' ')
8442 newop++;
8443 if (*newop)
8444 tbg = newop;
8445 else
8446 return 0;
8447
8448 /* until first space - background */
8449 while (*newop && *newop != ' ')
8450 newop++;
8451 if (*newop)
8452 *newop++ = '\0';
8453
8454 for (j = 0; j < 4; ++j) {
8455 if (!strcmpi(wn, wnames[j]) || !strcmpi(wn, shortnames[j])) {
8456 if (tfg && !strstri(tfg, " ")) {
8457 if (*fgp[j])
8458 free((genericptr_t) *fgp[j]);
8459 *fgp[j] = dupstr(tfg);
8460 }
8461 if (tbg && !strstri(tbg, " ")) {
8462 if (*bgp[j])
8463 free((genericptr_t) *bgp[j]);
8464 *bgp[j] = dupstr(tbg);
8465 }
8466 break;
8467 }
8468 }
8469 }
8470 return 1;
8471 }
8472
8473 /* set up for wizard mode if player or save file has requested to it;
8474 called from port-specific startup code to handle `nethack -D' or
8475 OPTIONS=playmode:debug, or from dorecover()'s restgamestate() if
8476 restoring a game which was saved in wizard mode */
8477 void
set_playmode(void)8478 set_playmode(void)
8479 {
8480 if (wizard) {
8481 if (authorize_wizard_mode())
8482 g.plnamelen = (int) strlen(strcpy(g.plname, "wizard"));
8483 else
8484 wizard = FALSE; /* not allowed or not available */
8485 /* force explore mode if we didn't make it into wizard mode */
8486 discover = !wizard;
8487 iflags.deferred_X = FALSE;
8488 }
8489 /* don't need to do anything special for explore mode or normal play */
8490 }
8491
8492 #endif /* OPTION_LISTS_ONLY */
8493
8494 /*options.c*/
8495
8496