Source:NetHack 3.2.0/pager.c

Below is the full text to pager.c from the source code of NetHack 3.2.0. To link to a particular line, write [[NetHack 3.2.0/pager.c#line123 ]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

1.   /*	SCCS Id: @(#)pager.c	3.2	96/02/14	*/ 2.   /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3.    /* NetHack may be freely redistributed. See license for details. */ 4.     5.    /* This file contains the command routines dowhatis and dohelp and */ 6.   /* a few other help related facilities */ 7.    8.    #include "hack.h"  9.    #include "dlb.h"  10. 11.  static boolean FDECL(is_swallow_sym, (int)); 12.  static int FDECL(append_str, (char *, const char *)); 13.  static void FDECL(lookat, (int, int, char *)); 14.  static void FDECL(checkfile, (char *, BOOLEAN_P)); 15.  static int FDECL(do_look, (BOOLEAN_P)); 16.  static boolean FDECL(help_menu, (int *)); 17.  #ifdef PORT_HELP 18.  extern void NDECL(port_help); 19.  #endif 20.   21.   /* Returns "true" for characters that could represent a monster's stomach. */ 22.   static boolean 23.  is_swallow_sym(c) 24.  int c;  25. { 26.       int i;  27. for (i = S_sw_tl; i <= S_sw_br; i++) 28.  	if ((int)showsyms[i] == c) return TRUE; 29.      return FALSE; 30.  }  31.    32.   /*  33.    * Append new_str to the end of buf if new_str doesn't already exist as  34. * a substring of buf. Return 1 if the string was appended, 0 otherwise. 35.   * It is expected that buf is of size BUFSZ. 36.   */  37.   static int 38.  append_str(buf, new_str) 39.      char *buf; 40.      const char *new_str; 41.  {  42.       int space_left;	/* space remaining in buf */ 43.   44.       if (strstri(buf, new_str)) return 0; 45.   46.       space_left = BUFSZ - strlen(buf) - 1; 47.      (void) strncat(buf, " or ", space_left); 48.      (void) strncat(buf, new_str, space_left - 4); 49.      return 1; 50.  }  51.    52.   /*  53.    * Return the name of the glyph found at (x,y). 54.   */  55.   static void 56.  lookat(x, y, buf) 57.      int x, y;  58. char *buf; 59.  {  60.       register struct monst *mtmp; 61.      int glyph; 62.   63.       buf[0] = 0; 64.      glyph = glyph_at(x,y); 65.      if (u.ux == x && u.uy == y && canseeself) { 66.  	Sprintf(buf, "%s%s called %s",  67.   		Invis ? "invisible " : "",  68.   		u.mtimedone ? mons[u.umonnum].mname : player_mon->mname,  69.   		plname); 70.      }  71.       else if (u.uswallow) { 72.  	/* all locations when swallowed other than the hero are the monster */ 73.  	Sprintf(buf, "interior of %s",  74.   				    Blind ? "a monster" : a_monnam(u.ustuck)); 75.      }  76.       else if (glyph_is_monster(glyph)) { 77.  	bhitpos.x = x;  78. bhitpos.y = y; 79. mtmp = m_at(x,y); 80.  	if(mtmp != (struct monst *) 0) { 81.  	    register boolean hp = (mtmp->data == &mons[PM_HIGH_PRIEST]); 82.   83.   	    Sprintf(buf, "%s%s%s",  84.   		    (mtmp->mx != x || mtmp->my != y) ?  85.   			((mtmp->isshk && !Hallucination) 86.  				? "tail of " : "tail of a ") : "",  87.   		    (!hp && mtmp->mtame && !Hallucination) ? "tame " :  88.   		    (!hp && mtmp->mpeaceful && !Hallucination) ?  89.   		                                          "peaceful " : "",  90.   		    (hp ? "high priest" : l_monnam(mtmp))); 91.  	    if (u.ustuck == mtmp) 92.  		Strcat(buf, (Upolyd && sticks(uasmon)) ?  93.   			", being held" : ", holding you"); 94.  	    if (mtmp->mleashed) 95.  		Strcat(buf, ", leashed to you"); 96.  	}  97.       }  98.       else if (glyph_is_object(glyph)) { 99.  	struct obj *otmp = vobj_at(x,y); 100.  101.  	if(otmp == (struct obj *) 0 || otmp->otyp != glyph_to_obj(glyph)) { 102. 	    if(glyph_to_obj(glyph) != STRANGE_OBJECT) { 103. 		otmp = mksobj(glyph_to_obj(glyph), FALSE, FALSE); 104. 		if(otmp->oclass == GOLD_CLASS) 105. 		    otmp->quan = 2L; /* to force pluralization */ 106. 		else if (otmp->otyp == SLIME_MOLD) 107. 		    otmp->spe = current_fruit;	/* give the fruit a type */ 108. 		Strcpy(buf, distant_name(otmp, xname)); 109. 		dealloc_obj(otmp); 110. 	    }  111.  	} else 112. 	    Strcpy(buf, distant_name(otmp, xname)); 113.  114.  	if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR) 115. 	    Strcat(buf, " embedded in stone"); 116. 	else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR) 117. 	    Strcat(buf, " embedded in a wall"); 118. 	else if (closed_door(x,y)) 119. 	    Strcat(buf, " embedded in a door"); 120. 	else if (is_pool(x,y)) 121. 	    Strcat(buf, " in water"); 122. 	else if (is_lava(x,y)) 123. 	    Strcat(buf, " in molten lava");	/* [can this ever happen?] */ 124.     }  125.      else if (glyph_is_trap(glyph)) { 126. 	int tnum = glyph_to_trap(glyph); 127. 	Strcpy(buf, defsyms[  128.  	    trap_to_defsym(Hallucination ? rn2(TRAPNUM-3)+3 : tnum)].explanation); 129.     }  130.      else if(!glyph_is_cmap(glyph)) 131. 	Strcpy(buf,"dark part of a room"); 132.     else switch(glyph_to_cmap(glyph)) { 133.     case S_altar: 134. 	if(!In_endgame(&u.uz)) 135. 	    Sprintf(buf, "%s altar",  136.  		align_str(Amask2align(levl[x][y].altarmask & ~AM_SHRINE))); 137. 	else Sprintf(buf, "aligned altar"); 138. 	break; 139.     case S_ndoor: 140. 	if (is_drawbridge_wall(x, y) >= 0) 141. 	    Strcpy(buf,"open drawbridge portcullis"); 142. 	else if ((levl[x][y].doormask & ~D_TRAPPED) == D_BROKEN) 143. 	    Strcpy(buf,"broken door"); 144. 	else 145. 	    Strcpy(buf,"doorway"); 146. 	break; 147.     case S_cloud: 148. 	Strcpy(buf, Is_airlevel(&u.uz) ? "cloudy area" : "fog/vapor cloud"); 149. 	break; 150.     default: 151. 	Strcpy(buf,defsyms[glyph_to_cmap(glyph)].explanation); 152. 	break; 153.     }  154.  }  155.   156.  /*  157.   * Look in the "data" file for more info. Called if the user typed in the 158.  * whole name (user_typed_name == TRUE), or we've found a possible match 159.  * with a character/glyph and flags.help is TRUE. 160.  *  161.   * NOTE: when (user_typed_name == FALSE), inp is considered read-only and 162.  *	 must not be changed directly, e.g. via lcase. We want to force 163.  *	 lcase for data.base lookup so that we can have a clean key. 164.  *	 Therefore, we create a copy of inp _just_ for data.base lookup. 165.  */  166.  static void 167. checkfile(inp, user_typed_name) 168.     char *inp; 169.     boolean user_typed_name; 170. {  171.      dlb *fp; 172.     char buf[BUFSZ], newstr[BUFSZ]; 173.     char *ep, *dbase_str; 174.     long txt_offset; 175.     int chk_skip; 176.     boolean found_in_file = FALSE, skipping_entry = FALSE; 177.  178.      fp = dlb_fopen(DATAFILE, "r"); 179.     if (!fp) { 180. 	pline("Cannot open data file!"); 181. 	return; 182.     }  183.   184.      /* To prevent the need for entries in data.base like *ngel to account 185.      * for Angel and angel, make the lookup string the same for both 186.      * user_typed_name and picked name. 187.      */  188.      dbase_str = strcpy(newstr, inp); 189.     (void) lcase(dbase_str); 190.  191.      if (!strncmp(dbase_str, "interior of ", 12)) 192. 	dbase_str += 12; 193.     if (!strncmp(dbase_str, "a ", 2)) 194. 	dbase_str += 2; 195.     else if (!strncmp(dbase_str, "an ", 3)) 196. 	dbase_str += 3; 197.     else if (!strncmp(dbase_str, "the ", 4)) 198. 	dbase_str += 4; 199.     if (!strncmp(dbase_str, "tame ", 5)) 200. 	dbase_str += 5; 201.     else if (!strncmp(dbase_str, "peaceful ", 9)) 202. 	dbase_str += 9; 203.     if (!strncmp(dbase_str, "invisible ", 10)) 204. 	dbase_str += 10; 205.  206.      /* Make sure the name is non-empty. */ 207.      if (*dbase_str) { 208. 	/* adjust the input to remove "named " and convert to lower case */ 209. 	char *alt = 0;	/* alternate description */ 210. 	if ((ep = strstri(dbase_str, " named ")) != 0) 211. 	    alt = ep + 7; 212. 	else 213. 	    ep = strstri(dbase_str, " called "); 214. 	if (ep) *ep = '\0'; 215.  216.  	/*  217.  	 * If the object is named, then the name is the alternate description; 218. 	 * otherwise, the result of makesingular applied to the name is. This 219. 	 * isn't strictly optimal, but named objects of interest to the user 220. 	 * will usually be found under their name, rather than under their 221. 	 * object type, so looking for a singular form is pointless. 222. 	 */  223.   224.  	if (!alt) 225. 	    alt = makesingular(dbase_str); 226. 	else 227. 	    if (user_typed_name) 228. 		(void) lcase(alt); 229.  230.  	/* skip first record; read second */ 231. 	txt_offset = 0L; 232. 	if (!dlb_fgets(buf, BUFSZ, fp) || !dlb_fgets(buf, BUFSZ, fp)) { 233. 	    impossible("can't read 'data' file"); 234. 	    (void) dlb_fclose(fp); 235. 	    return; 236. 	} else if (sscanf(buf, "%8lx\n", &txt_offset) < 1 || txt_offset <= 0) 237. 	    goto bad_data_file; 238.  239.  	/* look for the appropriate entry */ 240. 	while (dlb_fgets(buf,BUFSZ,fp)) { 241. 	    if (*buf == '.') break;  /* we passed last entry without success */ 242.  243.  	    if (digit(*buf)) { 244. 		/* a number indicates the end of current entry */ 245. 		skipping_entry = FALSE; 246. 	    } else if (!skipping_entry) { 247. 		if (!(ep = index(buf, '\n'))) goto bad_data_file; 248. 		*ep = 0; 249. 		/* if we match a key that begins with "~", skip this entry */ 250. 		chk_skip = (*buf == '~') ? 1 : 0; 251.  		if (pmatch(&buf[chk_skip], dbase_str) ||  252.  			(alt && pmatch(&buf[chk_skip], alt))) { 253. 		    if (chk_skip) { 254. 			skipping_entry = TRUE; 255. 			continue; 256. 		    } else { 257. 			found_in_file = TRUE; 258. 			break; 259. 		    }  260.  		}  261.  	    }  262.  	}  263.      }  264.   265.      if(found_in_file) { 266. 	long entry_offset; 267. 	int  entry_count; 268. 	int  i;  269. 270. 	/* skip over other possible matches for the info */ 271. 	do { 272. 	    if (!dlb_fgets(buf, BUFSZ, fp)) goto bad_data_file; 273. 	} while (!digit(*buf)); 274. 	if (sscanf(buf, "%ld,%d\n", &entry_offset, &entry_count) < 2) { 275. bad_data_file:	impossible("'data' file in wrong format"); 276. 		(void) dlb_fclose(fp); 277. 		return; 278. 	}  279.   280.  	if (user_typed_name || yn("More info?") == 'y') { 281. 	    winid datawin; 282.  283.  	    if (dlb_fseek(fp, txt_offset + entry_offset, SEEK_SET) < 0) { 284. 		pline("? Seek error on 'data' file!"); 285. 		(void) dlb_fclose(fp); 286. 		return; 287. 	    }  288.  	    datawin = create_nhwindow(NHW_MENU); 289. 	    for (i = 0; i < entry_count; i++) { 290. 		if (!dlb_fgets(buf, BUFSZ, fp)) goto bad_data_file; 291. 		if ((ep = index(buf, '\n')) != 0) *ep = 0; 292. 		if (index(buf+1, '\t') != 0) (void) tabexpand(buf+1); 293. 		putstr(datawin, 0, buf+1); 294. 	    }  295.  	    display_nhwindow(datawin, FALSE); 296. 	    destroy_nhwindow(datawin); 297. 	}  298.      } else if (user_typed_name) 299. 	pline("I don't have any information on those things."); 300.  301.      (void) dlb_fclose(fp); 302. }  303.   304.  static int 305. do_look(quick) 306.     boolean quick;	/* use cursor && don't search for "more info" */ 307. {  308.      char    out_str[BUFSZ], look_buf[BUFSZ]; 309.     const char *x_str, *firstmatch = 0; 310.     int     i;  311. int    sym;		/* typed symbol or converted glyph */ 312.     int	    found;		/* count of matching syms found */ 313.     coord   cc;			/* screen pos of unknown glyph */ 314.     boolean save_verbose;	/* saved value of flags.verbose */ 315.     boolean from_screen;	/* question from the screen */ 316.     boolean need_to_look;	/* need to get explan. from glyph */ 317.     boolean hit_trap;		/* true if found trap explanation */ 318.     int skipped_venom = 0;	/* non-zero if we ignored "splash of venom" */ 319.     static const char *mon_interior = "the interior of a monster"; 320.  321.      if (quick) { 322. 	from_screen = TRUE;	/* yes, we want to use the cursor */ 323.     } else { 324. 	i = ynq("Specify unknown object by cursor?"); 325. 	if (i == 'q') return 0; 326. 	from_screen = (i == 'y'); 327.     }  328.   329.      if (from_screen) { 330. 	cc.x = u.ux; 331. 	cc.y = u.uy; 332. 	sym = 0;		/* gcc -Wall lint */ 333.     } else { 334. 	getlin("Specify what? (type the word)", out_str); 335. 	if (out_str[0] == '\0' || out_str[0] == '\033') 336. 	    return 0; 337.  338.  	if (out_str[1]) {	/* user typed in a complete string */ 339. 	    checkfile(out_str, TRUE); 340. 	    return 0; 341. 	}  342.  	sym = out_str[0]; 343.     }  344.   345.      /* Save the verbose flag, we change it later. */ 346.      save_verbose = flags.verbose; 347.     flags.verbose = flags.verbose && !quick; 348.     /*  349.       * The user typed one letter, or we're identifying from the screen. 350.      */  351.      do { 352. 	/* Reset some variables. */ 353.  	need_to_look = FALSE; 354. 	found = 0; 355. 	out_str[0] = '\0'; 356.  357.  	if (from_screen) { 358. 	    int glyph;	/* glyph at selected position */ 359.  360.  	    if (flags.verbose) 361. 		pline("Please move the cursor to an unknown object."); 362. 	    else 363. 		pline("Pick an object."); 364.  365.  	    getpos(&cc, FALSE, "an unknown object"); 366. 	    if (cc.x < 0) { 367. 		flags.verbose = save_verbose; 368. 		return 0;	/* done */ 369. 	    }  370.  	    flags.verbose = FALSE;	/* only print long question once */ 371.  372.  	    /* Convert the glyph at the selected position to a symbol. */ 373.  	    glyph = glyph_at(cc.x,cc.y); 374. 	    if (glyph_is_cmap(glyph)) { 375. 		sym = showsyms[glyph_to_cmap(glyph)]; 376. 	    } else if (glyph_is_trap(glyph)) { 377. 		sym = showsyms[trap_to_defsym(glyph_to_trap(glyph))]; 378. 	    } else if (glyph_is_object(glyph)) { 379. 		sym = oc_syms[(int)objects[glyph_to_obj(glyph)].oc_class]; 380. 	    } else if (glyph_is_monster(glyph)) { 381. 		sym = monsyms[(int)mons[glyph_to_mon(glyph)].mlet]; 382. 	    } else if (glyph_is_swallow(glyph)) { 383. 		sym = showsyms[glyph_to_swallow(glyph)+S_sw_tl]; 384. 	    } else { 385. 		impossible("do_look:  bad glyph %d at (%d,%d)",  386.  						glyph, (int)cc.x, (int)cc.y); 387. 		sym = ' '; 388. 	    }  389.  	}  390.   391.  	/*  392.  	 * Check all the possibilities, saving all explanations in a buffer. 393. 	 * When all have been checked then the string is printed. 394. 	 */  395.   396.  	/* Check for monsters */ 397. 	for (i = 0; i < MAXMCLASSES; i++) { 398. 	    if (sym == (from_screen ? monsyms[i] : def_monsyms[i])) { 399. 		need_to_look = TRUE; 400. 		if (!found) { 401. 		    Sprintf(out_str, "%c       %s", sym, an(monexplain[i])); 402. 		    firstmatch = monexplain[i]; 403. 		    found++; 404. 		} else { 405. 		    found += append_str(out_str, an(monexplain[i])); 406. 		}  407.  	    }  408.  	}  409.   410.  	/*  411.  	 * Special case: if identifying from the screen, and we're swallowed, 412. 	 * and looking at something other than our own symbol, then just say 413. 	 * "the interior of a monster". 414. 	 */  415.  	if (u.uswallow && from_screen && is_swallow_sym(sym)) { 416. 	    if (!found) { 417. 		Sprintf(out_str, "%c       %s", sym, mon_interior); 418. 		firstmatch = mon_interior; 419. 	    } else { 420. 		found += append_str(out_str, mon_interior); 421. 	    }  422.  	    need_to_look = TRUE; 423. 	}  424.   425.  	/* Now check for objects */ 426. 	for (i = 1; i < MAXOCLASSES; i++) { 427. 	    if (sym == (from_screen ? oc_syms[i] : def_oc_syms[i])) { 428. 		need_to_look = TRUE; 429. 		if (from_screen && i == VENOM_CLASS) { 430. 		    skipped_venom++; 431. 		    continue; 432. 		}  433.  		if (!found) { 434. 		    Sprintf(out_str, "%c       %s", sym, an(objexplain[i])); 435. 		    firstmatch = objexplain[i]; 436. 		    found++; 437. 		} else { 438. 		    found += append_str(out_str, an(objexplain[i])); 439. 		}  440.  	    }  441.  	}  442.   443.  #define is_cmap_trap(i) ((i) >= S_arrow_trap && (i) <= S_polymorph_trap) 444. #define is_cmap_drawbridge(i) ((i) >= S_vodbridge && (i) <= S_hcdbridge) 445.  446.  	/* Now check for graphics symbols */ 447. 	for (hit_trap = FALSE, i = 0; i < MAXPCHARS; i++) { 448. 	    x_str = defsyms[i].explanation; 449. 	    if (sym == (from_screen ? showsyms[i] : defsyms[i].sym) && *x_str) { 450. 		/* avoid "an air", "a water", or "a floor of a room" */ 451. 		int article = (i == S_room) ? 2 :		/* 2=>"the" */ 452. 			      !(strcmp(x_str, "air") == 0 ||	/* 1=>"an"  */  453.  				strcmp(x_str, "water") == 0);	/* 0=>(none)*/ 454.  455.  		if (!found) { 456. 		    if (is_cmap_trap(i)) { 457. 			Sprintf(out_str, "%c       a trap", sym); 458. 			hit_trap = TRUE; 459. 		    } else { 460. 			Sprintf(out_str, "%c       %s", sym,  461.  				article == 2 ? the(x_str) :  462.  				article == 1 ? an(x_str) : x_str); 463. 		    }  464.  		    firstmatch = x_str; 465. 		    found++; 466. 		} else if (!u.uswallow && !(hit_trap && is_cmap_trap(i)) &&  467.  			   !(found >= 3 && is_cmap_drawbridge(i))) { 468. 		    found += append_str(out_str,  469.  					article == 2 ? the(x_str) :  470.  					article == 1 ? an(x_str) : x_str); 471. 		    if (is_cmap_trap(i)) hit_trap = TRUE; 472. 		}  473.   474.  		if (i == S_altar || is_cmap_trap(i)) 475. 		    need_to_look = TRUE; 476. 	    }  477.  	}  478.   479.  	/* if we ignored venom and list turned out to be short, put it back */ 480. 	if (skipped_venom && found < 2) { 481. 	    x_str = objexplain[VENOM_CLASS]; 482. 	    if (!found) { 483. 		Sprintf(out_str, "%c       %s", sym, an(x_str)); 484. 		firstmatch = x_str; 485. 		found++; 486. 	    } else { 487. 		found += append_str(out_str, an(x_str)); 488. 	    }  489.  	}  490.   491.  	/*  492.  	 * If we are looking at the screen, follow multiple posibilities or  493. * an ambigious explanation by something more detailed. 494. 	 */  495.  	if (from_screen) { 496. 	    if (found > 1 || need_to_look) { 497. 		lookat(cc.x, cc.y, look_buf); 498. 		firstmatch = look_buf; 499. 		if (*firstmatch) { 500. 		    char temp_buf[BUFSZ]; 501. 		    Sprintf(temp_buf, " (%s)", firstmatch); 502. 		    (void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1); 503. 		    found = 1;	/* we have something to look up */ 504. 		}  505.  	    }  506.  	}  507.   508.  	/* Finally, print out our explanation. */ 509.  	if (found) { 510. 	    pline(out_str); 511. 	    /* check the data file for information about this thing */ 512. 	    if (found == 1 && !quick && flags.help) { 513. 		char temp_buf[BUFSZ]; 514. 		Strcpy(temp_buf, firstmatch); 515. 		checkfile(temp_buf, FALSE); 516. 	    }  517.  	} else { 518. 	    pline("I've never heard of such things."); 519. 	}  520.   521.      } while (from_screen && !quick); 522.  523.      flags.verbose = save_verbose; 524.     return 0; 525. }  526.   527.   528.  int 529. dowhatis 530. {  531.  	return do_look(FALSE); 532. }  533.   534.  int 535. doquickwhatis 536. {  537.  	return do_look(TRUE); 538. }  539.   540.  int 541. doidtrap 542. {  543.  	register struct trap *trap; 544. 	int x, y, tt; 545.  546.  	if (!getdir((char *)0)) return 0; 547. 	x = u.ux + u.dx; 548. 	y = u.uy + u.dy; 549. 	for (trap = ftrap; trap; trap = trap->ntrap) 550. 	    if (trap->tx == x && trap->ty == y) { 551. 		if (!trap->tseen) break; 552. 		tt = trap->ttyp; 553. 		if (u.dz) { 554. 		    if (u.dz < 0 ? (tt == TRAPDOOR || tt == HOLE) :  555.  			    tt == ROCKTRAP) break; 556. 		}  557.  		if (Hallucination) tt = rn1(TRAPNUM-3, 3); 558. 		pline("That is %s%s%s.",  559.  		      an(defsyms[trap_to_defsym(tt)].explanation),  560.  		      !trap->madeby_u ? "" : (tt == WEB) ? " woven" :  561.  			  /* trap doors & spiked pits can't be made by  562.  			     player, and should be considered at least  563.  			     as much "set" as "dug" anyway */  564.  			  (tt == HOLE || tt == PIT) ? " dug" : " set",  565.  		      !trap->madeby_u ? "" : " by you"); 566. 		return 0; 567. 	    }  568.  	pline("I can't see a trap there."); 569. 	return 0; 570. }  571.   572.  int 573. dowhatdoes 574. {  575.  	dlb *fp; 576. 	char bufr[BUFSZ+6]; 577. 	register char *buf = &bufr[6], *ep, q, ctrl, meta; 578.  579.  	fp = dlb_fopen(CMDHELPFILE, "r"); 580. 	if (!fp) { 581. 		pline("Cannot open data file!"); 582. 		return 0; 583. 	}  584.   585.  #if defined(UNIX) || defined(VMS) 586. 	introff; 587. #endif 588. 	q = yn_function("What command?", (char *)0, '\0'); 589. #if defined(UNIX) || defined(VMS) 590. 	intron; 591. #endif 592. 	ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0); 593. 	meta = ((0x80 & q) ? (0x7f & q) : 0); 594. 	while(dlb_fgets(buf,BUFSZ,fp)) 595. 	    if ((ctrl && *buf=='^' && *(buf+1)==ctrl) ||  596.  		(meta && *buf=='M' && *(buf+1)=='-' && *(buf+2)==meta) ||  597.  		*buf==q) { 598. 		ep = index(buf, '\n'); 599. 		if(ep) *ep = 0; 600. 		if (ctrl && buf[2] == '\t'){ 601. 			buf = bufr + 1; 602. 			(void) strncpy(buf, "^?      ", 8); 603. 			buf[1] = ctrl; 604. 		} else if (meta && buf[3] == '\t'){ 605. 			buf = bufr + 2; 606. 			(void) strncpy(buf, "M-?     ", 8); 607. 			buf[2] = meta; 608. 		} else if(buf[1] == '\t'){ 609. 			buf = bufr; 610. 			buf[0] = q;  611. (void) strncpy(buf+1, "      ", 7); 612. 		}  613.  		pline("%s", buf); 614. 		(void) dlb_fclose(fp); 615. 		return 0; 616. 	    }  617.  	pline("I've never heard of such commands."); 618. 	(void) dlb_fclose(fp); 619. 	return 0; 620. }  621.   622.  /* data for help_menu */ 623. static const char *help_menu_items[] = { 624. /* 0*/	"Long description of the game and commands.", 625. /* 1*/	"List of game commands.", 626. /* 2*/	"Concise history of NetHack.", 627. /* 3*/	"Info on a character in the game display.", 628. /* 4*/	"Info on what a given key does.", 629. /* 5*/	"List of game options.", 630. /* 6*/	"Longer explanation of game options.", 631. /* 7*/	"List of extended commands.", 632. /* 8*/	"The NetHack license.", 633. #ifdef PORT_HELP 634. 	"%s-specific help and commands.", 635. #define PORT_HELP_ID 100 636. #define WIZHLP_SLOT 10 637. #else 638. #define WIZHLP_SLOT 9 639. #endif 640. #ifdef WIZARD 641. 	"List of wizard-mode commands.", 642. #endif 643. 	"",  644.  	(char *)0 645. };  646.   647.  static boolean 648. help_menu(sel) 649. 	int *sel; 650. {  651.  	winid tmpwin = create_nhwindow(NHW_MENU); 652. #ifdef PORT_HELP 653. 	char helpbuf[QBUFSZ]; 654. #endif 655. 	int i, n;  656. menu_item *selected; 657. 	anything any; 658.  659.  	any.a_void = 0;		/* zero all bits */ 660. 	start_menu(tmpwin); 661. #ifdef WIZARD 662. 	if (!wizard) help_menu_items[WIZHLP_SLOT] = "", 663. 		     help_menu_items[WIZHLP_SLOT+1] = (char *)0; 664. #endif 665. 	for (i = 0; help_menu_items[i]; i++) 666. #ifdef PORT_HELP 667. 	    /* port-specific line has a %s in it for the PORT_ID */ 668. 	    if (help_menu_items[i][0] == '%') { 669. 		Sprintf(helpbuf, help_menu_items[i], PORT_ID); 670. 		any.a_int = PORT_HELP_ID + 1; 671. 		add_menu(tmpwin, NO_GLYPH, &any, 0, ATR_NONE,  672.  			 helpbuf, MENU_UNSELECTED); 673. 	    } else 674. #endif 675. 	    {  676.  		any.a_int = (*help_menu_items[i]) ? i+1 : 0; 677. 		add_menu(tmpwin, NO_GLYPH, &any, 0,  678.  			ATR_NONE, help_menu_items[i], MENU_UNSELECTED); 679. 	    }  680.  	end_menu(tmpwin, "Select one item:"); 681. 	n = select_menu(tmpwin, PICK_ONE, &selected); 682. 	destroy_nhwindow(tmpwin); 683. 	if (n > 0) { 684. 	    *sel = selected[0].item.a_int - 1; 685. 	    free((genericptr_t)selected); 686. 	    return TRUE; 687. 	}  688.  	return FALSE; 689. }  690.   691.  int 692. dohelp 693. {  694.  	int sel = 0; 695.  696.  	if (help_menu(&sel)) { 697. 		switch (sel) { 698. 			case  0:  display_file(HELP, TRUE);  break; 699. 			case  1:  display_file(SHELP, TRUE);  break; 700. 			case  2:  (void) dohistory;  break; 701. 			case  3:  (void) dowhatis;  break; 702. 			case  4:  (void) dowhatdoes;  break; 703. 			case  5:  option_help;  break; 704. 			case  6:  display_file(OPTIONFILE, TRUE);  break; 705. 			case  7:  (void) doextlist;  break; 706. 			case  8:  display_file(LICENSE, TRUE);  break; 707. #ifdef WIZARD 708. 			/* handle slot 9 or 10 */ 709. 			default: display_file(DEBUGHELP, TRUE);  break; 710. #endif 711. #ifdef PORT_HELP 712. 			case PORT_HELP_ID:  port_help;  break; 713. #endif 714. 		}  715.  	}  716.  	return 0; 717. }  718.   719.  int 720. dohistory 721. {  722.  	display_file(HISTORY, TRUE); 723. 	return 0; 724. }  725.   726.  /*pager.c*/