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, &paranoia_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