/* SCCS Id: @(#)options.c 3.3 2000/08/01 */ /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ /* NetHack may be freely redistributed. See license for details. */ #ifdef OPTION_LISTS_ONLY /* (AMIGA) external program for opt lists */ #include "config.h" #include "objclass.h" #include "flag.h" NEARDATA struct flag flags; /* provide linkage */ NEARDATA struct instance_flags iflags; /* provide linkage */ #define static #else #include "hack.h" #include "tcap.h" #include #endif #define WINTYPELEN 16 /* * NOTE: If you add (or delete) an option, please update the short * options help (option_help()), the long options help (dat/opthelp), * and the current options setting display function (doset()), * and also the Guidebooks. */ static struct Bool_Opt { const char *name; boolean *addr, initvalue; } boolopt[] = { #ifdef AMIGA {"altmeta", &flags.altmeta, TRUE}, #else {"altmeta", (boolean *)0, TRUE}, #endif #ifdef MFLOPPY {"asksavedisk", &flags.asksavedisk, FALSE}, #else {"asksavedisk", (boolean *)0, FALSE}, #endif {"autopickup", &flags.pickup, TRUE}, {"autoquiver", &flags.autoquiver, FALSE}, #if defined(MICRO) && !defined(AMIGA) {"BIOS", &iflags.BIOS, FALSE}, #else {"BIOS", (boolean *)0, FALSE}, #endif #ifdef INSURANCE {"checkpoint", &flags.ins_chkpt, TRUE}, #else {"checkpoint", (boolean *)0, FALSE}, #endif #ifdef MFLOPPY {"checkspace", &iflags.checkspace, TRUE}, #else {"checkspace", (boolean *)0, FALSE}, #endif #ifdef TEXTCOLOR # ifdef MICRO {"color", &iflags.use_color, TRUE}, # else /* systems that support multiple terminals, many monochrome */ {"color", &iflags.use_color, FALSE}, # endif #else {"color", (boolean *)0, FALSE}, #endif {"confirm",&flags.confirm, TRUE}, #ifdef TERMLIB {"DECgraphics", &iflags.DECgraphics, FALSE}, #else {"DECgraphics", (boolean *)0, FALSE}, #endif #ifdef TTY_GRAPHICS {"eight_bit_tty", &iflags.eight_bit_tty, FALSE}, {"extmenu", &iflags.extmenu, FALSE}, #else {"eight_bit_tty", (boolean *)0, FALSE}, {"extmenu", (boolean *)0, FALSE}, #endif #ifdef OPT_DISPMAP {"fast_map", &flags.fast_map, TRUE}, #else {"fast_map", (boolean *)0, TRUE}, #endif {"female", &flags.female, FALSE}, {"fixinv", &flags.invlet_constant, TRUE}, #ifdef AMIFLUSH {"flush", &flags.amiflush, FALSE}, #else {"flush", (boolean *)0, FALSE}, #endif {"help", &flags.help, TRUE}, #ifdef TEXTCOLOR {"hilite_pet", &iflags.hilite_pet, FALSE}, #else {"hilite_pet", (boolean *)0, FALSE}, #endif #ifdef ASCIIGRAPH {"IBMgraphics", &iflags.IBMgraphics, FALSE}, #else {"IBMgraphics", (boolean *)0, FALSE}, #endif {"ignintr", &flags.ignintr, FALSE}, #ifdef MAC_GRAPHICS_ENV {"large_font", &iflags.large_font, FALSE}, #else {"large_font", (boolean *)0, FALSE}, #endif {"legacy", &flags.legacy, TRUE}, {"lit_corridor", &flags.lit_corridor, FALSE}, #ifdef MAC_GRAPHICS_ENV {"Macgraphics", &iflags.MACgraphics, TRUE}, #else {"Macgraphics", (boolean *)0, FALSE}, #endif #ifdef MAIL {"mail", &flags.biff, TRUE}, #else {"mail", (boolean *)0, TRUE}, #endif #ifdef NEWS {"news", &iflags.news, TRUE}, #else {"news", (boolean *)0, FALSE}, #endif {"null", &flags.null, TRUE}, {"number_pad", &iflags.num_pad, FALSE}, #ifdef MAC {"page_wait", &flags.page_wait, TRUE}, #else {"page_wait", (boolean *)0, FALSE}, #endif {"perm_invent", &flags.perm_invent, FALSE}, #ifdef MAC {"popup_dialog", &iflags.popup_dialog, FALSE}, #else {"popup_dialog", (boolean *)0, FALSE}, #endif {"prayconfirm", &flags.prayconfirm, TRUE}, #if defined(MSDOS) && defined(USE_TILES) {"preload_tiles", &iflags.preload_tiles, TRUE}, #else {"preload_tiles", (boolean *)0, FALSE}, #endif {"pushweapon", &flags.pushweapon, FALSE}, #if defined(MICRO) && !defined(AMIGA) {"rawio", &iflags.rawio, FALSE}, #else {"rawio", (boolean *)0, FALSE}, #endif {"rest_on_space", &flags.rest_on_space, FALSE}, {"safe_pet", &flags.safe_dog, TRUE}, #ifdef WIZARD {"sanity_check", &iflags.sanity_check, FALSE}, #else {"sanity_check", (boolean *)0, FALSE}, #endif #ifdef EXP_ON_BOTL {"showexp", &flags.showexp, FALSE}, #else {"showexp", (boolean *)0, FALSE}, #endif #ifdef SCORE_ON_BOTL {"showscore", &flags.showscore, FALSE}, #else {"showscore", (boolean *)0, FALSE}, #endif {"silent", &flags.silent, TRUE}, {"sortpack", &flags.sortpack, TRUE}, {"sound", &flags.soundok, TRUE}, {"standout", &flags.standout, FALSE}, {"time", &flags.time, FALSE}, #ifdef TIMED_DELAY {"timed_delay", &flags.nap, TRUE}, #else {"timed_delay", (boolean *)0, FALSE}, #endif {"tombstone",&flags.tombstone, TRUE}, {"toptenwin",&flags.toptenwin, FALSE}, {"verbose", &flags.verbose, TRUE}, {(char *)0, (boolean *)0, FALSE} }; /* compound options, for option_help() and external programs like Amiga * frontend */ #define SET_IN_FILE 0 /* config file option only, not visible in game * or via program */ #define SET_VIA_PROG 1 /* may be set via extern program, not seen in game */ #define DISP_IN_GAME 2 /* may be set via extern program, displayed in game */ #define SET_IN_GAME 3 /* may be set via extern program or set in the game */ static struct Comp_Opt { const char *name, *descr; int size; /* for frontends and such allocating space -- * usually allowed size of data in game, but * occasionally maximum reasonable size for * typing when game maintains information in * a different format */ int optflags; } compopt[] = { { "align", "your starting alignment (lawful, neutral, or chaotic)", 8, DISP_IN_GAME }, #ifdef MAC { "background", "the color of the background (black or white)", 6, SET_IN_FILE }, #endif { "catname", "the name of your (first) cat (e.g., catname:Tabby)", PL_PSIZ, DISP_IN_GAME }, { "disclose", "the kinds of information to disclose at end of game", sizeof(flags.end_disclose), SET_IN_GAME }, { "dogname", "the name of your (first) dog (e.g., dogname:Fang)", PL_PSIZ, DISP_IN_GAME }, { "dungeon", "the symbols to use in drawing the dungeon map", MAXDCHARS+1, SET_IN_FILE }, { "effects", "the symbols to use in drawing special effects", MAXECHARS+1, SET_IN_FILE }, #ifdef MAC { "fontmap", "the font to use in the map window", 40, SET_IN_FILE }, { "fontmessage", "the font to use in the message window", 40, SET_IN_FILE }, { "fonttext", "the font to use in text windows", 40, SET_IN_FILE }, #endif { "fruit", "the name of a fruit you enjoy eating", PL_FSIZ, SET_IN_GAME }, { "gender", "your starting gender (male or female)", 8, DISP_IN_GAME }, { "horsename", "the name of your (first) horse (e.g., horsename:Silver)", PL_PSIZ, DISP_IN_GAME }, { "menustyle", "user interface for object selection", MENUTYPELEN, SET_IN_GAME }, { "menu_deselect_all", "deselect all items in a menu", 4, SET_IN_FILE }, { "menu_deselect_page", "deselect all items on this page of a menu", 4, SET_IN_FILE }, { "menu_first_page", "jump to the first page in a menu", 4, SET_IN_FILE }, { "menu_invert_all", "invert all items in a menu", 4, SET_IN_FILE }, { "menu_invert_page", "invert all items on this page of a menu", 4, SET_IN_FILE }, { "menu_last_page", "jump to the last page in a menu", 4, SET_IN_FILE }, { "menu_next_page", "goto the next menu page", 4, SET_IN_FILE }, { "menu_previous_page", "goto the previous menu page", 4, SET_IN_FILE }, { "menu_search", "search for a menu item", 4, SET_IN_FILE }, { "menu_select_all", "select all items in a menu", 4, SET_IN_FILE }, { "menu_select_page", "select all items on this page of a menu", 4, SET_IN_FILE }, { "monsters", "the symbols to use for monsters", MAXMCLASSES, SET_IN_FILE }, { "msghistory", "number of top line messages to save", 5, DISP_IN_GAME }, { "name", "your character's name (e.g., name:Merlin-W)", PL_NSIZ, DISP_IN_GAME }, { "objects", "the symbols to use for objects", MAXOCLASSES, SET_IN_FILE }, { "packorder", "the inventory order of the items in your pack", MAXOCLASSES, SET_IN_GAME }, #ifdef CHANGE_COLOR { "palette", "palette (00c/880/-fff is blue/yellow/reverse white)", 15 , SET_IN_GAME }, # if defined(MAC) { "hicolor", "same as palette, only order is reversed", 15, SET_IN_FILE }, # endif #endif { "pettype", "your preferred initial pet type", 4, DISP_IN_GAME }, { "pickup_burden", "maximum burden picked up before prompt", 20, SET_IN_GAME }, { "pickup_types", "types of objects to pick up automatically", MAXOCLASSES, SET_IN_GAME }, { "race", "your starting race (e.g., Human, Elf)", PL_CSIZ, DISP_IN_GAME }, { "role", "your starting role (e.g., Barbarian, Valkyrie)", PL_CSIZ, DISP_IN_GAME }, { "scores", "the parts of the score list you wish to see", 32, SET_IN_GAME }, #ifdef MSDOS { "soundcard", "type of sound card to use", 20, SET_IN_FILE }, #endif { "suppress_alert", "suppress alerts about version-specific features", 8, SET_IN_GAME }, { "traps", "the symbols to use in drawing traps", MAXTCHARS+1, SET_IN_FILE }, #ifdef MAC {"use_stone", "use stone background patterns", 8, SET_IN_FILE }, #endif #ifdef MSDOS { "video", "method of video updating", 20, SET_IN_FILE }, #endif #ifdef VIDEOSHADES { "videocolors", "color mappings for internal screen routines", 40, DISP_IN_GAME }, { "videoshades", "gray shades to map to black/gray/white", 32, DISP_IN_GAME }, #endif #if 0 { "warnlevel", "minimum monster level to trigger warning", 4, SET_IN_GAME }, #endif { "windowtype", "windowing system to use", WINTYPELEN, DISP_IN_GAME }, { (char *)0, (char *)0, 0 } }; #ifdef OPTION_LISTS_ONLY #undef static #else /* use rest of file */ static boolean need_redraw; /* for doset() */ #if defined(TOS) && defined(TEXTCOLOR) extern boolean colors_changed; /* in tos.c */ #endif #ifdef VIDEOSHADES extern char *shade[3]; /* in sys/msdos/video.c */ extern char ttycolors[CLR_MAX]; /* in sys/msdos/video.c */ #endif static char def_inv_order[MAXOCLASSES] = { GOLD_CLASS, AMULET_CLASS, WEAPON_CLASS, ARMOR_CLASS, FOOD_CLASS, SCROLL_CLASS, SPBOOK_CLASS, POTION_CLASS, RING_CLASS, WAND_CLASS, TOOL_CLASS, GEM_CLASS, ROCK_CLASS, BALL_CLASS, CHAIN_CLASS, 0, }; /* * Default menu manipulation command accelerators. These may _not_ be: * * + a number - reserved for counts * + an upper or lower case US ASCII letter - used for accelerators * + ESC - reserved for escaping the menu * + NULL, CR or LF - reserved for commiting the selection(s). NULL * is kind of odd, but the tty's xwaitforspace() will return it if * someone hits a . * + a default object class symbol - used for object class accelerators * * Standard letters (for now) are: * * < back 1 page * > forward 1 page * ^ first page * | last page * : search * * page all * , select . * \ deselect - * ~ invert @ * * The command name list is duplicated in the compopt array. */ typedef struct { const char *name; char cmd; } menu_cmd_t; #define NUM_MENU_CMDS 11 static const menu_cmd_t default_menu_cmd_info[NUM_MENU_CMDS] = { /* 0*/ { "menu_first_page", MENU_FIRST_PAGE }, { "menu_last_page", MENU_LAST_PAGE }, { "menu_next_page", MENU_NEXT_PAGE }, { "menu_previous_page", MENU_PREVIOUS_PAGE }, { "menu_select_all", MENU_SELECT_ALL }, /* 5*/ { "menu_deselect_all", MENU_UNSELECT_ALL }, { "menu_invert_all", MENU_INVERT_ALL }, { "menu_select_page", MENU_SELECT_PAGE }, { "menu_deselect_page", MENU_UNSELECT_PAGE }, { "menu_invert_page", MENU_INVERT_PAGE }, /*10*/ { "menu_search", MENU_SEARCH }, }; /* * Allow the user to map incoming characters to various menu commands. * The accelerator list must be a valid C string. */ #define MAX_MENU_MAPPED_CMDS 32 /* some number */ char mapped_menu_cmds[MAX_MENU_MAPPED_CMDS+1]; /* exported */ static char mapped_menu_op[MAX_MENU_MAPPED_CMDS+1]; static short n_menu_mapped = 0; static boolean initial, from_file; STATIC_DCL void FDECL(doset_add_menu, (winid,const char *,int)); STATIC_DCL void FDECL(nmcpy, (char *, const char *, int)); STATIC_DCL void FDECL(escapes, (const char *, char *)); STATIC_DCL int FDECL(boolopt_only_initial, (int)); STATIC_DCL void FDECL(rejectoption, (const char *)); STATIC_DCL void FDECL(badoption, (const char *)); STATIC_DCL char *FDECL(string_for_opt, (char *,BOOLEAN_P)); STATIC_DCL char *FDECL(string_for_env_opt, (const char *, char *,BOOLEAN_P)); STATIC_DCL void FDECL(bad_negation, (const char *,BOOLEAN_P)); STATIC_DCL int FDECL(change_inv_order, (char *)); STATIC_DCL void FDECL(oc_to_str, (char *, char *)); STATIC_DCL void FDECL(graphics_opts, (char *,const char *,int,int)); STATIC_DCL int FDECL(feature_alert_opts, (char *, const char *)); STATIC_DCL const char *FDECL(get_compopt_value, (const char *, char *)); STATIC_DCL boolean FDECL(special_handling, (const char *, BOOLEAN_P, BOOLEAN_P)); STATIC_DCL void FDECL(warning_opts, (char *,const char *)); #if 0 STATIC_DCL int FDECL(warnlevel_opts, (char *, const char *)); #endif /* check whether a user-supplied option string is a proper leading substring of a particular option name; option string might have a colon or equals sign and arbitrary value appended to it */ boolean match_optname(user_string, opt_name, min_length, val_allowed) const char *user_string, *opt_name; int min_length; boolean val_allowed; { int len = (int)strlen(user_string); if (val_allowed) { const char *p = index(user_string, ':'), *q = index(user_string, '='); if (!p || (q && q < p)) p = q; while(p && p > user_string && isspace(*(p-1))) p--; if (p) len = (int)(p - user_string); } return (len >= min_length) && !strncmpi(opt_name, user_string, len); } /* most environment variables will eventually be printed in an error * message if they don't work, and most error message paths go through * BUFSZ buffers, which could be overflowed by a maliciously long * environment variable. if a variable can legitimately be long, or * if it's put in a smaller buffer, the responsible code will have to * bounds-check itself. */ char * nh_getenv(ev) const char *ev; { char *getev = getenv(ev); if (getev && strlen(getev) <= (BUFSZ / 2)) return getev; else return (char *)0; } void initoptions() { char *opts; int i; /* initialize the random number generator */ setrandom(); for (i = 0; boolopt[i].name; i++) { if (boolopt[i].addr) *(boolopt[i].addr) = boolopt[i].initvalue; } flags.end_own = FALSE; flags.end_top = 3; flags.end_around = 2; iflags.msg_history = 20; /* Use negative indices to indicate not yet selected */ flags.initrole = -1; flags.initrace = -1; flags.initgend = -1; flags.initalign = -1; /* Set the default monster and object class symbols. Don't use */ /* memcpy() --- sizeof char != sizeof uchar on some machines. */ for (i = 0; i < MAXOCLASSES; i++) oc_syms[i] = (uchar) def_oc_syms[i]; for (i = 0; i < MAXMCLASSES; i++) monsyms[i] = (uchar) def_monsyms[i]; for (i = 0; i < WARNCOUNT; i++) warnsyms[i] = def_warnsyms[i].sym; flags.warnlevel = 1; flags.warntype = 0L; /* assert( sizeof flags.inv_order == sizeof def_inv_order ); */ (void)memcpy((genericptr_t)flags.inv_order, (genericptr_t)def_inv_order, sizeof flags.inv_order); flags.pickup_types[0] = '\0'; flags.pickup_burden = MOD_ENCUMBER; switch_graphics(ASCII_GRAPHICS); /* set default characters */ #if defined(UNIX) && defined(TTY_GRAPHICS) /* * Set defaults for some options depending on what we can * detect about the environment's capabilities. * This has to be done after the global initialization above * and before reading user-specific initialization via * config file/environment variable below. */ /* this detects the IBM-compatible console on most 386 boxes */ if (!strncmp(nh_getenv("TERM"), "AT", 2)) { switch_graphics(IBM_GRAPHICS); # ifdef TEXTCOLOR iflags.use_color = TRUE; # endif } #endif /* UNIX && TTY_GRAPHICS */ #if defined(UNIX) || defined(VMS) # ifdef TTY_GRAPHICS /* detect whether a "vt" terminal can handle alternate charsets */ if (!strncmpi(nh_getenv("TERM"), "vt", 2) && (AS && AE) && index(AS, '\016') && index(AE, '\017')) { switch_graphics(DEC_GRAPHICS); } # endif #endif /* UNIX || VMS */ #ifdef MAC_GRAPHICS_ENV switch_graphics(MAC_GRAPHICS); #endif /* MAC_GRAPHICS_ENV */ flags.menu_style = MENU_FULL; /* since this is done before init_objects(), do partial init here */ objects[SLIME_MOLD].oc_name_idx = SLIME_MOLD; nmcpy(pl_fruit, OBJ_NAME(objects[SLIME_MOLD]), PL_FSIZ); #ifndef MAC opts = getenv("NETHACKOPTIONS"); if (!opts) opts = getenv("HACKOPTIONS"); if (opts) { if (*opts == '/' || *opts == '\\' || *opts == '@') { if (*opts == '@') opts++; /* @filename */ /* looks like a filename */ if (strlen(opts) < BUFSZ/2) read_config_file(opts); } else { read_config_file((char *)0); /* let the total length of options be long; * parseoptions() will check each individually */ parseoptions(opts, TRUE, FALSE); } } else #endif read_config_file((char *)0); #ifdef AMIGA ami_wbench_init(); /* must be here or can't set fruit */ #endif (void)fruitadd(pl_fruit); /* Remove "slime mold" from list of object names; this will */ /* prevent it from being wished unless it's actually present */ /* as a named (or default) fruit. Wishing for "fruit" will */ /* result in the player's preferred fruit [better than "\033"]. */ obj_descr[SLIME_MOLD].oc_name = "fruit"; return; } STATIC_OVL void nmcpy(dest, src, maxlen) char *dest; const char *src; int maxlen; { int count; for(count = 1; count < maxlen; count++) { if(*src == ',' || *src == '\0') break; /*exit on \0 terminator*/ *dest++ = *src++; } *dest = 0; } /* * escapes: escape expansion for showsyms. C-style escapes understood include * \n, \b, \t, \r, \xnnn (hex), \onnn (octal), \nnn (decimal). The ^-prefix * for control characters is also understood, and \[mM] followed by any of the * previous forms or by a character has the effect of 'meta'-ing the value (so * that the alternate character set will be enabled). */ STATIC_OVL void escapes(cp, tp) const char *cp; char *tp; { while (*cp) { int cval = 0, meta = 0; if (*cp == '\\' && index("mM", cp[1])) { meta = 1; cp += 2; } if (*cp == '\\' && index("0123456789xXoO", cp[1])) { const char *dp, *hex = "00112233445566778899aAbBcCdDeEfF"; int dcount = 0; cp++; if (*cp == 'x' || *cp == 'X') for (++cp; (dp = index(hex, *cp)) && (dcount++ < 2); cp++) cval = (cval * 16) + (dp - hex) / 2; else if (*cp == 'o' || *cp == 'O') for (++cp; (index("01234567",*cp)) && (dcount++ < 3); cp++) cval = (cval * 8) + (*cp - '0'); else for (; (index("0123456789",*cp)) && (dcount++ < 3); cp++) cval = (cval * 10) + (*cp - '0'); } else if (*cp == '\\') /* C-style character escapes */ { switch (*++cp) { case '\\': cval = '\\'; break; case 'n': cval = '\n'; break; case 't': cval = '\t'; break; case 'b': cval = '\b'; break; case 'r': cval = '\r'; break; default: cval = *cp; } cp++; } else if (*cp == '^') /* expand control-character syntax */ { cval = (*++cp & 0x1f); cp++; } else cval = *cp++; if (meta) cval |= 0x80; *tp++ = cval; } *tp = '\0'; } /* some boolean options can only be set on start-up */ STATIC_OVL int boolopt_only_initial(i) int i; { return (boolopt[i].addr == &flags.female || boolopt[i].addr == &flags.legacy #if defined(MICRO) && !defined(AMIGA) || boolopt[i].addr == &iflags.rawio || boolopt[i].addr == &iflags.BIOS #endif #if defined(MSDOS) && defined(USE_TILES) || boolopt[i].addr == &iflags.preload_tiles #endif ); } STATIC_OVL void rejectoption(optname) const char *optname; { #ifdef MICRO # ifdef AMIGA if(FromWBench){ pline("\"%s\" settable only from %s or in icon.", optname, configfile); } else # endif pline("\"%s\" settable only from %s.", optname, configfile); #else pline("%s can be set only from NETHACKOPTIONS or %s.", optname, configfile); #endif } STATIC_OVL void badoption(opts) const char *opts; { if (!initial) { if (!strncmp(opts, "h", 1) || !strncmp(opts, "?", 1)) option_help(); else pline("Bad syntax: %s. Enter \"?g\" for help.", opts); return; } #ifdef MAC else return; #endif # ifdef AMIGA if(ami_wbench_badopt(opts)) { # endif if(from_file) raw_printf("Bad syntax in OPTIONS in %s: %s.", configfile, opts); else raw_printf("Bad syntax in NETHACKOPTIONS: %s.", opts); # ifdef AMIGA } # endif wait_synch(); } STATIC_OVL char * string_for_opt(opts, val_optional) char *opts; boolean val_optional; { char *colon, *equals; colon = index(opts, ':'); equals = index(opts, '='); if (!colon || (equals && equals < colon)) colon = equals; if (!colon || !*++colon) { if (!val_optional) badoption(opts); return (char *)0; } return colon; } STATIC_OVL char * string_for_env_opt(optname, opts, val_optional) const char *optname; char *opts; boolean val_optional; { if(!initial) { rejectoption(optname); return (char *)0; } return string_for_opt(opts, val_optional); } STATIC_OVL void bad_negation(optname, with_parameter) const char *optname; boolean with_parameter; { pline_The("%s option may not %sbe negated.", optname, with_parameter ? "both have a value and " : ""); } /* * Change the inventory order, using the given string as the new order. * Missing characters in the new order are filled in at the end from * the current inv_order, except for gold, which is forced to be first * if not explicitly present. * * This routine returns 1 unless there is a duplicate or bad char in * the string. */ STATIC_OVL int change_inv_order(op) char *op; { int oc_sym, num; char *sp, buf[BUFSZ]; num = 0; if (!index(op, GOLD_SYM)) buf[num++] = GOLD_CLASS; for (sp = op; *sp; sp++) { oc_sym = def_char_to_objclass(*sp); /* reject bad or duplicate entries */ if (oc_sym == MAXOCLASSES || oc_sym == RANDOM_CLASS || oc_sym == ILLOBJ_CLASS || !index(flags.inv_order, oc_sym) || index(sp+1, *sp)) return 0; /* retain good ones */ buf[num++] = (char) oc_sym; } buf[num] = '\0'; /* fill in any omitted classes, using previous ordering */ for (sp = flags.inv_order; *sp; sp++) if (!index(buf, *sp)) { buf[num++] = *sp; buf[num] = '\0'; /* explicitly terminate for next index() */ } Strcpy(flags.inv_order, buf); return 1; } STATIC_OVL void graphics_opts(opts, optype, maxlen, offset) register char *opts; const char *optype; int maxlen, offset; { uchar translate[MAXPCHARS+1]; int length, i; if (!(opts = string_for_env_opt(optype, opts, FALSE))) return; escapes(opts, opts); length = strlen(opts); if (length > maxlen) length = maxlen; /* match the form obtained from PC configuration files */ for (i = 0; i < length; i++) translate[i] = (uchar) opts[i]; assign_graphics(translate, length, maxlen, offset); } STATIC_OVL void warning_opts(opts, optype) register char *opts; const char *optype; { uchar translate[MAXPCHARS+1]; int length, i; if (!(opts = string_for_env_opt(optype, opts, FALSE))) return; escapes(opts, opts); length = strlen(opts); if (length > WARNCOUNT) length = WARNCOUNT; /* match the form obtained from PC configuration files */ for (i = 0; i < length; i++) translate[i] = (((i < WARNCOUNT) && opts[i]) ? (uchar) opts[i] : def_warnsyms[i].sym); assign_warnings(translate); } void assign_warnings(graph_chars) register uchar *graph_chars; { int i; for (i = 0; i < WARNCOUNT; i++) warnsyms[i] = graph_chars[i]; } #if 0 /* warnlevel is unnecessary with the new warning introduced in 3.3.1 */ STATIC_OVL int warnlevel_opts(op, optn) char *op; const char *optn; { char buf[BUFSZ]; int twarnlevel; boolean rejectlevel = FALSE; if (op) { twarnlevel = atoi(op); if (twarnlevel >= WARNCOUNT || twarnlevel < 1) rejectlevel = TRUE; else { flags.warnlevel = twarnlevel; see_monsters(); } } if (rejectlevel) { if (!initial) pline("warnlevel must be 1 to %d.", WARNCOUNT - 1); else { Sprintf(buf, "\n%s=%s Invalid warnlevel ignored (must be 1 to %d)", optn, op, WARNCOUNT - 1); badoption(buf); } return 0; } if (!initial) { if (flags.warnlevel < WARNCOUNT -1) Sprintf(buf, "s %d to %d", flags.warnlevel, WARNCOUNT - 1); else Sprintf(buf, " %d", flags.warnlevel); pline("Warning level%s will be displayed.", buf); } return 1; } #endif STATIC_OVL int feature_alert_opts(op, optn) char *op; const char *optn; { char buf[BUFSZ]; boolean rejectver = FALSE; unsigned long fnv = get_feature_notice_ver(op); /* version.c */ if (fnv == 0L) return 0; if (fnv > get_current_feature_ver()) rejectver = TRUE; else flags.suppress_alert = fnv; if (rejectver) { if (!initial) You_cant("disable new feature alerts for future versions."); else { Sprintf(buf, "\n%s=%s Invalid reference to a future version ignored", optn, op); badoption(buf); } return 0; } if (!initial) { Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ, FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH); pline("Feature change alerts disabled for NetHack %s features and prior.", buf); } return 1; } void parseoptions(opts, tinitial, tfrom_file) register char *opts; boolean tinitial, tfrom_file; { register char *op; unsigned num; boolean negated; int i; const char *fullname; initial = tinitial; from_file = tfrom_file; if ((op = index(opts, ',')) != 0) { *op++ = 0; parseoptions(op, initial, from_file); } if (strlen(opts) > BUFSZ/2) { badoption("option too long"); return; } /* strip leading and trailing white space */ while (isspace(*opts)) opts++; op = eos(opts); while (--op >= opts && isspace(*op)) *op = '\0'; if (!*opts) return; negated = FALSE; while ((*opts == '!') || !strncmpi(opts, "no", 2)) { if (*opts == '!') opts++; else opts += 2; negated = !negated; } /* variant spelling */ if (match_optname(opts, "colour", 5, FALSE)) Strcpy(opts, "color"); /* fortunately this isn't longer */ /* special boolean options */ if (match_optname(opts, "female", 3, FALSE)) { if(!initial && flags.female == negated) pline("That is not anatomically possible."); else flags.initgend = flags.female = !negated; return; } if (match_optname(opts, "male", 4, FALSE)) { if(!initial && flags.female != negated) pline("That is not anatomically possible."); else flags.initgend = flags.female = negated; return; } #if defined(MICRO) && !defined(AMIGA) /* included for compatibility with old NetHack.cnf files */ if (match_optname(opts, "IBM_", 4, FALSE)) { iflags.BIOS = !negated; return; } #endif /* MICRO */ /* compound options */ fullname = "pettype"; if (match_optname(opts, fullname, 3, TRUE)) { if ((op = string_for_env_opt(fullname, opts, negated)) != 0) { if (negated) bad_negation(fullname, TRUE); else switch (*op) { case 'd': /* dog */ case 'D': preferred_pet = 'd'; break; case 'c': /* cat */ case 'C': case 'f': /* feline */ case 'F': preferred_pet = 'c'; break; default: pline("Unrecognized pet type '%s'", op); break; } } else if (negated) preferred_pet = 0; return; } fullname = "catname"; if (match_optname(opts, fullname, 3, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) nmcpy(catname, op, PL_PSIZ); return; } fullname = "dogname"; if (match_optname(opts, fullname, 3, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) nmcpy(dogname, op, PL_PSIZ); return; } fullname = "horsename"; if (match_optname(opts, fullname, 5, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) nmcpy(horsename, op, PL_PSIZ); return; } fullname = "msghistory"; if (match_optname(opts, fullname, 3, TRUE)) { op = string_for_env_opt(fullname, opts, negated); if ((negated && !op) || (!negated && op)) { iflags.msg_history = negated ? 0 : atoi(op); } else if (negated) bad_negation(fullname, TRUE); return; } #ifdef CHANGE_COLOR #ifdef MAC fullname = "use_stone"; if (match_optname(opts, fullname, 6, TRUE)) { op = string_for_env_opt(fullname, opts, negated); if ((negated && !op) || (!negated && op)) { iflags.use_stone = negated ? 0 : atoi(op); } else if (negated) bad_negation(fullname, TRUE); return; } fullname = "background"; if (match_optname(opts, fullname, 5,TRUE)) { if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { if (!strncmpi (op, "white", 5)) change_background (1); else if (!strncmpi (op, "black", 5)) change_background (0); } return; } fullname = "font"; if (!strncmpi(opts, fullname, 4)) { int wintype = -1; opts += 4; if (!strncmpi (opts, "map", 3)) wintype = NHW_MAP; else if (!strncmpi (opts, "message", 7)) wintype = NHW_MESSAGE; else if (!strncmpi (opts, "text", 4)) wintype = NHW_TEXT; if (wintype > 0 && (op = string_for_env_opt(fullname, opts, FALSE)) != 0) { set_font_name (wintype, op); } return; } #endif if (match_optname(opts, "palette", 3, TRUE) # ifdef MAC || match_optname(opts, "hicolor", 3, TRUE) # endif ) { int color_number, color_incr; # ifdef MAC if (match_optname(opts, "hicolor", 3, TRUE)) { if (negated) { bad_negation("hicolor", FALSE); return; } color_number = CLR_MAX + 4; /* HARDCODED inverse number */ color_incr = -1; } else { # endif if (negated) { bad_negation("palette", FALSE); return; } color_number = 0; color_incr = 1; # ifdef MAC } # endif if ((op = string_for_opt(opts, FALSE)) != (char *)0) { char *pt = op; int cnt, tmp, reverse; long rgb; while (*pt && color_number >= 0) { cnt = 3; rgb = 0L; if (*pt == '-') { reverse = 1; pt++; } else { reverse = 0; } while (cnt-- > 0) { if (*pt && *pt != '/') { # ifdef AMIGA rgb <<= 4; # else rgb <<= 8; # endif tmp = *(pt++); if (isalpha(tmp)) { tmp = (tmp + 9) & 0xf; /* Assumes ASCII... */ } else { tmp &= 0xf; /* Digits in ASCII too... */ } # ifndef AMIGA /* Add an extra so we fill f -> ff and 0 -> 00 */ rgb += tmp << 4; # endif rgb += tmp; } } if (*pt == '/') { pt++; } change_color(color_number, rgb, reverse); color_number += color_incr; } } if (!initial) { need_redraw = TRUE; } return; } #endif if (match_optname(opts, "fruit", 2, TRUE)) { char empty_str = '\0'; op = string_for_opt(opts, negated); if (negated) { if (op) { bad_negation("fruit", TRUE); return; } op = &empty_str; goto goodfruit; } if (!op) return; if (!initial) { struct fruit *f; num = 0; for(f=ffruit; f; f=f->nextf) { if (!strcmp(op, f->fname)) goto goodfruit; num++; } if (num >= 100) { pline("Doing that so many times isn't very fruitful."); return; } } goodfruit: nmcpy(pl_fruit, op, PL_FSIZ); /* OBJ_NAME(objects[SLIME_MOLD]) won't work after initialization */ if (!*pl_fruit) nmcpy(pl_fruit, "slime mold", PL_FSIZ); if (!initial) (void)fruitadd(pl_fruit); /* If initial, then initoptions is allowed to do it instead * of here (initoptions always has to do it even if there's * no fruit option at all. Also, we don't want people * setting multiple fruits in their options.) */ return; } /* graphics:string */ fullname = "graphics"; if (match_optname(opts, fullname, 2, TRUE)) { if (negated) bad_negation(fullname, FALSE); else graphics_opts(opts, fullname, MAXPCHARS, 0); return; } fullname = "dungeon"; if (match_optname(opts, fullname, 2, TRUE)) { if (negated) bad_negation(fullname, FALSE); else graphics_opts(opts, fullname, MAXDCHARS, 0); return; } fullname = "traps"; if (match_optname(opts, fullname, 2, TRUE)) { if (negated) bad_negation(fullname, FALSE); else graphics_opts(opts, fullname, MAXTCHARS, MAXDCHARS); return; } fullname = "effects"; if (match_optname(opts, fullname, 2, TRUE)) { if (negated) bad_negation(fullname, FALSE); else graphics_opts(opts, fullname, MAXECHARS, MAXDCHARS+MAXTCHARS); return; } /* objects:string */ fullname = "objects"; if (match_optname(opts, fullname, 7, TRUE)) { int length; if (negated) { bad_negation(fullname, FALSE); return; } if (!(opts = string_for_env_opt(fullname, opts, FALSE))) return; escapes(opts, opts); /* * Override the default object class symbols. The first * object in the object class is the "random object". I * don't want to use 0 as an object class, so the "random * object" is basically a place holder. * * The object class symbols have already been initialized in * initoptions(). */ length = strlen(opts); if (length >= MAXOCLASSES) length = MAXOCLASSES-1; /* don't count RANDOM_OBJECT */ for (i = 0; i < length; i++) oc_syms[i+1] = (uchar) opts[i]; return; } /* monsters:string */ fullname = "monsters"; if (match_optname(opts, fullname, 8, TRUE)) { int length; if (negated) { bad_negation(fullname, FALSE); return; } if (!(opts = string_for_env_opt(fullname, opts, FALSE))) return; escapes(opts, opts); /* Override default mon class symbols set in initoptions(). */ length = strlen(opts); if (length >= MAXMCLASSES) length = MAXMCLASSES-1; /* mon class 0 unused */ for (i = 0; i < length; i++) monsyms[i+1] = (uchar) opts[i]; return; } fullname = "warnings"; if (match_optname(opts, fullname, 5, TRUE)) { if (negated) bad_negation(fullname, FALSE); else warning_opts(opts, fullname); return; } #if 0 fullname = "warnlevel"; if (match_optname(opts, fullname, 5, TRUE)) { op = string_for_opt(opts, negated); if (negated) bad_negation(fullname, FALSE); else if (op) (void) warnlevel_opts(op,fullname); return; } #endif /* name:string */ fullname = "name"; if (match_optname(opts, fullname, 4, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) nmcpy(plname, op, PL_NSIZ); return; } /* role:string or character:string */ fullname = "role"; if (match_optname(opts, fullname, 4, TRUE) || match_optname(opts, (fullname = "character"), 4, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { if ((flags.initrole = str2role(op)) == ROLE_NONE) badoption(opts); else /* Backwards compatibility */ nmcpy(pl_character, op, PL_NSIZ); } return; } /* race:string */ fullname = "race"; if (match_optname(opts, fullname, 4, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { if ((flags.initrace = str2race(op)) == ROLE_NONE) badoption(opts); else /* Backwards compatibility */ pl_race = *op; } return; } /* gender:string */ fullname = "gender"; if (match_optname(opts, fullname, 4, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { if ((flags.initgend = str2gend(op)) == ROLE_NONE) badoption(opts); else flags.female = flags.initgend; } return; } /* align:string */ fullname = "align"; if (match_optname(opts, fullname, 4, TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) if ((flags.initalign = str2align(op)) == ROLE_NONE) badoption(opts); return; } /* the order to list the pack */ fullname = "packorder"; if (match_optname(opts, fullname, 4, TRUE)) { if (negated) { bad_negation(fullname, FALSE); return; } else if (!(op = string_for_opt(opts, FALSE))) return; if (!change_inv_order(op)) badoption(opts); return; } /* maximum burden picked up before prompt (Warren Cheung) */ fullname = "pickup_burden"; if (match_optname(opts, fullname, 8, TRUE)) { if (negated) { bad_negation(fullname, FALSE); return; } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { switch (tolower(*op)) { /* Unencumbered */ case 'u': flags.pickup_burden = UNENCUMBERED; break; /* Burdened (slight encumberance) */ case 'b': flags.pickup_burden = SLT_ENCUMBER; break; /* streSsed (moderate encumberance) */ case 's': flags.pickup_burden = MOD_ENCUMBER; break; /* straiNed (heavy encumberance) */ case 'n': flags.pickup_burden = HVY_ENCUMBER; break; /* OverTaxed (extreme encumberance) */ case 'o': case 't': flags.pickup_burden = EXT_ENCUMBER; break; /* overLoaded */ case 'l': flags.pickup_burden = OVERLOADED; break; default: badoption(opts); } } return; } /* types of objects to pick up automatically */ if (match_optname(opts, "pickup_types", 8, TRUE)) { char ocl[MAXOCLASSES + 1], tbuf[MAXOCLASSES + 1], qbuf[QBUFSZ], abuf[BUFSZ]; int oc_sym; boolean badopt = FALSE, compat = (strlen(opts) <= 6), use_menu; oc_to_str(flags.pickup_types, tbuf); flags.pickup_types[0] = '\0'; /* all */ op = string_for_opt(opts, (compat || !initial)); if (!op) { if (compat || negated || initial) { /* for backwards compatibility, "pickup" without a value is a synonym for autopickup of all types (and during initialization, we can't prompt yet) */ flags.pickup = !negated; return; } oc_to_str(flags.inv_order, ocl); use_menu = TRUE; if (flags.menu_style == MENU_TRADITIONAL || flags.menu_style == MENU_COMBINATION) { use_menu = FALSE; Sprintf(qbuf, "New pickup_types: [%s am] (%s)", ocl, *tbuf ? tbuf : "all"); getlin(qbuf, abuf); op = mungspaces(abuf); if (abuf[0] == '\0' || abuf[0] == '\033') op = tbuf; /* restore */ else if (abuf[0] == 'm') use_menu = TRUE; } if (use_menu) { (void) choose_classes_menu("Auto-Pickup what?", 1, TRUE, ocl, tbuf); op = tbuf; } } if (negated) { bad_negation("pickup_types", TRUE); return; } while (*op == ' ') op++; if (*op != 'a' && *op != 'A') { num = 0; while (*op) { oc_sym = def_char_to_objclass(*op); /* make sure all are valid obj symbols occuring once */ if (oc_sym != MAXOCLASSES && !index(flags.pickup_types, oc_sym)) { flags.pickup_types[num] = (char)oc_sym; flags.pickup_types[++num] = '\0'; } else badopt = TRUE; op++; } if (badopt) badoption(opts); } return; } /* things to disclose at end of game */ if (match_optname(opts, "disclose", 4, TRUE)) { flags.end_disclose[0] = '\0'; /* all */ if (!(op = string_for_opt(opts, TRUE))) { /* for backwards compatibility, "disclose" without a * value means all (was inventory and attributes, * the only things available then), but negated * it means "none" * (note "none" contains none of "iavkgc") */ if (negated) Strcpy(flags.end_disclose, "none"); return; } if (negated) { bad_negation("disclose", TRUE); return; } num = 0; while (*op && num < sizeof flags.end_disclose - 1) { register char c; c = lowc(*op); if (c == 'k') c = 'v'; /* killed -> vanquished */ if (!index(flags.end_disclose, c)) { flags.end_disclose[num++] = c; flags.end_disclose[num] = '\0'; /* for index */ } op++; } return; } /* scores:5t[op] 5a[round] o[wn] */ if (match_optname(opts, "scores", 4, TRUE)) { if (negated) { bad_negation("scores", FALSE); return; } if (!(op = string_for_opt(opts, FALSE))) return; while (*op) { int inum = 1; if (digit(*op)) { inum = atoi(op); while (digit(*op)) op++; } else if (*op == '!') { negated = !negated; op++; } while (*op == ' ') op++; switch (*op) { case 't': case 'T': flags.end_top = inum; break; case 'a': case 'A': flags.end_around = inum; break; case 'o': case 'O': flags.end_own = !negated; break; default: badoption(opts); return; } while (letter(*++op) || *op == ' ') continue; if (*op == '/') op++; } return; } fullname = "suppress_alert"; if (match_optname(opts, fullname, 4, TRUE)) { op = string_for_opt(opts, negated); if (negated) bad_negation(fullname, FALSE); else if (op) (void) feature_alert_opts(op,fullname); return; } #ifdef VIDEOSHADES /* videocolors:string */ fullname = "videocolors"; if (match_optname(opts, fullname, 6, TRUE) || match_optname(opts, "videocolours", 10, TRUE)) { if (negated) { bad_negation(fullname, FALSE); return; } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { return; } if (!assign_videocolors(opts)) badoption(opts); return; } /* videoshades:string */ fullname = "videoshades"; if (match_optname(opts, fullname, 6, TRUE)) { if (negated) { bad_negation(fullname, FALSE); return; } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { return; } if (!assign_videoshades(opts)) badoption(opts); return; } #endif /* VIDEOSHADES */ #ifdef MSDOS # ifdef NO_TERMS /* video:string -- must be after longer tests */ fullname = "video"; if (match_optname(opts, fullname, 5, TRUE)) { if (negated) { bad_negation(fullname, FALSE); return; } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { return; } if (!assign_video(opts)) badoption(opts); return; } # endif /* NO_TERMS */ /* soundcard:string -- careful not to match boolean 'sound' */ fullname = "soundcard"; if (match_optname(opts, fullname, 6, TRUE)) { if (negated) { bad_negation(fullname, FALSE); return; } else if (!(opts = string_for_env_opt(fullname, opts, FALSE))) { return; } if (!assign_soundcard(opts)) badoption(opts); return; } #endif /* MSDOS */ fullname = "windowtype"; if (match_optname(opts, fullname, 3, TRUE)) { if (negated) { bad_negation(fullname, FALSE); return; } else if ((op = string_for_env_opt(fullname, opts, FALSE)) != 0) { char buf[WINTYPELEN]; nmcpy(buf, op, WINTYPELEN); choose_windows(buf); } return; } /* menustyle:traditional or combo or full or partial */ if (match_optname(opts, "menustyle", 4, TRUE)) { int tmp; boolean val_required = (strlen(opts) > 5 && !negated); if (!(op = string_for_opt(opts, !val_required))) { if (val_required) return; /* string_for_opt gave feedback */ tmp = negated ? 'n' : 'f'; } else { tmp = tolower(*op); } switch (tmp) { case 'n': /* none */ case 't': /* traditional */ flags.menu_style = MENU_TRADITIONAL; break; case 'c': /* combo: trad.class sel+menu */ flags.menu_style = MENU_COMBINATION; break; case 'p': /* partial: no class menu */ flags.menu_style = MENU_PARTIAL; break; case 'f': /* full: class menu + menu */ flags.menu_style = MENU_FULL; break; default: badoption(opts); } return; } /* check for menu command mapping */ for (i = 0; i < NUM_MENU_CMDS; i++) { fullname = default_menu_cmd_info[i].name; if (match_optname(opts, fullname, (int)strlen(fullname), TRUE)) { if (negated) bad_negation(fullname, FALSE); else if ((op = string_for_opt(opts, FALSE)) != 0) { int j; char c, op_buf[BUFSZ]; boolean isbad = FALSE; escapes(op, op_buf); c = *op_buf; if (c == 0 || c == '\r' || c == '\n' || c == '\033' || c == ' ' || digit(c) || (letter(c) && c != '@')) isbad = TRUE; else /* reject default object class symbols */ for (j = 1; j < MAXOCLASSES; j++) if (c == def_oc_syms[i]) { isbad = TRUE; break; } if (isbad) badoption(opts); else add_menu_cmd_alias(c, default_menu_cmd_info[i].cmd); } return; } } /* OK, if we still haven't recognized the option, check the boolean * options list */ for (i = 0; boolopt[i].name; i++) { if (match_optname(opts, boolopt[i].name, 3, FALSE)) { /* options that don't exist */ if (!boolopt[i].addr) { if (!initial && !negated) pline_The("\"%s\" option is not available.", boolopt[i].name); return; } /* options that must come from config file */ if (!initial && boolopt_only_initial(i)) { rejectoption(boolopt[i].name); return; } *(boolopt[i].addr) = !negated; #if defined(TERMLIB) || defined(ASCIIGRAPH) || defined(MAC_GRAPHICS_ENV) if (FALSE # ifdef TERMLIB || (boolopt[i].addr) == &iflags.DECgraphics # endif # ifdef ASCIIGRAPH || (boolopt[i].addr) == &iflags.IBMgraphics # endif # ifdef MAC_GRAPHICS_ENV || (boolopt[i].addr) == &iflags.MACgraphics # endif ) { # ifdef REINCARNATION if (!initial && Is_rogue_level(&u.uz)) assign_rogue_graphics(FALSE); # endif need_redraw = TRUE; # ifdef TERMLIB if ((boolopt[i].addr) == &iflags.DECgraphics) switch_graphics(iflags.DECgraphics ? DEC_GRAPHICS : ASCII_GRAPHICS); # endif # ifdef ASCIIGRAPH if ((boolopt[i].addr) == &iflags.IBMgraphics) switch_graphics(iflags.IBMgraphics ? IBM_GRAPHICS : ASCII_GRAPHICS); # endif # ifdef MAC_GRAPHICS_ENV if ((boolopt[i].addr) == &iflags.MACgraphics) switch_graphics(iflags.MACgraphics ? MAC_GRAPHICS : ASCII_GRAPHICS); # endif # ifdef REINCARNATION if (!initial && Is_rogue_level(&u.uz)) assign_rogue_graphics(TRUE); # endif } #endif /* TERMLIB || ASCIIGRAPH || MAC_GRAPHICS_ENV */ /* only do processing below if setting with doset() */ if (initial) return; if ((boolopt[i].addr) == &flags.time #ifdef EXP_ON_BOTL || (boolopt[i].addr) == &flags.showexp #endif #ifdef SCORE_ON_BOTL || (boolopt[i].addr) == &flags.showscore #endif ) flags.botl = TRUE; else if ((boolopt[i].addr) == &flags.invlet_constant) { if (flags.invlet_constant) reassign(); } #ifdef LAN_MAIL else if ((boolopt[i].addr) == &flags.biff) { if (flags.biff) lan_mail_init(); else lan_mail_finish(); } #endif else if ((boolopt[i].addr) == &iflags.num_pad) number_pad(iflags.num_pad ? 1 : 0); else if ((boolopt[i].addr) == &flags.lit_corridor) { /* * All corridor squares seen via night vision or * candles & lamps change. Update them by calling * newsym() on them. Don't do this if we are * initializing the options --- the vision system * isn't set up yet. */ vision_recalc(2); /* shut down vision */ vision_full_recalc = 1; /* delayed recalc */ } #ifdef TEXTCOLOR else if ((boolopt[i].addr) == &iflags.use_color || (boolopt[i].addr) == &iflags.hilite_pet) { need_redraw = TRUE; # ifdef TOS if ((boolopt[i].addr) == &iflags.use_color && iflags.BIOS) { if (colors_changed) restore_colors(); else set_colors(); } # endif } #endif return; } } /* out of valid options */ badoption(opts); } static NEARDATA const char *menutype[] = { "traditional", "combination", "partial", "full" }; static NEARDATA const char *burdentype[] = { "unencumbered", "burdened", "stressed", "strained", "overtaxed", "overloaded" }; /* * Convert the given string of object classes to a string of default object * symbols. */ STATIC_OVL void oc_to_str(src,dest) char *src, *dest; { int i; while ((i = (int) *src++) != 0) { if (i < 0 || i >= MAXOCLASSES) impossible("oc_to_str: illegal object class %d", i); else *dest++ = def_oc_syms[i]; } *dest = '\0'; } /* * Add the given mapping to the menu command map list. Always keep the * maps valid C strings. */ void add_menu_cmd_alias(from_ch, to_ch) char from_ch, to_ch; { if (n_menu_mapped >= MAX_MENU_MAPPED_CMDS) pline("out of menu map space"); else { mapped_menu_cmds[n_menu_mapped] = from_ch; mapped_menu_op[n_menu_mapped] = to_ch; n_menu_mapped++; mapped_menu_cmds[n_menu_mapped] = 0; mapped_menu_op[n_menu_mapped] = 0; } } /* * Map the given character to its corresponding menu command. If it * doesn't match anything, just return the original. */ char map_menu_cmd(ch) char ch; { char *found = index(mapped_menu_cmds, ch); if (found) { int idx = found - mapped_menu_cmds; ch = mapped_menu_op[idx]; } return ch; } #if defined(MICRO) || defined(MAC) # define OPTIONS_HEADING "OPTIONS" #else # define OPTIONS_HEADING "NETHACKOPTIONS" #endif static char fmtstr_doset_add_menu[] = "%s%-15s [%s] "; STATIC_OVL void doset_add_menu(win, option, indexoffset) winid win; /* window to add to */ const char *option; /* option name */ int indexoffset; /* value to add to index in compopt[], or zero if option cannot be changed */ { const char *value = "unknown"; /* current value */ char buf[BUFSZ], buf2[BUFSZ]; anything any; int i; any.a_void = 0; if (indexoffset == 0) { any.a_int = 0; value = get_compopt_value(option, buf2); } else { for (i=0; compopt[i].name; i++) if (strcmp(option, compopt[i].name) == 0) break; if (compopt[i].name) { any.a_int = i + 1 + indexoffset; value = get_compopt_value(option, buf2); } else { /* We are trying to add an option not found in compopt[]. This is almost certainly bad, but we'll let it through anyway (with a zero value, so it can't be selected). */ any.a_int = 0; } } /* " " replaces "a - " -- assumes menus follow that style */ Sprintf(buf, fmtstr_doset_add_menu, (any.a_int ? "" : " "), option, value); add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); } /* Changing options via menu by Per Liboriussen */ int doset() { char buf[BUFSZ], buf2[BUFSZ]; int i, pass, boolcount, pick_cnt, pick_idx, opt_indx; boolean *bool_p; winid tmpwin; anything any; menu_item *pick_list; int indexoffset, startpass, endpass; boolean setinitial = FALSE, fromfile = FALSE; int biggest_name = 0; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); any.a_void = 0; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "Booleans (selecting will toggle value):", MENU_UNSELECTED); any.a_int = 0; /* first list any other non-modifiable booleans, then modifiable ones */ for (pass = 0; pass <= 1; pass++) for (i = 0; boolopt[i].name; i++) if ((bool_p = boolopt[i].addr) != 0 && (boolopt_only_initial(i) ^ pass)) { if (bool_p == &flags.female) continue; /* obsolete */ #ifdef WIZARD if (bool_p == &iflags.sanity_check && !wizard) continue; #endif any.a_int = (pass == 0) ? 0 : i + 1; Sprintf(buf, "%s%-13s [%s]", pass == 0 ? " " : "", boolopt[i].name, *bool_p ? "true" : "false"); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, buf, MENU_UNSELECTED); } boolcount = i; indexoffset = boolcount; any.a_void = 0; add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "Compounds (selecting will prompt for new value):", MENU_UNSELECTED); startpass = DISP_IN_GAME; endpass = SET_IN_GAME; /* spin through the options to find the biggest name and adjust the format string accordingly if needed */ biggest_name = 0; for (i = 0; compopt[i].name; i++) if (compopt[i].optflags >= startpass && compopt[i].optflags <= endpass && strlen(compopt[i].name) > (unsigned) biggest_name) biggest_name = (int) strlen(compopt[i].name); if (biggest_name > 30) biggest_name = 30; Sprintf(fmtstr_doset_add_menu, "%%s%%-%ds [%%s]", biggest_name); /* deliberately put `name', `role', `race', `gender' first */ doset_add_menu(tmpwin, "name", 0); doset_add_menu(tmpwin, "role", 0); doset_add_menu(tmpwin, "race", 0); doset_add_menu(tmpwin, "gender", 0); for (pass = startpass; pass <= endpass; pass++) for (i = 0; compopt[i].name; i++) if (compopt[i].optflags == pass) { if (!strcmp(compopt[i].name, "name") || !strcmp(compopt[i].name, "role") || !strcmp(compopt[i].name, "race") || !strcmp(compopt[i].name, "gender")) continue; else doset_add_menu(tmpwin, compopt[i].name, (pass == DISP_IN_GAME) ? 0 : indexoffset); } #ifdef PREFIXES_IN_USE add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "", MENU_UNSELECTED); add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, "Variable playground locations:", MENU_UNSELECTED); for (i = 0; i < PREFIX_COUNT; i++) doset_add_menu(tmpwin, fqn_prefix_names[i], 0); #endif end_menu(tmpwin, "Set what options?"); need_redraw = FALSE; if ((pick_cnt = select_menu(tmpwin, PICK_ANY, &pick_list)) > 0) { /* * Walk down the selection list and either invert the booleans * or prompt for new values. In most cases, call parseoptions() * to take care of options that require special attention, like * redraws. */ for (pick_idx = 0; pick_idx < pick_cnt; ++pick_idx) { opt_indx = pick_list[pick_idx].item.a_int - 1; if (opt_indx < boolcount) { /* boolean option */ Sprintf(buf, "%s%s", *boolopt[opt_indx].addr ? "!" : "", boolopt[opt_indx].name); parseoptions(buf, setinitial, fromfile); } else { /* compound option */ opt_indx -= boolcount; if (!special_handling(compopt[opt_indx].name, setinitial, fromfile)) { Sprintf(buf, "Set %s to what?", compopt[opt_indx].name); getlin(buf, buf2); Sprintf(buf, "%s:%s", compopt[opt_indx].name, buf2); /* pass the buck */ parseoptions(buf, setinitial, fromfile); } } } free((genericptr_t)pick_list); pick_list = (menu_item *)0; } destroy_nhwindow(tmpwin); if (need_redraw) (void) doredraw(); return 0; } STATIC_OVL boolean special_handling(optname, setinitial, setfromfile) const char *optname; boolean setinitial,setfromfile; { winid tmpwin; anything any; int i; char buf[BUFSZ]; boolean retval = FALSE; /* Special handling of menustyle, pickup_burden, and pickup_types. */ if (!strcmp("menustyle", optname)) { const char *style_name; menu_item *style_pick = (menu_item *)0; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); for (i = 0; i < SIZE(menutype); i++) { style_name = menutype[i]; /* note: separate `style_name' variable used to avoid an optimizer bug in VAX C V2.3 */ any.a_int = i + 1; add_menu(tmpwin, NO_GLYPH, &any, *style_name, 0, ATR_NONE, style_name, MENU_UNSELECTED); } end_menu(tmpwin, "Select menustyle:"); if (select_menu(tmpwin, PICK_ONE, &style_pick) > 0) { flags.menu_style = style_pick->item.a_int - 1; free((genericptr_t)style_pick); } destroy_nhwindow(tmpwin); retval = TRUE; } else if (!strcmp("pickup_burden", optname)) { const char *burden_name, *burden_letters = "ubsntl"; menu_item *burden_pick = (menu_item *)0; tmpwin = create_nhwindow(NHW_MENU); start_menu(tmpwin); for (i = 0; i < SIZE(burdentype); i++) { burden_name = burdentype[i]; any.a_int = i + 1; add_menu(tmpwin, NO_GLYPH, &any, burden_letters[i], 0, ATR_NONE, burden_name, MENU_UNSELECTED); } end_menu(tmpwin, "Select encumberence level:"); if (select_menu(tmpwin, PICK_ONE, &burden_pick) > 0) { flags.pickup_burden = burden_pick->item.a_int - 1; free((genericptr_t)burden_pick); } destroy_nhwindow(tmpwin); retval = TRUE; } else if (!strcmp("pickup_types", optname)) { /* parseoptions will prompt for the list of types */ parseoptions(strcpy(buf, "pickup_types"), setinitial, setfromfile); retval = TRUE; } return retval; } #define rolestring(val,array,field) ((val >= 0) ? array[val].field : \ (val == ROLE_RANDOM) ? randomrole : none) /* This is ugly. We have all the option names in the compopt[] array, but we need to look at each option individually to get the value. */ STATIC_OVL const char * get_compopt_value(optname, buf) const char *optname; char *buf; { char ocl[MAXOCLASSES+1]; static const char none[] = "(none)", randomrole[] = "random", to_be_done[] = "(to be done)"; #ifdef PREFIXES_IN_USE int i; #endif buf[0] = '\0'; if (!strcmp(optname,"align")) Sprintf(buf, "%s", rolestring(flags.initalign, aligns, adj)); else if (!strcmp(optname, "catname")) Sprintf(buf, "%s", catname[0] ? catname : none ); else if (!strcmp(optname, "disclose")) Sprintf(buf, "%s", flags.end_disclose[0] ? flags.end_disclose : "all" ); else if (!strcmp(optname, "dogname")) Sprintf(buf, "%s", dogname[0] ? dogname : none ); else if (!strcmp(optname, "dungeon")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "effects")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "fruit")) Sprintf(buf, "%s", pl_fruit); else if (!strcmp(optname, "gender")) Sprintf(buf, "%s", rolestring(flags.initgend, genders, adj)); else if (!strcmp(optname, "horsename")) Sprintf(buf, "%s", horsename[0] ? horsename : none); else if (!strcmp(optname, "menustyle")) Sprintf(buf, "%s", menutype[(int)flags.menu_style] ); else if (!strcmp(optname, "menu_deselect_all")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_deselect_page")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_first_page")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_invert_all")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_invert_page")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_last_page")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_next_page")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_previous_page")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_search")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_select_all")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "menu_select_page")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "monsters")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "msghistory")) Sprintf(buf, "%u", iflags.msg_history); else if (!strcmp(optname, "name")) Sprintf(buf, "%s", plname); else if (!strcmp(optname, "objects")) Sprintf(buf, "%s", to_be_done); else if (!strcmp(optname, "packorder")) { oc_to_str(flags.inv_order, ocl); Sprintf(buf, "%s", ocl); } #ifdef CHANGE_COLOR else if (!strcmp(optname, "palette")) Sprintf(buf, "%s", get_color_string()); #endif else if (!strcmp(optname, "pettype")) Sprintf(buf, "%s", (preferred_pet == 'c') ? "cat" : (preferred_pet == 'd') ? "dog" : "random" ); else if (!strcmp(optname, "pickup_burden")) Sprintf(buf, "%s", burdentype[flags.pickup_burden] ); else if (!strcmp(optname, "pickup_types")) { oc_to_str(flags.pickup_types, ocl); Sprintf(buf, "%s", ocl[0] ? ocl : "all" ); } else if (!strcmp(optname, "race")) Sprintf(buf, "%s", rolestring(flags.initrace, races, noun)); else if (!strcmp(optname, "role")) Sprintf(buf, "%s", rolestring(flags.initrole, roles, name.m)); else if (!strcmp(optname, "scores")) { Sprintf(buf, "%d top/%d around%s", flags.end_top, flags.end_around, flags.end_own ? "/own" : ""); } #ifdef MSDOS else if (!strcmp(optname, "soundcard")) Sprintf(buf, "%s", to_be_done); #endif else if (!strcmp(optname, "suppress_alert")) { if (flags.suppress_alert == 0L) Strcpy(buf, none); else Sprintf(buf, "%lu.%lu.%lu", FEATURE_NOTICE_VER_MAJ, FEATURE_NOTICE_VER_MIN, FEATURE_NOTICE_VER_PATCH); } else if (!strcmp(optname, "traps")) Sprintf(buf, "%s", to_be_done); #ifdef MSDOS else if (!strcmp(optname, "video")) Sprintf(buf, "%s", to_be_done); #endif #ifdef VIDEOSHADES else if (!strcmp(optname, "videoshades")) Sprintf(buf, "%s-%s-%s", shade[0],shade[1],shade[2]); else if (!strcmp(optname, "videocolors")) Sprintf(buf, "%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d-%d", ttycolors[CLR_RED], ttycolors[CLR_GREEN], ttycolors[CLR_BROWN], ttycolors[CLR_BLUE], ttycolors[CLR_MAGENTA], ttycolors[CLR_CYAN], ttycolors[CLR_ORANGE], ttycolors[CLR_BRIGHT_GREEN], ttycolors[CLR_YELLOW], ttycolors[CLR_BRIGHT_BLUE], ttycolors[CLR_BRIGHT_MAGENTA], ttycolors[CLR_BRIGHT_CYAN]); #endif /* VIDEOSHADES */ #if 0 else if (!strcmp(optname, "warnlevel")) Sprintf(buf, "%d", flags.warnlevel); #endif else if (!strcmp(optname, "windowtype")) Sprintf(buf, "%s", windowprocs.name); #ifdef PREFIXES_IN_USE else { for (i = 0; i < PREFIX_COUNT; ++i) if (!strcmp(optname, fqn_prefix_names[i]) && fqn_prefix[i]) Sprintf(buf, "%s", fqn_prefix[i]); } #endif if (buf[0]) return buf; else return "unknown"; } int dotogglepickup() { char buf[BUFSZ], ocl[MAXOCLASSES+1]; flags.pickup = !flags.pickup; if (flags.pickup) { oc_to_str(flags.pickup_types, ocl); Sprintf(buf, "ON, for %s objects", ocl[0] ? ocl : "all"); } else { Strcpy(buf, "OFF"); } pline("Autopickup: %s.", buf); return 0; } /* data for option_help() */ static const char *opt_intro[] = { "", " NetHack Options Help:", "", #define CONFIG_SLOT 3 /* fill in next value at run-time */ (char *)0, #if !defined(MICRO) && !defined(MAC) "or use `NETHACKOPTIONS=\"\"' in your environment", #endif "( is a list of options separated by commas)", #ifdef VMS "-- for example, $ DEFINE NETHACKOPTIONS \"noautopickup,fruit:kumquat\"", #endif "or press \"O\" while playing and use the menu.", "", "Boolean options (which can be negated by prefixing them with '!' or \"no\"):", (char *)0 }; static const char *opt_epilog[] = { "", "Some of the options can be set only before the game is started; those", "items will not be selectable in the 'O' command's menu.", (char *)0 }; void option_help() { char buf[BUFSZ], buf2[BUFSZ]; register int i; winid datawin; datawin = create_nhwindow(NHW_TEXT); #ifdef AMIGA if (FromWBench) Sprintf(buf, "Set options as OPTIONS= in %s or in icon", configfile); else #endif Sprintf(buf, "Set options as OPTIONS= in %s", configfile); opt_intro[CONFIG_SLOT] = (const char *) buf; for (i = 0; opt_intro[i]; i++) putstr(datawin, 0, opt_intro[i]); /* Boolean options */ for (i = 0; boolopt[i].name; i++) { if (boolopt[i].addr) { #ifdef WIZARD if (boolopt[i].addr == &iflags.sanity_check && !wizard) continue; #endif next_opt(datawin, boolopt[i].name); } } next_opt(datawin, ""); /* Compound options */ putstr(datawin, 0, "Compound options:"); for (i = 0; compopt[i].name; i++) { Sprintf(buf2, "`%s'", compopt[i].name); Sprintf(buf, "%-20s - %s%c", buf2, compopt[i].descr, compopt[i+1].name ? ',' : '.'); putstr(datawin, 0, buf); } for (i = 0; opt_epilog[i]; i++) putstr(datawin, 0, opt_epilog[i]); display_nhwindow(datawin, FALSE); destroy_nhwindow(datawin); return; } /* * prints the next boolean option, on the same line if possible, on a new * line if not. End with next_opt(""). */ void next_opt(datawin, str) winid datawin; const char *str; { static char *buf = 0; int i; char *s; if (!buf) *(buf = (char *)alloc(BUFSZ)) = '\0'; if (!*str) { s = eos(buf); if (s > &buf[1] && s[-2] == ',') Strcpy(s - 2, "."); /* replace last ", " */ i = COLNO; /* (greater than COLNO - 2) */ } else { i = strlen(buf) + strlen(str) + 2; } if (i > COLNO - 2) { /* rule of thumb */ putstr(datawin, 0, buf); buf[0] = 0; } if (*str) { Strcat(buf, str); Strcat(buf, ", "); } else { putstr(datawin, 0, str); free(buf), buf = 0; } return; } /* Returns the fid of the fruit type; if that type already exists, it * returns the fid of that one; if it does not exist, it adds a new fruit * type to the chain and returns the new one. */ int fruitadd(str) char *str; { register int i; register struct fruit *f; struct fruit *lastf = 0; int highest_fruit_id = 0; char buf[PL_FSIZ]; boolean user_specified = (str == pl_fruit); /* if not user-specified, then it's a fruit name for a fruit on * a bones level... */ /* Note: every fruit has an id (spe for fruit objects) of at least * 1; 0 is an error. */ if (user_specified) { /* disallow naming after other foods (since it'd be impossible * to tell the difference) */ boolean found = FALSE, numeric = FALSE; for (i = bases[FOOD_CLASS]; objects[i].oc_class == FOOD_CLASS; i++) { if (!strcmp(OBJ_NAME(objects[i]), pl_fruit)) { found = TRUE; break; } } { char *c; c = pl_fruit; for(c = pl_fruit; *c >= '0' && *c <= '9'; c++) ; if (isspace(*c) || *c == 0) numeric = TRUE; } if (found || numeric || !strncmp(str, "cursed ", 7) || !strncmp(str, "uncursed ", 9) || !strncmp(str, "blessed ", 8) || !strncmp(str, "partly eaten ", 13) || (!strncmp(str, "tin of ", 7) && (!strcmp(str+7, "spinach") || name_to_mon(str+7) >= LOW_PM)) || !strcmp(str, "empty tin") || ((!strncmp(eos(str)-7," corpse",7) || !strncmp(eos(str)-4, " egg",4)) && name_to_mon(str) >= LOW_PM)) { Strcpy(buf, pl_fruit); Strcpy(pl_fruit, "candied "); nmcpy(pl_fruit+8, buf, PL_FSIZ-8); } } for(f=ffruit; f; f = f->nextf) { lastf = f; if(f->fid > highest_fruit_id) highest_fruit_id = f->fid; if(!strncmp(str, f->fname, PL_FSIZ)) goto nonew; } /* if adding another fruit would overflow spe, use a random fruit instead... we've got a lot to choose from. */ if (highest_fruit_id >= 127) return rnd(127); highest_fruit_id++; f = newfruit(); if (ffruit) lastf->nextf = f; else ffruit = f; Strcpy(f->fname, str); f->fid = highest_fruit_id; f->nextf = 0; nonew: if (user_specified) current_fruit = highest_fruit_id; return f->fid; } /* * This is a somewhat generic menu for taking a list of NetHack style * class choices and presenting them via a description * rather than the traditional NetHack characters. * (Benefits users whose first exposure to NetHack is via tiles). * * prompt * The title at the top of the menu. * * category: 0 = monster class * 1 = object class * * way * FALSE = PICK_ONE, TRUE = PICK_ANY * * class_list * a null terminated string containing the list of choices. * * class_selection * a null terminated string containing the selected characters. * * Returns number selected. */ int choose_classes_menu(prompt, category, way, class_list, class_select) const char *prompt; int category; boolean way; char *class_list; char *class_select; { menu_item *pick_list = (menu_item *)0; winid win; anything any; char buf[BUFSZ]; int i, n; int ret; int next_accelerator, accelerator; if (class_list == (char *)0 || class_select == (char *)0) return 0; accelerator = 0; next_accelerator = 'a'; any.a_void = 0; win = create_nhwindow(NHW_MENU); start_menu(win); while (*class_list) { const char *text; boolean selected; text = (char *)0; selected = FALSE; switch (category) { case 0: text = monexplain[def_char_to_monclass(*class_list)]; accelerator = *class_list; Sprintf(buf, "%s", text); break; case 1: text = objexplain[def_char_to_objclass(*class_list)]; accelerator = next_accelerator; Sprintf(buf, "%c %s", *class_list, text); break; default: impossible("choose_classes_menu: invalid category %d", category); } if (way && *class_select) { /* Selections there already */ if (index(class_select, *class_list)) { selected = TRUE; } } any.a_int = *class_list; add_menu(win, NO_GLYPH, &any, accelerator, category ? *class_list : 0, ATR_NONE, buf, selected); ++class_list; if (category > 0) { ++next_accelerator; if (next_accelerator == ('z' + 1)) next_accelerator = 'A'; if (next_accelerator == ('Z' + 1)) break; } } end_menu(win, prompt); n = select_menu(win, way ? PICK_ANY : PICK_ONE, &pick_list); destroy_nhwindow(win); if (n > 0) { for (i = 0; i < n; ++i) *class_select++ = (char)pick_list[i].item.a_int; free((genericptr_t)pick_list); ret = n; } else if (n == -1) { class_select = eos(class_select); ret = -1; } else ret = 0; *class_select = '\0'; return ret; } #endif /* OPTION_LISTS_ONLY */ /*options.c*/