option parsing buffer overflow vulnerability
[aNetHack.git] / src / pager.c
blob3e95336b0823871354503b0a52affc1d1f8a2c9e
1 /* NetHack 3.6 pager.c $NHDT-Date: 1455674291 2016/02/17 01:58:11 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.93 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* This file contains the command routines dowhatis() and dohelp() and */
6 /* a few other help related facilities */
8 #include "hack.h"
9 #include "dlb.h"
11 STATIC_DCL boolean FDECL(is_swallow_sym, (int));
12 STATIC_DCL int FDECL(append_str, (char *, const char *));
13 STATIC_DCL void FDECL(look_at_object, (char *, int, int, int));
14 STATIC_DCL void FDECL(look_at_monster, (char *, char *,
15 struct monst *, int, int));
16 STATIC_DCL struct permonst *FDECL(lookat, (int, int, char *, char *));
17 STATIC_DCL void FDECL(checkfile, (char *, struct permonst *,
18 BOOLEAN_P, BOOLEAN_P));
19 STATIC_DCL void FDECL(look_all, (BOOLEAN_P,BOOLEAN_P));
20 STATIC_DCL boolean FDECL(help_menu, (int *));
21 STATIC_DCL void NDECL(docontact);
22 #ifdef PORT_HELP
23 extern void NDECL(port_help);
24 #endif
26 /* Returns "true" for characters that could represent a monster's stomach. */
27 STATIC_OVL boolean
28 is_swallow_sym(c)
29 int c;
31 int i;
33 for (i = S_sw_tl; i <= S_sw_br; i++)
34 if ((int) showsyms[i] == c)
35 return TRUE;
36 return FALSE;
40 * Append new_str to the end of buf if new_str doesn't already exist as
41 * a substring of buf. Return 1 if the string was appended, 0 otherwise.
42 * It is expected that buf is of size BUFSZ.
44 STATIC_OVL int
45 append_str(buf, new_str)
46 char *buf;
47 const char *new_str;
49 int space_left; /* space remaining in buf */
51 if (strstri(buf, new_str))
52 return 0;
54 space_left = BUFSZ - strlen(buf) - 1;
55 (void) strncat(buf, " or ", space_left);
56 (void) strncat(buf, new_str, space_left - 4);
57 return 1;
60 /* shared by monster probing (via query_objlist!) as well as lookat() */
61 char *
62 self_lookat(outbuf)
63 char *outbuf;
65 char race[QBUFSZ];
67 /* include race with role unless polymorphed */
68 race[0] = '\0';
69 if (!Upolyd)
70 Sprintf(race, "%s ", urace.adj);
71 Sprintf(outbuf, "%s%s%s called %s",
72 /* being blinded may hide invisibility from self */
73 (Invis && (senseself() || !Blind)) ? "invisible " : "", race,
74 mons[u.umonnum].mname, plname);
75 if (u.usteed)
76 Sprintf(eos(outbuf), ", mounted on %s", y_monnam(u.usteed));
77 return outbuf;
80 /* describe a hidden monster; used for look_at during extended monster
81 detection and for probing */
82 void
83 mhidden_description(mon, altmon, outbuf)
84 struct monst *mon;
85 boolean altmon; /* for probing: if mimicking a monster, say so */
86 char *outbuf;
88 struct obj *otmp;
89 boolean fakeobj;
90 int x = mon->mx, y = mon->my, glyph = levl[x][y].glyph;
92 *outbuf = '\0';
93 if (mon->m_ap_type == M_AP_FURNITURE
94 || mon->m_ap_type == M_AP_OBJECT) {
95 Strcpy(outbuf, ", mimicking ");
96 if (mon->m_ap_type == M_AP_FURNITURE) {
97 Strcat(outbuf, an(defsyms[mon->mappearance].explanation));
98 } else if (mon->m_ap_type == M_AP_OBJECT
99 /* remembered glyph, not glyph_at() which is 'mon' */
100 && glyph_is_object(glyph)) {
101 objfrommap:
102 otmp = (struct obj *) 0;
103 fakeobj = object_from_map(glyph, x, y, &otmp);
104 Strcat(outbuf, (otmp && otmp->otyp != STRANGE_OBJECT)
105 ? ansimpleoname(otmp)
106 : an(obj_descr[STRANGE_OBJECT].oc_name));
107 if (fakeobj)
108 dealloc_obj(otmp);
109 } else {
110 Strcat(outbuf, something);
112 } else if (mon->m_ap_type == M_AP_MONSTER) {
113 if (altmon)
114 Sprintf(outbuf, ", masquerading as %s",
115 an(mons[mon->mappearance].mname));
116 } else if (mon->mundetected) {
117 Strcpy(outbuf, ", hiding");
118 if (hides_under(mon->data)) {
119 Strcat(outbuf, " under ");
120 /* remembered glyph, not glyph_at() which is 'mon' */
121 if (glyph_is_object(glyph))
122 goto objfrommap;
123 Strcat(outbuf, something);
124 } else if (is_hider(mon->data)) {
125 Sprintf(eos(outbuf), " on the %s",
126 (is_flyer(mon->data) || mon->data->mlet == S_PIERCER)
127 ? "ceiling"
128 : surface(x, y)); /* trapper */
129 } else {
130 if (mon->data->mlet == S_EEL && is_pool(x, y))
131 Strcat(outbuf, " in murky water");
136 /* extracted from lookat(); also used by namefloorobj() */
137 boolean
138 object_from_map(glyph, x, y, obj_p)
139 int glyph, x, y;
140 struct obj **obj_p;
142 boolean fakeobj = FALSE;
143 struct monst *mtmp;
144 struct obj *otmp = vobj_at(x, y);
145 int glyphotyp = glyph_to_obj(glyph);
147 *obj_p = (struct obj *) 0;
148 /* there might be a mimic here posing as an object */
149 mtmp = m_at(x, y);
150 if (mtmp && is_obj_mappear(mtmp, (unsigned) glyphotyp))
151 otmp = 0;
152 else
153 mtmp = 0;
155 if (!otmp || otmp->otyp != glyphotyp) {
156 /* this used to exclude STRANGE_OBJECT; now caller deals with it */
157 otmp = mksobj(glyphotyp, FALSE, FALSE);
158 if (!otmp)
159 return FALSE;
160 fakeobj = TRUE;
161 if (otmp->oclass == COIN_CLASS)
162 otmp->quan = 2L; /* to force pluralization */
163 else if (otmp->otyp == SLIME_MOLD)
164 otmp->spe = context.current_fruit; /* give it a type */
165 if (mtmp && has_mcorpsenm(mtmp)) /* mimic as corpse/statue */
166 otmp->corpsenm = MCORPSENM(mtmp);
168 /* if located at adjacent spot, mark it as having been seen up close */
169 if (otmp && distu(x, y) <= 2 && !Blind && !Hallucination)
170 otmp->dknown = 1;
172 *obj_p = otmp;
173 return fakeobj; /* when True, caller needs to dealloc *obj_p */
176 STATIC_OVL void
177 look_at_object(buf, x, y, glyph)
178 char *buf; /* output buffer */
179 int x, y, glyph;
181 struct obj *otmp = 0;
182 boolean fakeobj = object_from_map(glyph, x, y, &otmp);
184 if (otmp) {
185 Strcpy(buf, (otmp->otyp != STRANGE_OBJECT)
186 ? distant_name(otmp, doname)
187 : obj_descr[STRANGE_OBJECT].oc_name);
188 if (fakeobj)
189 dealloc_obj(otmp), otmp = 0;
190 } else
191 Strcpy(buf, something); /* sanity precaution */
193 if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR)
194 Strcat(buf, " embedded in stone");
195 else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR)
196 Strcat(buf, " embedded in a wall");
197 else if (closed_door(x, y))
198 Strcat(buf, " embedded in a door");
199 else if (is_pool(x, y))
200 Strcat(buf, " in water");
201 else if (is_lava(x, y))
202 Strcat(buf, " in molten lava"); /* [can this ever happen?] */
203 return;
206 STATIC_OVL void
207 look_at_monster(buf, monbuf, mtmp, x, y)
208 char *buf, *monbuf; /* buf: output, monbuf: optional output */
209 struct monst *mtmp;
210 int x, y;
212 char *name, monnambuf[BUFSZ];
213 boolean accurate = !Hallucination;
215 name = (mtmp->data == &mons[PM_COYOTE] && accurate)
216 ? coyotename(mtmp, monnambuf)
217 : distant_monnam(mtmp, ARTICLE_NONE, monnambuf);
218 Sprintf(buf, "%s%s%s",
219 (mtmp->mx != x || mtmp->my != y)
220 ? ((mtmp->isshk && accurate) ? "tail of " : "tail of a ")
221 : "",
222 (mtmp->mtame && accurate)
223 ? "tame "
224 : (mtmp->mpeaceful && accurate)
225 ? "peaceful "
226 : "",
227 name);
228 if (u.ustuck == mtmp)
229 Strcat(buf, (Upolyd && sticks(youmonst.data))
230 ? ", being held" : ", holding you");
231 if (mtmp->mleashed)
232 Strcat(buf, ", leashed to you");
234 if (mtmp->mtrapped && cansee(mtmp->mx, mtmp->my)) {
235 struct trap *t = t_at(mtmp->mx, mtmp->my);
236 int tt = t ? t->ttyp : NO_TRAP;
238 /* newsym lets you know of the trap, so mention it here */
239 if (tt == BEAR_TRAP || tt == PIT || tt == SPIKED_PIT || tt == WEB)
240 Sprintf(eos(buf), ", trapped in %s",
241 an(defsyms[trap_to_defsym(tt)].explanation));
244 /* we know the hero sees a monster at this location, but if it's shown
245 due to persistant monster detection he might remember something else */
246 if (mtmp->mundetected || mtmp->m_ap_type)
247 mhidden_description(mtmp, FALSE, eos(buf));
249 if (monbuf) {
250 unsigned how_seen = howmonseen(mtmp);
252 monbuf[0] = '\0';
253 if (how_seen != 0 && how_seen != MONSEEN_NORMAL) {
254 if (how_seen & MONSEEN_NORMAL) {
255 Strcat(monbuf, "normal vision");
256 how_seen &= ~MONSEEN_NORMAL;
257 /* how_seen can't be 0 yet... */
258 if (how_seen)
259 Strcat(monbuf, ", ");
261 if (how_seen & MONSEEN_SEEINVIS) {
262 Strcat(monbuf, "see invisible");
263 how_seen &= ~MONSEEN_SEEINVIS;
264 if (how_seen)
265 Strcat(monbuf, ", ");
267 if (how_seen & MONSEEN_INFRAVIS) {
268 Strcat(monbuf, "infravision");
269 how_seen &= ~MONSEEN_INFRAVIS;
270 if (how_seen)
271 Strcat(monbuf, ", ");
273 if (how_seen & MONSEEN_TELEPAT) {
274 Strcat(monbuf, "telepathy");
275 how_seen &= ~MONSEEN_TELEPAT;
276 if (how_seen)
277 Strcat(monbuf, ", ");
279 if (how_seen & MONSEEN_XRAYVIS) {
280 /* Eyes of the Overworld */
281 Strcat(monbuf, "astral vision");
282 how_seen &= ~MONSEEN_XRAYVIS;
283 if (how_seen)
284 Strcat(monbuf, ", ");
286 if (how_seen & MONSEEN_DETECT) {
287 Strcat(monbuf, "monster detection");
288 how_seen &= ~MONSEEN_DETECT;
289 if (how_seen)
290 Strcat(monbuf, ", ");
292 if (how_seen & MONSEEN_WARNMON) {
293 if (Hallucination)
294 Strcat(monbuf, "paranoid delusion");
295 else
296 Sprintf(eos(monbuf), "warned of %s",
297 makeplural(mtmp->data->mname));
298 how_seen &= ~MONSEEN_WARNMON;
299 if (how_seen)
300 Strcat(monbuf, ", ");
302 /* should have used up all the how_seen bits by now */
303 if (how_seen) {
304 impossible("lookat: unknown method of seeing monster");
305 Sprintf(eos(monbuf), "(%u)", how_seen);
307 } /* seen by something other than normal vision */
308 } /* monbuf is non-null */
312 * Return the name of the glyph found at (x,y).
313 * If not hallucinating and the glyph is a monster, also monster data.
315 STATIC_OVL struct permonst *
316 lookat(x, y, buf, monbuf)
317 int x, y;
318 char *buf, *monbuf;
320 struct monst *mtmp = (struct monst *) 0;
321 struct permonst *pm = (struct permonst *) 0;
322 int glyph;
324 buf[0] = monbuf[0] = '\0';
325 glyph = glyph_at(x, y);
326 if (u.ux == x && u.uy == y && canspotself()) {
327 /* fill in buf[] */
328 (void) self_lookat(buf);
330 /* file lookup can't distinguish between "gnomish wizard" monster
331 and correspondingly named player character, always picking the
332 former; force it to find the general "wizard" entry instead */
333 if (Role_if(PM_WIZARD) && Race_if(PM_GNOME) && !Upolyd)
334 pm = &mons[PM_WIZARD];
336 /* When you see yourself normally, no explanation is appended
337 (even if you could also see yourself via other means).
338 Sensing self while blind or swallowed is treated as if it
339 were by normal vision (cf canseeself()). */
340 if ((Invisible || u.uundetected) && !Blind && !u.uswallow) {
341 unsigned how = 0;
343 if (Infravision)
344 how |= 1;
345 if (Unblind_telepat)
346 how |= 2;
347 if (Detect_monsters)
348 how |= 4;
350 if (how)
351 Sprintf(
352 eos(buf), " [seen: %s%s%s%s%s]",
353 (how & 1) ? "infravision" : "",
354 /* add comma if telep and infrav */
355 ((how & 3) > 2) ? ", " : "", (how & 2) ? "telepathy" : "",
356 /* add comma if detect and (infrav or telep or both) */
357 ((how & 7) > 4) ? ", " : "",
358 (how & 4) ? "monster detection" : "");
360 } else if (u.uswallow) {
361 /* all locations when swallowed other than the hero are the monster */
362 Sprintf(buf, "interior of %s",
363 Blind ? "a monster" : a_monnam(u.ustuck));
364 pm = u.ustuck->data;
365 } else if (glyph_is_monster(glyph)) {
366 bhitpos.x = x;
367 bhitpos.y = y;
368 if ((mtmp = m_at(x, y)) != 0) {
369 look_at_monster(buf, monbuf, mtmp, x, y);
370 pm = mtmp->data;
372 } else if (glyph_is_object(glyph)) {
373 look_at_object(buf, x, y, glyph); /* fill in buf[] */
374 } else if (glyph_is_trap(glyph)) {
375 int tnum = what_trap(glyph_to_trap(glyph));
377 Strcpy(buf, defsyms[trap_to_defsym(tnum)].explanation);
378 } else if (glyph_is_warning(glyph)) {
379 int warnindx = glyph_to_warning(glyph);
380 Strcpy(buf, def_warnsyms[warnindx].explanation);
381 } else if (!glyph_is_cmap(glyph)) {
382 Strcpy(buf, "unexplored area");
383 } else
384 switch (glyph_to_cmap(glyph)) {
385 case S_altar:
386 Sprintf(buf, "%s %saltar",
387 /* like endgame high priests, endgame high altars
388 are only recognizable when immediately adjacent */
389 (Is_astralevel(&u.uz) && distu(x, y) > 2)
390 ? "aligned"
391 : align_str(
392 Amask2align(levl[x][y].altarmask & ~AM_SHRINE)),
393 ((levl[x][y].altarmask & AM_SHRINE)
394 && (Is_astralevel(&u.uz) || Is_sanctum(&u.uz)))
395 ? "high "
396 : "");
397 break;
398 case S_ndoor:
399 if (is_drawbridge_wall(x, y) >= 0)
400 Strcpy(buf, "open drawbridge portcullis");
401 else if ((levl[x][y].doormask & ~D_TRAPPED) == D_BROKEN)
402 Strcpy(buf, "broken door");
403 else
404 Strcpy(buf, "doorway");
405 break;
406 case S_cloud:
407 Strcpy(buf,
408 Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud");
409 break;
410 case S_stone:
411 if (!levl[x][y].seenv) {
412 Strcpy(buf, "unexplored");
413 break;
414 } else if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) {
415 Strcpy(buf, "stone");
416 break;
418 /*else FALLTHRU*/
419 default:
420 Strcpy(buf, defsyms[glyph_to_cmap(glyph)].explanation);
421 break;
424 return (pm && !Hallucination) ? pm : (struct permonst *) 0;
428 * Look in the "data" file for more info. Called if the user typed in the
429 * whole name (user_typed_name == TRUE), or we've found a possible match
430 * with a character/glyph and flags.help is TRUE.
432 * NOTE: when (user_typed_name == FALSE), inp is considered read-only and
433 * must not be changed directly, e.g. via lcase(). We want to force
434 * lcase() for data.base lookup so that we can have a clean key.
435 * Therefore, we create a copy of inp _just_ for data.base lookup.
437 STATIC_OVL void
438 checkfile(inp, pm, user_typed_name, without_asking)
439 char *inp;
440 struct permonst *pm;
441 boolean user_typed_name, without_asking;
443 dlb *fp;
444 char buf[BUFSZ], newstr[BUFSZ], givenname[BUFSZ];
445 char *ep, *dbase_str;
446 unsigned long txt_offset = 0L;
447 int chk_skip, pass = 1;
448 boolean found_in_file = FALSE, skipping_entry = FALSE, yes_to_moreinfo;
449 winid datawin = WIN_ERR;
451 fp = dlb_fopen(DATAFILE, "r");
452 if (!fp) {
453 pline("Cannot open data file!");
454 return;
458 * If someone passed us garbage, prevent fault.
460 if (!inp || (inp && strlen(inp) > (BUFSZ - 1))) {
461 pline("bad do_look buffer passed!");
462 return;
465 /* To prevent the need for entries in data.base like *ngel to account
466 * for Angel and angel, make the lookup string the same for both
467 * user_typed_name and picked name.
469 if (pm != (struct permonst *) 0 && !user_typed_name)
470 dbase_str = strcpy(newstr, pm->mname);
471 else
472 dbase_str = strcpy(newstr, inp);
473 (void) lcase(dbase_str);
475 if (!strncmp(dbase_str, "interior of ", 12))
476 dbase_str += 12;
477 if (!strncmp(dbase_str, "a ", 2))
478 dbase_str += 2;
479 else if (!strncmp(dbase_str, "an ", 3))
480 dbase_str += 3;
481 else if (!strncmp(dbase_str, "the ", 4))
482 dbase_str += 4;
483 if (!strncmp(dbase_str, "tame ", 5))
484 dbase_str += 5;
485 else if (!strncmp(dbase_str, "peaceful ", 9))
486 dbase_str += 9;
487 if (!strncmp(dbase_str, "invisible ", 10))
488 dbase_str += 10;
489 if (!strncmp(dbase_str, "saddled ", 8))
490 dbase_str += 8;
491 if (!strncmp(dbase_str, "statue of ", 10))
492 dbase_str[6] = '\0';
493 else if (!strncmp(dbase_str, "figurine of ", 12))
494 dbase_str[8] = '\0';
496 /* Make sure the name is non-empty. */
497 if (*dbase_str) {
498 /* adjust the input to remove "named " and convert to lower case */
499 char *alt = 0; /* alternate description */
501 if ((ep = strstri(dbase_str, " named ")) != 0)
502 alt = ep + 7;
503 else if ((ep = strstri(dbase_str, " called ")) != 0) {
504 if (strlen(ep + 8) < BUFSZ - 1) {
505 Strcpy(givenname, ep + 8);
506 givenname[BUFSZ-1] = '\0';
507 alt = &givenname[0];
510 if (!ep)
511 ep = strstri(dbase_str, ", ");
512 if (ep && ep > dbase_str)
513 *ep = '\0';
516 * If the object is named, then the name is the alternate description;
517 * otherwise, the result of makesingular() applied to the name is.
518 * This
519 * isn't strictly optimal, but named objects of interest to the user
520 * will usually be found under their name, rather than under their
521 * object type, so looking for a singular form is pointless.
523 if (!alt)
524 alt = makesingular(dbase_str);
526 if (!strcmp(alt, dbase_str))
527 pass = 0;
529 for (; pass >= 0; pass--) {
530 txt_offset = 0L;
531 if (dlb_fseek(fp, txt_offset, SEEK_SET) < 0 ) {
532 impossible("can't get to start of 'data' file");
533 dlb_fclose(fp);
534 return;
536 /* skip first record; read second */
537 if (!dlb_fgets(buf, BUFSZ, fp) || !dlb_fgets(buf, BUFSZ, fp)) {
538 impossible("can't read 'data' file");
539 (void) dlb_fclose(fp);
540 return;
541 } else if (sscanf(buf, "%8lx\n", &txt_offset) < 1
542 || txt_offset == 0L)
543 goto bad_data_file;
545 /* look for the appropriate entry */
546 while (dlb_fgets(buf, BUFSZ, fp)) {
547 if (*buf == '.')
548 break; /* we passed last entry without success */
550 if (digit(*buf)) {
551 /* a number indicates the end of current entry */
552 skipping_entry = FALSE;
553 } else if (!skipping_entry) {
554 if (!(ep = index(buf, '\n')))
555 goto bad_data_file;
556 (void) strip_newline((ep > buf) ? ep - 1 : ep);
557 /* if we match a key that begins with "~", skip this entry */
558 chk_skip = (*buf == '~') ? 1 : 0;
559 if ((pass == 0 && pmatch(&buf[chk_skip], dbase_str)) ||
560 (pass == 1 && alt && pmatch(&buf[chk_skip], alt))) {
561 if (chk_skip) {
562 skipping_entry = TRUE;
563 continue;
564 } else {
565 found_in_file = TRUE;
566 break;
571 if (found_in_file) {
572 long entry_offset;
573 int entry_count;
574 int i;
576 /* skip over other possible matches for the info */
577 do {
578 if (!dlb_fgets(buf, BUFSZ, fp))
579 goto bad_data_file;
580 } while (!digit(*buf));
581 if (sscanf(buf, "%ld,%d\n", &entry_offset, &entry_count) < 2) {
582 bad_data_file:
583 impossible("'data' file in wrong format or corrupted");
584 /* window will exist if we came here from below via 'goto' */
585 if (datawin != WIN_ERR)
586 destroy_nhwindow(datawin);
587 (void) dlb_fclose(fp);
588 return;
591 yes_to_moreinfo = FALSE;
592 if (!user_typed_name && !without_asking) {
593 unsigned maxt = strlen("More info about \"\"?");
594 char *entrytext = pass ? alt : dbase_str;
595 char question[BUFSZ];
596 if (strlen(entrytext) < BUFSZ - maxt) {
597 Strcpy(question, "More info about \"");
598 Strcat(question, entrytext);
599 Strcat(question, "\"?");
601 if (yn(question) == 'y')
602 yes_to_moreinfo = TRUE;
605 if (user_typed_name || without_asking || yes_to_moreinfo) {
606 if (dlb_fseek(fp,
607 (long) txt_offset + entry_offset, SEEK_SET) < 0) {
608 pline("? Seek error on 'data' file!");
609 (void) dlb_fclose(fp);
610 return;
612 datawin = create_nhwindow(NHW_MENU);
613 for (i = 0; i < entry_count; i++) {
614 if (!dlb_fgets(buf, BUFSZ, fp))
615 goto bad_data_file;
616 (void) strip_newline(buf);
617 if (index(buf + 1, '\t') != 0)
618 (void) tabexpand(buf + 1);
619 putstr(datawin, 0, buf + 1);
621 display_nhwindow(datawin, FALSE);
622 destroy_nhwindow(datawin);
624 } else if (user_typed_name && pass == 0)
625 pline("I don't have any information on those things.");
628 (void) dlb_fclose(fp);
632 do_screen_description(cc, looked, sym, out_str, firstmatch)
633 coord cc;
634 boolean looked;
635 int sym;
636 char *out_str;
637 const char **firstmatch;
639 boolean need_to_look = FALSE;
640 int glyph = NO_GLYPH;
641 static char look_buf[BUFSZ];
642 char prefix[BUFSZ];
643 int found = 0; /* count of matching syms found */
644 int i, alt_i;
645 int skipped_venom = 0;
646 boolean hit_trap;
647 const char *x_str;
648 static const char *mon_interior = "the interior of a monster";
650 if (looked) {
651 int oc;
652 unsigned os;
654 glyph = glyph_at(cc.x, cc.y);
655 /* Convert glyph at selected position to a symbol for use below. */
656 (void) mapglyph(glyph, &sym, &oc, &os, cc.x, cc.y);
658 Sprintf(prefix, "%s ", encglyph(glyph));
659 } else
660 Sprintf(prefix, "%c ", sym);
663 * Check all the possibilities, saving all explanations in a buffer.
664 * When all have been checked then the string is printed.
668 * Special case: if identifying from the screen, and we're swallowed,
669 * and looking at something other than our own symbol, then just say
670 * "the interior of a monster".
672 if (u.uswallow && looked
673 && (is_swallow_sym(sym) || (int) showsyms[S_stone] == sym)) {
674 if (!found) {
675 Sprintf(out_str, "%s%s", prefix, mon_interior);
676 *firstmatch = mon_interior;
677 } else {
678 found += append_str(out_str, mon_interior);
680 need_to_look = TRUE;
681 goto didlook;
684 /* Check for monsters */
685 for (i = 0; i < MAXMCLASSES; i++) {
686 if (sym == (looked ? showsyms[i + SYM_OFF_M] : def_monsyms[i].sym)
687 && def_monsyms[i].explain) {
688 need_to_look = TRUE;
689 if (!found) {
690 Sprintf(out_str, "%s%s", prefix, an(def_monsyms[i].explain));
691 *firstmatch = def_monsyms[i].explain;
692 found++;
693 } else {
694 found += append_str(out_str, an(def_monsyms[i].explain));
698 /* handle '@' as a special case if it refers to you and you're
699 playing a character which isn't normally displayed by that
700 symbol; firstmatch is assumed to already be set for '@' */
701 if ((looked ? (sym == showsyms[S_HUMAN + SYM_OFF_M]
702 && cc.x == u.ux && cc.y == u.uy)
703 : (sym == def_monsyms[S_HUMAN].sym && !flags.showrace))
704 && !(Race_if(PM_HUMAN) || Race_if(PM_ELF)) && !Upolyd)
705 found += append_str(out_str, "you"); /* tack on "or you" */
707 /* Now check for objects */
708 for (i = 1; i < MAXOCLASSES; i++) {
709 if (sym == (looked ? showsyms[i + SYM_OFF_O] : def_oc_syms[i].sym)) {
710 need_to_look = TRUE;
711 if (looked && i == VENOM_CLASS) {
712 skipped_venom++;
713 continue;
715 if (!found) {
716 Sprintf(out_str, "%s%s", prefix, an(def_oc_syms[i].explain));
717 *firstmatch = def_oc_syms[i].explain;
718 found++;
719 } else {
720 found += append_str(out_str, an(def_oc_syms[i].explain));
725 if (sym == DEF_INVISIBLE) {
726 if (!found) {
727 Sprintf(out_str, "%s%s", prefix, an(invisexplain));
728 *firstmatch = invisexplain;
729 found++;
730 } else {
731 found += append_str(out_str, an(invisexplain));
735 #define is_cmap_trap(i) ((i) >= S_arrow_trap && (i) <= S_polymorph_trap)
736 #define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge)
738 /* Now check for graphics symbols */
739 alt_i = (sym == (looked ? showsyms[0] : defsyms[0].sym)) ? 0 : (2 + 1);
740 for (hit_trap = FALSE, i = 0; i < MAXPCHARS; i++) {
741 /* when sym is the default background character, we process
742 i == 0 three times: unexplored, stone, dark part of a room */
743 if (alt_i < 2) {
744 x_str = !alt_i++ ? "unexplored" : "stone";
745 i = 0; /* for second iteration, undo loop increment */
746 /* alt_i is now 1 or 2 */
747 } else {
748 if (alt_i++ == 2)
749 i = 0; /* undo loop increment */
750 x_str = defsyms[i].explanation;
751 /* alt_i is now 3 or more and no longer of interest */
753 if (sym == (looked ? showsyms[i] : defsyms[i].sym) && *x_str) {
754 /* avoid "an unexplored", "an stone", "an air", "a water",
755 "a floor of a room", "a dark part of a room";
756 article==2 => "the", 1 => "an", 0 => (none) */
757 int article = strstri(x_str, " of a room") ? 2
758 : !(alt_i <= 2
759 || strcmp(x_str, "air") == 0
760 || strcmp(x_str, "water") == 0);
762 if (!found) {
763 if (is_cmap_trap(i)) {
764 Sprintf(out_str, "%sa trap", prefix);
765 hit_trap = TRUE;
766 } else {
767 Sprintf(out_str, "%s%s", prefix,
768 article == 2 ? the(x_str)
769 : article == 1 ? an(x_str) : x_str);
771 *firstmatch = x_str;
772 found++;
773 } else if (!u.uswallow && !(hit_trap && is_cmap_trap(i))
774 && !(found >= 3 && is_cmap_drawbridge(i))
775 /* don't mention vibrating square outside of Gehennom
776 unless this happens to be one (hallucination?) */
777 && (i != S_vibrating_square || Inhell
778 || (looked && glyph_is_trap(glyph)
779 && glyph_to_trap(glyph) == VIBRATING_SQUARE))) {
780 found += append_str(out_str, (article == 2) ? the(x_str)
781 : (article == 1) ? an(x_str)
782 : x_str);
783 if (is_cmap_trap(i))
784 hit_trap = TRUE;
787 if (i == S_altar || is_cmap_trap(i))
788 need_to_look = TRUE;
792 /* Now check for warning symbols */
793 for (i = 1; i < WARNCOUNT; i++) {
794 x_str = def_warnsyms[i].explanation;
795 if (sym == (looked ? warnsyms[i] : def_warnsyms[i].sym)) {
796 if (!found) {
797 Sprintf(out_str, "%s%s", prefix, def_warnsyms[i].explanation);
798 *firstmatch = def_warnsyms[i].explanation;
799 found++;
800 } else {
801 found += append_str(out_str, def_warnsyms[i].explanation);
803 /* Kludge: warning trumps boulders on the display.
804 Reveal the boulder too or player can get confused */
805 if (looked && sobj_at(BOULDER, cc.x, cc.y))
806 Strcat(out_str, " co-located with a boulder");
807 break; /* out of for loop*/
811 /* if we ignored venom and list turned out to be short, put it back */
812 if (skipped_venom && found < 2) {
813 x_str = def_oc_syms[VENOM_CLASS].explain;
814 if (!found) {
815 Sprintf(out_str, "%s%s", prefix, an(x_str));
816 *firstmatch = x_str;
817 found++;
818 } else {
819 found += append_str(out_str, an(x_str));
823 /* handle optional boulder symbol as a special case */
824 if (iflags.bouldersym && sym == iflags.bouldersym) {
825 if (!found) {
826 *firstmatch = "boulder";
827 Sprintf(out_str, "%s%s", prefix, an(*firstmatch));
828 found++;
829 } else {
830 found += append_str(out_str, "boulder");
835 * If we are looking at the screen, follow multiple possibilities or
836 * an ambiguous explanation by something more detailed.
838 didlook:
839 if (looked) {
840 if (found > 1 || need_to_look) {
841 char monbuf[BUFSZ];
842 char temp_buf[BUFSZ];
844 (void) lookat(cc.x, cc.y, look_buf, monbuf);
845 *firstmatch = look_buf;
846 if (*(*firstmatch)) {
847 Sprintf(temp_buf, " (%s)", *firstmatch);
848 (void) strncat(out_str, temp_buf,
849 BUFSZ - strlen(out_str) - 1);
850 found = 1; /* we have something to look up */
852 if (monbuf[0]) {
853 Sprintf(temp_buf, " [seen: %s]", monbuf);
854 (void) strncat(out_str, temp_buf,
855 BUFSZ - strlen(out_str) - 1);
860 return found;
863 /* getpos() return values */
864 #define LOOK_TRADITIONAL 0 /* '.' -- ask about "more info?" */
865 #define LOOK_QUICK 1 /* ',' -- skip "more info?" */
866 #define LOOK_ONCE 2 /* ';' -- skip and stop looping */
867 #define LOOK_VERBOSE 3 /* ':' -- show more info w/o asking */
869 /* also used by getpos hack in do_name.c */
870 const char what_is_an_unknown_object[] = "an unknown object";
873 do_look(mode, click_cc)
874 int mode;
875 coord *click_cc;
877 boolean quick = (mode == 1); /* use cursor; don't search for "more info" */
878 boolean clicklook = (mode == 2); /* right mouse-click method */
879 char out_str[BUFSZ];
880 const char *firstmatch = 0;
881 struct permonst *pm = 0;
882 int i = '\0', ans = 0;
883 int sym; /* typed symbol or converted glyph */
884 int found; /* count of matching syms found */
885 coord cc; /* screen pos of unknown glyph */
886 boolean save_verbose; /* saved value of flags.verbose */
887 boolean from_screen; /* question from the screen */
889 if (!clicklook) {
890 if (quick) {
891 from_screen = TRUE; /* yes, we want to use the cursor */
892 i = 'y';
895 if (i != 'y') {
896 menu_item *pick_list = (menu_item *) 0;
897 winid win;
898 anything any;
900 any = zeroany;
901 win = create_nhwindow(NHW_MENU);
902 start_menu(win);
903 any.a_char = '/';
904 /* 'y' and 'n' to keep backwards compatibility with previous
905 versions: "Specify unknown object by cursor?" */
906 add_menu(win, NO_GLYPH, &any,
907 flags.lootabc ? 0 : any.a_char, 'y', ATR_NONE,
908 "something on the map", MENU_UNSELECTED);
909 any.a_char = 'i';
910 add_menu(win, NO_GLYPH, &any,
911 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
912 "something you're carrying", MENU_UNSELECTED);
913 any.a_char = '?';
914 add_menu(win, NO_GLYPH, &any,
915 flags.lootabc ? 0 : any.a_char, 'n', ATR_NONE,
916 "something else (by symbol or name)", MENU_UNSELECTED);
917 if (!u.uswallow && !Hallucination) {
918 any = zeroany;
919 add_menu(win, NO_GLYPH, &any, 0, 0, ATR_NONE,
920 "", MENU_UNSELECTED);
921 /* these options work sensibly for the swallowed case,
922 but there's no reason for the player to use them then;
923 objects work fine when hallucinating, but screen
924 symbol/monster class letter doesn't match up with
925 bogus monster type, so suppress when hallucinating */
926 any.a_char = 'm';
927 add_menu(win, NO_GLYPH, &any,
928 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
929 "nearby monsters", MENU_UNSELECTED);
930 any.a_char = 'M';
931 add_menu(win, NO_GLYPH, &any,
932 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
933 "all monsters shown on map", MENU_UNSELECTED);
934 any.a_char = 'o';
935 add_menu(win, NO_GLYPH, &any,
936 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
937 "nearby objects", MENU_UNSELECTED);
938 any.a_char = 'O';
939 add_menu(win, NO_GLYPH, &any,
940 flags.lootabc ? 0 : any.a_char, 0, ATR_NONE,
941 "all objects shown on map", MENU_UNSELECTED);
943 end_menu(win, "What do you want to look at:");
944 if (select_menu(win, PICK_ONE, &pick_list) > 0) {
945 i = pick_list->item.a_char;
946 free((genericptr_t) pick_list);
948 destroy_nhwindow(win);
951 switch (i) {
952 default:
953 case 'q':
954 return 0;
955 case 'y':
956 case '/':
957 from_screen = TRUE;
958 sym = 0;
959 cc.x = u.ux;
960 cc.y = u.uy;
961 break;
962 case 'i':
964 char invlet;
965 struct obj *invobj;
967 invlet = display_inventory((const char *) 0, TRUE);
968 if (!invlet || invlet == '\033')
969 return 0;
970 *out_str = '\0';
971 for (invobj = invent; invobj; invobj = invobj->nobj)
972 if (invobj->invlet == invlet) {
973 strcpy(out_str, singular(invobj, xname));
974 break;
976 if (*out_str)
977 checkfile(out_str, pm, TRUE, TRUE);
978 return 0;
980 case '?':
981 from_screen = FALSE;
982 getlin("Specify what? (type the word)", out_str);
983 if (out_str[0] == '\0' || out_str[0] == '\033')
984 return 0;
986 if (out_str[1]) { /* user typed in a complete string */
987 checkfile(out_str, pm, TRUE, TRUE);
988 return 0;
990 sym = out_str[0];
991 break;
992 case 'm':
993 look_all(TRUE, TRUE); /* list nearby monsters */
994 return 0;
995 case 'M':
996 look_all(FALSE, TRUE); /* list all monsters */
997 return 0;
998 case 'o':
999 look_all(TRUE, FALSE); /* list nearby objects */
1000 return 0;
1001 case 'O':
1002 look_all(FALSE, FALSE); /* list all objects */
1003 return 0;
1005 } else { /* clicklook */
1006 cc.x = click_cc->x;
1007 cc.y = click_cc->y;
1008 sym = 0;
1009 from_screen = FALSE;
1012 /* Save the verbose flag, we change it later. */
1013 save_verbose = flags.verbose;
1014 flags.verbose = flags.verbose && !quick;
1016 * The user typed one letter, or we're identifying from the screen.
1018 do {
1019 /* Reset some variables. */
1020 pm = (struct permonst *) 0;
1021 found = 0;
1022 out_str[0] = '\0';
1024 if (from_screen || clicklook) {
1025 if (from_screen) {
1026 if (flags.verbose)
1027 pline("Please move the cursor to %s.",
1028 what_is_an_unknown_object);
1029 else
1030 pline("Pick an object.");
1032 ans = getpos(&cc, quick, what_is_an_unknown_object);
1033 if (ans < 0 || cc.x < 0) {
1034 flags.verbose = save_verbose;
1035 return 0; /* done */
1037 flags.verbose = FALSE; /* only print long question once */
1041 found = do_screen_description(cc, (from_screen || clicklook), sym,
1042 out_str, &firstmatch);
1044 /* Finally, print out our explanation. */
1045 if (found) {
1046 /* Used putmixed() because there may be an encoded glyph present
1048 putmixed(WIN_MESSAGE, 0, out_str);
1050 /* check the data file for information about this thing */
1051 if (found == 1 && ans != LOOK_QUICK && ans != LOOK_ONCE
1052 && (ans == LOOK_VERBOSE || (flags.help && !quick))
1053 && !clicklook) {
1054 char temp_buf[BUFSZ];
1056 Strcpy(temp_buf, firstmatch);
1057 checkfile(temp_buf, pm, FALSE,
1058 (boolean) (ans == LOOK_VERBOSE));
1060 } else {
1061 pline("I've never heard of such things.");
1064 } while (from_screen && !quick && ans != LOOK_ONCE && !clicklook);
1066 flags.verbose = save_verbose;
1067 return 0;
1070 STATIC_OVL void
1071 look_all(nearby, do_mons)
1072 boolean nearby; /* True => within BOLTLIM, False => entire map */
1073 boolean do_mons; /* True => monsters, False => objects */
1075 winid win;
1076 int x, y, lo_x, lo_y, hi_x, hi_y, glyph, count = 0;
1077 char lookbuf[BUFSZ], outbuf[BUFSZ];
1079 win = create_nhwindow(NHW_TEXT);
1080 lo_y = nearby ? max(u.uy - BOLT_LIM, 0) : 0;
1081 lo_x = nearby ? max(u.ux - BOLT_LIM, 1) : 1;
1082 hi_y = nearby ? min(u.uy + BOLT_LIM, ROWNO - 1) : ROWNO - 1;
1083 hi_x = nearby ? min(u.ux + BOLT_LIM, COLNO - 1) : COLNO - 1;
1084 for (y = lo_y; y <= hi_y; y++) {
1085 for (x = lo_x; x <= hi_x; x++) {
1086 lookbuf[0] = '\0';
1087 glyph = glyph_at(x, y);
1088 if (do_mons) {
1089 if (glyph_is_monster(glyph)) {
1090 struct monst *mtmp;
1092 bhitpos.x = x; /* [is this actually necessary?] */
1093 bhitpos.y = y;
1094 if (x == u.ux && y == u.uy && canspotself()) {
1095 (void) self_lookat(lookbuf);
1096 ++count;
1097 } else if ((mtmp = m_at(x, y)) != 0) {
1098 look_at_monster(lookbuf, (char *) 0, mtmp, x, y);
1099 ++count;
1101 } else if (glyph_is_invisible(glyph)) {
1102 /* remembered, unseen, creature */
1103 Strcpy(lookbuf, invisexplain);
1104 ++count;
1105 } else if (glyph_is_warning(glyph)) {
1106 int warnindx = glyph_to_warning(glyph);
1108 Strcpy(lookbuf, def_warnsyms[warnindx].explanation);
1109 ++count;
1111 } else { /* !do_mons */
1112 if (glyph_is_object(glyph)) {
1113 look_at_object(lookbuf, x, y, glyph);
1114 ++count;
1117 if (*lookbuf) {
1118 char coordbuf[20], which[12], cmode;
1120 cmode = (iflags.getpos_coords != GPCOORDS_NONE)
1121 ? iflags.getpos_coords : GPCOORDS_MAP;
1122 if (count == 1) {
1123 Strcpy(which, do_mons ? "monsters" : "objects");
1124 if (nearby)
1125 Sprintf(outbuf, "%s currently shown near %s:",
1126 upstart(which),
1127 (cmode != GPCOORDS_COMPASS)
1128 ? coord_desc(u.ux, u.uy, coordbuf, cmode)
1129 : !canspotself() ? "your position" : "you");
1130 else
1131 Sprintf(outbuf, "All %s currently shown on the map:",
1132 which);
1133 putstr(win, 0, outbuf);
1134 putstr(win, 0, "");
1136 /* prefix: "coords C " where 'C' is mon or obj symbol */
1137 Sprintf(outbuf, (cmode == GPCOORDS_SCREEN) ? "%s "
1138 : (cmode == GPCOORDS_MAP) ? "%8s "
1139 : "%12s ",
1140 coord_desc(x, y, coordbuf, cmode));
1141 Sprintf(eos(outbuf), "%s ", encglyph(glyph));
1142 /* guard against potential overflow */
1143 lookbuf[sizeof lookbuf - 1 - strlen(outbuf)] = '\0';
1144 Strcat(outbuf, lookbuf);
1145 putmixed(win, 0, outbuf);
1149 if (count)
1150 display_nhwindow(win, TRUE);
1151 else
1152 pline("No %s are currently shown %s.",
1153 do_mons ? "monsters" : "objects",
1154 nearby ? "nearby" : "on the map");
1155 destroy_nhwindow(win);
1158 /* the '/' command */
1160 dowhatis()
1162 return do_look(0, (coord *) 0);
1165 /* the ';' command */
1167 doquickwhatis()
1169 return do_look(1, (coord *) 0);
1172 /* the '^' command */
1174 doidtrap()
1176 register struct trap *trap;
1177 int x, y, tt;
1179 if (!getdir("^"))
1180 return 0;
1181 x = u.ux + u.dx;
1182 y = u.uy + u.dy;
1183 for (trap = ftrap; trap; trap = trap->ntrap)
1184 if (trap->tx == x && trap->ty == y) {
1185 if (!trap->tseen)
1186 break;
1187 tt = trap->ttyp;
1188 if (u.dz) {
1189 if (u.dz < 0 ? (tt == TRAPDOOR || tt == HOLE)
1190 : tt == ROCKTRAP)
1191 break;
1193 tt = what_trap(tt);
1194 pline("That is %s%s%s.",
1195 an(defsyms[trap_to_defsym(tt)].explanation),
1196 !trap->madeby_u
1197 ? ""
1198 : (tt == WEB)
1199 ? " woven"
1200 /* trap doors & spiked pits can't be made by
1201 player, and should be considered at least
1202 as much "set" as "dug" anyway */
1203 : (tt == HOLE || tt == PIT)
1204 ? " dug"
1205 : " set",
1206 !trap->madeby_u ? "" : " by you");
1207 return 0;
1209 pline("I can't see a trap there.");
1210 return 0;
1213 char *
1214 dowhatdoes_core(q, cbuf)
1215 char q;
1216 char *cbuf;
1218 dlb *fp;
1219 char bufr[BUFSZ];
1220 register char *buf = &bufr[6], ctrl, meta;
1222 fp = dlb_fopen(CMDHELPFILE, "r");
1223 if (!fp) {
1224 pline("Cannot open data file!");
1225 return 0;
1228 ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0);
1229 meta = ((0x80 & q) ? (0x7f & q) : 0);
1230 while (dlb_fgets(buf, BUFSZ - 6, fp)) {
1231 if ((ctrl && *buf == '^' && *(buf + 1) == ctrl)
1232 || (meta && *buf == 'M' && *(buf + 1) == '-'
1233 && *(buf + 2) == meta) || *buf == q) {
1234 (void) strip_newline(buf);
1235 if (ctrl && buf[2] == '\t') {
1236 buf = bufr + 1;
1237 (void) strncpy(buf, "^? ", 8);
1238 buf[1] = ctrl;
1239 } else if (meta && buf[3] == '\t') {
1240 buf = bufr + 2;
1241 (void) strncpy(buf, "M-? ", 8);
1242 buf[2] = meta;
1243 } else if (buf[1] == '\t') {
1244 buf = bufr;
1245 buf[0] = q;
1246 (void) strncpy(buf + 1, " ", 7);
1248 (void) dlb_fclose(fp);
1249 Strcpy(cbuf, buf);
1250 return cbuf;
1253 (void) dlb_fclose(fp);
1254 return (char *) 0;
1258 dowhatdoes()
1260 char bufr[BUFSZ];
1261 char q, *reslt;
1263 #if defined(UNIX) || defined(VMS)
1264 introff();
1265 #endif
1266 q = yn_function("What command?", (char *) 0, '\0');
1267 #if defined(UNIX) || defined(VMS)
1268 intron();
1269 #endif
1270 reslt = dowhatdoes_core(q, bufr);
1271 if (reslt)
1272 pline1(reslt);
1273 else
1274 pline("I've never heard of such commands.");
1275 return 0;
1278 void
1279 docontact()
1281 winid cwin = create_nhwindow(NHW_TEXT);
1282 char buf[BUFSZ];
1284 if (sysopt.support) {
1285 /*XXX overflow possibilities*/
1286 Sprintf(buf, "To contact local support, %s", sysopt.support);
1287 putstr(cwin, 0, buf);
1288 putstr(cwin, 0, "");
1289 } else if (sysopt.fmtd_wizard_list) { /* formatted SYSCF WIZARDS */
1290 Sprintf(buf, "To contact local support, contact %s.",
1291 sysopt.fmtd_wizard_list);
1292 putstr(cwin, 0, buf);
1293 putstr(cwin, 0, "");
1295 putstr(cwin, 0, "To contact the NetHack development team directly,");
1296 /*XXX overflow possibilities*/
1297 Sprintf(buf, "see the 'Contact' form on our website or email <%s>.",
1298 DEVTEAM_EMAIL);
1299 putstr(cwin, 0, buf);
1300 putstr(cwin, 0, "");
1301 putstr(cwin, 0, "For more information on NetHack, or to report a bug,");
1302 Sprintf(buf, "visit our website \"%s\".", DEVTEAM_URL);
1303 putstr(cwin, 0, buf);
1304 display_nhwindow(cwin, FALSE);
1305 destroy_nhwindow(cwin);
1308 /* data for help_menu() */
1309 static const char *help_menu_items[] = {
1310 /* 0*/ "About NetHack (version information).",
1311 /* 1*/ "Long description of the game and commands.",
1312 /* 2*/ "List of game commands.",
1313 /* 3*/ "Concise history of NetHack.",
1314 /* 4*/ "Info on a character in the game display.",
1315 /* 5*/ "Info on what a given key does.",
1316 /* 6*/ "List of game options.",
1317 /* 7*/ "Longer explanation of game options.",
1318 /* 8*/ "List of extended commands.",
1319 /* 9*/ "The NetHack license.",
1320 /* 10*/ "Support information.",
1321 #ifdef PORT_HELP
1322 "%s-specific help and commands.",
1323 #define PORT_HELP_ID 100
1324 #define WIZHLP_SLOT 12
1325 #else
1326 #define WIZHLP_SLOT 11
1327 #endif
1328 "List of wizard-mode commands.", "", (char *) 0
1331 STATIC_OVL boolean
1332 help_menu(sel)
1333 int *sel;
1335 winid tmpwin = create_nhwindow(NHW_MENU);
1336 #ifdef PORT_HELP
1337 char helpbuf[QBUFSZ];
1338 #endif
1339 int i, n;
1340 menu_item *selected;
1341 anything any;
1343 any = zeroany; /* zero all bits */
1344 start_menu(tmpwin);
1345 if (!wizard)
1346 help_menu_items[WIZHLP_SLOT] = "",
1347 help_menu_items[WIZHLP_SLOT + 1] = (char *) 0;
1348 for (i = 0; help_menu_items[i]; i++)
1349 #ifdef PORT_HELP
1350 /* port-specific line has a %s in it for the PORT_ID */
1351 if (help_menu_items[i][0] == '%') {
1352 Sprintf(helpbuf, help_menu_items[i], PORT_ID);
1353 any.a_int = PORT_HELP_ID + 1;
1354 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, helpbuf,
1355 MENU_UNSELECTED);
1356 } else
1357 #endif
1359 any.a_int = (*help_menu_items[i]) ? i + 1 : 0;
1360 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE,
1361 help_menu_items[i], MENU_UNSELECTED);
1363 end_menu(tmpwin, "Select one item:");
1364 n = select_menu(tmpwin, PICK_ONE, &selected);
1365 destroy_nhwindow(tmpwin);
1366 if (n > 0) {
1367 *sel = selected[0].item.a_int - 1;
1368 free((genericptr_t) selected);
1369 return TRUE;
1371 return FALSE;
1374 /* the '?' command */
1376 dohelp()
1378 int sel = 0;
1380 if (help_menu(&sel)) {
1381 switch (sel) {
1382 case 0:
1383 (void) doextversion();
1384 break;
1385 case 1:
1386 display_file(HELP, TRUE);
1387 break;
1388 case 2:
1389 display_file(SHELP, TRUE);
1390 break;
1391 case 3:
1392 (void) dohistory();
1393 break;
1394 case 4:
1395 (void) dowhatis();
1396 break;
1397 case 5:
1398 (void) dowhatdoes();
1399 break;
1400 case 6:
1401 option_help();
1402 break;
1403 case 7:
1404 display_file(OPTIONFILE, TRUE);
1405 break;
1406 case 8:
1407 (void) doextlist();
1408 break;
1409 case 9:
1410 display_file(LICENSE, TRUE);
1411 break;
1412 case 10:
1413 (void) docontact();
1414 break;
1415 #ifdef PORT_HELP
1416 case PORT_HELP_ID:
1417 port_help();
1418 break;
1419 #endif
1420 default:
1421 /* handle slot 11 or 12 */
1422 display_file(DEBUGHELP, TRUE);
1423 break;
1426 return 0;
1429 /* the 'V' command; also a choice for '?' */
1431 dohistory()
1433 display_file(HISTORY, TRUE);
1434 return 0;
1437 /*pager.c*/