Source:NetHack 3.0.0/pager.c

From NetHackWiki
Jump to navigation Jump to search

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

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

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

1.    /*	SCCS Id: @(#)pager.c	3.0	88/10/25 */
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 routine dowhatis() and a pager. */
6.    /* Also readmail() and doshell(), and generally the things that
7.       contact the outside world. */
8.    
9.    /* block some unused #defines to avoid overloading some cpp's */
10.   #define MONATTK_H
11.   #include	 "hack.h"
12.   
13.   #ifndef TOS
14.   #include <signal.h>
15.   #endif
16.   #if defined(BSD) || defined(ULTRIX)
17.   #include <sys/wait.h>
18.   #endif
19.   
20.   static char hc = 0;
21.   
22.   static void page_more();
23.   
24.   int
25.   dowhatis()
26.   {
27.   	FILE *fp;
28.   	char bufr[BUFSZ+6];
29.   	register char *buf = &bufr[6], *ep, q;
30.   	register struct monst *mtmp;
31.   
32.   	if(!(fp = fopen(DATAFILE, "r")))
33.   		pline("Cannot open data file!");
34.   	else {
35.   		coord	cc;
36.   		uchar	r;
37.   
38.   		pline ("Specify unknown object by cursor? ");
39.   		q = ynq();
40.   		cc.x = cc.y = -1;
41.   		if (q == 'q') {
42.   			(void) fclose(fp);
43.   			return 0;
44.   		} else if (q == 'n') {
45.   			pline("Specify what? ");
46.   			r = readchar();
47.   		} else {
48.   		    if(flags.verbose)
49.   			pline("Please move the cursor to the unknown object.");
50.   		    getpos(&cc, TRUE, "the unknown object");
51.   		    r = levl[cc.x][cc.y].scrsym;
52.   		}
53.   
54.   		if (r == showsyms.stone) q = defsyms.stone;
55.   		else if (r == showsyms.vwall) q = defsyms.vwall;
56.   		else if (r == showsyms.hwall) q = defsyms.hwall;
57.   		else if (r == showsyms.tlcorn) q = defsyms.tlcorn;
58.   		else if (r == showsyms.trcorn) q = defsyms.trcorn;
59.   		else if (r == showsyms.blcorn) q = defsyms.blcorn;
60.   		else if (r == showsyms.brcorn) q = defsyms.brcorn;
61.   		else if (r == showsyms.crwall) q = defsyms.crwall;
62.   		else if (r == showsyms.tuwall) q = defsyms.tuwall;
63.   		else if (r == showsyms.tdwall) q = defsyms.tdwall;
64.   		else if (r == showsyms.tlwall) q = defsyms.tlwall;
65.   		else if (r == showsyms.trwall) q = defsyms.trwall;
66.   		else if (r == showsyms.door) q = defsyms.door;
67.   		else if (r == showsyms.room) q = defsyms.room;
68.   		else if (r == showsyms.corr) q = defsyms.corr;
69.   		else if (r == showsyms.upstair) q = defsyms.upstair;
70.   		else if (r == showsyms.dnstair) q = defsyms.dnstair;
71.   		else if (r == showsyms.trap) q = defsyms.trap;
72.   #ifdef FOUNTAINS
73.   		else if (r == showsyms.pool) q = defsyms.pool;
74.   		else if (r == showsyms.fountain) q = defsyms.fountain;
75.   #endif
76.   #ifdef THRONES
77.   		else if (r == showsyms.throne) q = defsyms.throne;
78.   #endif
79.   		else if (r == showsyms.web) q = defsyms.web;
80.   #ifdef SINKS
81.   		else if (r == showsyms.sink) q = defsyms.sink;
82.   #endif
83.   #ifdef ALTARS
84.   		else if (r == showsyms.altar) q = defsyms.altar;
85.   #endif
86.   		else
87.   		    q = r;
88.   		if (index(quitchars, q)) {
89.   			(void) fclose(fp); /* sweet@scubed */
90.   			return 0;
91.   		}
92.   		if(q == '%') {
93.   			pline("%%       a piece of food");
94.   			(void) fclose(fp);
95.   			return 0;
96.   		}
97.   
98.   		if(q != '\t')
99.   		while(fgets(buf,BUFSZ,fp))
100.  		    if(*buf == q) {
101.  			ep = index(buf, '\n');
102.  			if(ep) *ep = 0;
103.  			/* else: bad data file */
104.  			/* Expand tab 'by hand' */
105.  			if(buf[1] == '\t'){
106.  				buf = bufr;
107.  				buf[0] = r;
108.  				(void) strncpy(buf+1, "       ", 7);
109.  			}
110.  			pline(buf);
111.  			if(cc.x != -1 && IS_ALTAR(levl[cc.x][cc.y].typ)) {
112.  			    int type = levl[u.ux][u.uy].altarmask & ~A_SHRINE;
113.  			    pline("(%s)", (type==0) ? "chaotic" :
114.  				(type==1) ? "neutral" : "lawful");
115.  			}
116.  			if (!Invisible && u.ux==cc.x && u.uy==cc.y) {
117.  			    pline("(%s named %s)",
118.  #ifdef POLYSELF
119.  				u.mtimedone ? mons[u.umonnum].mname :
120.  #endif
121.  				pl_character, plname);
122.  			} else if((q >= 'A' && q <= 'z') || index(";:& @`",q)) {
123.  			    for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
124.  				if(mtmp->mx == cc.x && mtmp->my == cc.y) {
125.  				    pline("(%s%s)",
126.  					mtmp->mtame ? "tame " :
127.  					  mtmp->mpeaceful ? "peaceful " : "",
128.  					strncmp(lmonnam(mtmp), "the ", 4)
129.  					  ? lmonnam(mtmp) : lmonnam(mtmp)+4);
130.  				    break;
131.  				}
132.  			}
133.  			if(ep[-1] == ';') {
134.  				pline("More info? ");
135.  				if(yn() == 'y') {
136.  					page_more(fp,1); /* does fclose() */
137.  					return 0;
138.  				}
139.  			}
140.  			(void) fclose(fp); 	/* kopper@psuvax1 */
141.  			return 0;
142.  		    }
143.  		pline("I've never heard of such things.");
144.  		(void) fclose(fp);
145.  	}
146.  	return 0;
147.  }
148.  
149.  int
150.  dowhatdoes()
151.  {
152.  	FILE *fp;
153.  	char bufr[BUFSZ+6];
154.  	register char *buf = &bufr[6], *ep, q, ctrl;
155.  
156.  	if(!(fp = fopen(CMDHELPFILE, "r"))) {
157.  		pline("Cannot open data file!");
158.  		return 0;
159.  	}
160.  	pline("What command? ");
161.  #ifdef UNIX
162.  	introff();
163.  #endif
164.  	q = readchar();
165.  #ifdef UNIX
166.  	intron();
167.  #endif
168.  	if (q == '\033') ctrl = '[';
169.  	else if (q != unctrl(q)) ctrl = q - 1 + 'A';
170.  	else ctrl = 0;
171.  	while(fgets(buf,BUFSZ,fp))
172.  	    if ((!ctrl && *buf==q) || (ctrl && *buf=='^' && *(buf+1)==ctrl)) {
173.  		ep = index(buf, '\n');
174.  		if(ep) *ep = 0;
175.  		if(!ctrl && buf[1] == '\t'){
176.  			buf = bufr;
177.  			buf[0] = q;
178.  			(void) strncpy(buf+1, "       ", 7);
179.  		} else if (ctrl && buf[2] == '\t'){
180.  			buf = bufr + 1;
181.  			buf[0] = '^';
182.  			buf[1] = ctrl;
183.  			(void) strncpy(buf+2, "      ", 6);
184.  		}
185.  		pline(buf);
186.  		(void) fclose(fp);
187.  		return 0;
188.  	    }
189.  	pline("I've never heard of such commands.");
190.  	(void) fclose(fp);
191.  	return 0;
192.  }
193.  
194.  /* make the paging of a file interruptible */
195.  static int got_intrup;
196.  
197.  #if !defined(MSDOS) && !defined(TOS)
198.  static int
199.  intruph(){
200.  	(void) signal(SIGINT, (SIG_RET_TYPE) intruph);
201.  	got_intrup++;
202.  	return 0;
203.  }
204.  #endif
205.  
206.  /* simple pager, also used from dohelp() */
207.  static void
208.  page_more(fp,strip)
209.  FILE *fp;
210.  int strip;	/* nr of chars to be stripped from each line (0 or 1) */
211.  {
212.  	register char *bufr;
213.  #if !defined(MSDOS) && !defined(MINIMAL_TERM)
214.  	register char *ep;
215.  #endif
216.  #if !defined(MSDOS) && !defined(TOS)
217.  	int (*prevsig)() = (int (*)())signal(SIGINT, (SIG_RET_TYPE) intruph);
218.  #endif
219.  #if defined(MSDOS) || defined(MINIMAL_TERM)
220.  	/* There seems to be a bug in ANSI.SYS  The first tab character
221.  	 * after a clear screen sequence is not expanded correctly.  Thus
222.  	 * expand the tabs by hand -dgk
223.  	 */
224.  	int tabstop = 8, spaces;
225.  	char buf[BUFSIZ], *bufp, *bufrp;
226.  
227.  	set_pager(0);
228.  	bufr = (char *) alloc((unsigned) COLNO);
229.  	while (fgets(buf, BUFSIZ, fp) && (!strip || *buf == '\t')){
230.  		bufp = buf;
231.  		bufrp = bufr;
232.  		while (*bufp && *bufp != '\n') {
233.  			if (*bufp == '\t') {
234.  				spaces = tabstop - (bufrp - bufr) % tabstop;
235.  				while (spaces--)
236.  					*bufrp++ = ' ';
237.  				bufp++;
238.  			} else
239.  				*bufrp++ = *bufp++;
240.  		}
241.  		*bufrp = '\0';
242.  #else /* MSDOS /**/
243.  	set_pager(0);
244.  	bufr = (char *) alloc((unsigned) COLNO);
245.  	bufr[COLNO-1] = 0;
246.  	while(fgets(bufr,COLNO-1,fp) && (!strip || *bufr == '\t')){
247.  		ep = index(bufr, '\n');
248.  		if(ep)
249.  			*ep = 0;
250.  #endif /* MSDOS /**/
251.  		if(got_intrup || page_line(bufr+strip)) {
252.  			set_pager(2);
253.  			goto ret;
254.  		}
255.  	}
256.  	set_pager(1);
257.  ret:
258.  	free((genericptr_t) bufr);
259.  	(void) fclose(fp);
260.  #if !defined(MSDOS) && !defined(TOS)
261.  	(void) signal(SIGINT, (SIG_RET_TYPE) prevsig);
262.  	got_intrup = 0;
263.  #endif
264.  }
265.  
266.  static boolean whole_screen = TRUE;
267.  #define	PAGMIN	12	/* minimum # of lines for page below level map */
268.  
269.  void
270.  set_whole_screen() {	/* called in termcap as soon as LI is known */
271.  	whole_screen = (LI-ROWNO-2 <= PAGMIN || !CD);
272.  }
273.  
274.  #ifdef NEWS
275.  int
276.  readnews() {
277.  	register int ret;
278.  
279.  	whole_screen = TRUE;	/* force a docrt(), our first */
280.  	ret = page_file(NEWS, TRUE);
281.  	set_whole_screen();
282.  	return(ret);		/* report whether we did docrt() */
283.  }
284.  #endif
285.  
286.  void
287.  set_pager(mode)
288.  register int mode;	/* 0: open  1: wait+close  2: close */
289.  {
290.  #ifdef LINT	/* lint may handle static decl poorly -- static boolean so; */
291.  	boolean so;
292.  #else
293.  	static boolean so;
294.  #endif
295.  	if(mode == 0) {
296.  		if(!whole_screen) {
297.  			/* clear topline */
298.  			clrlin();
299.  			/* use part of screen below level map */
300.  			curs(1, ROWNO+4);
301.  		} else {
302.  			cls();
303.  		}
304.  		so = flags.standout;
305.  		flags.standout = 1;
306.  	} else {
307.  		if(mode == 1) {
308.  			curs(1, LI);
309.  			more();
310.  		}
311.  		flags.standout = so;
312.  		if(whole_screen)
313.  			docrt();
314.  		else {
315.  			curs(1, ROWNO+4);
316.  			cl_eos();
317.  		}
318.  	}
319.  }
320.  
321.  int
322.  page_line(s)		/* returns 1 if we should quit */
323.  register char *s;
324.  {
325.  	if(cury == LI-1) {
326.  		if(!*s)
327.  			return(0);	/* suppress blank lines at top */
328.  		(void) putchar('\n');
329.  		cury++;
330.  		cmore("q\033");
331.  		if(morc) {
332.  			morc = 0;
333.  			return(1);
334.  		}
335.  		if(whole_screen)
336.  			cls();
337.  		else {
338.  			curs(1, ROWNO+4);
339.  			cl_eos();
340.  		}
341.  	}
342.  #ifdef TERMINFO
343.  	xputs(s); xputc('\n');
344.  #else
345.  	(void) puts(s);
346.  #endif
347.  	cury++;
348.  	return(0);
349.  }
350.  
351.  /*
352.   * Flexible pager: feed it with a number of lines and it will decide
353.   * whether these should be fed to the pager above, or displayed in a
354.   * corner.
355.   * Call:
356.   *	cornline(0, title or 0)	: initialize
357.   *	cornline(1, text)	: add text to the chain of texts
358.   *	cornline(2, morcs)	: output everything and cleanup
359.   *	cornline(3, 0)		: cleanup
360.   *	cornline(-1,"")		: special, for help menu mode only
361.   */
362.  
363.  void
364.  cornline(mode, text)
365.  int mode;
366.  char *text;
367.  {
368.  	static struct line {
369.  		struct line *next_line;
370.  		char *line_text;
371.  	} *texthead, *texttail;
372.  	static int maxlen;
373.  	static int linect;
374.  	register struct line *tl;
375.  	register boolean hmenu = FALSE;
376.  
377.  	if(mode == -1) { /* help menu display only */
378.  		mode = 2;
379.  		hmenu = TRUE;
380.  	}
381.  	if(mode == 0) {
382.  		texthead = 0;
383.  		maxlen = 0;
384.  		linect = 0;
385.  		if(text) {
386.  			cornline(1, text);	/* title */
387.  			cornline(1, "");	/* blank line */
388.  		}
389.  		return;
390.  	}
391.  
392.  	if(mode == 1) {
393.  	    register int len;
394.  
395.  	    if(!text) return;	/* superfluous, just to be sure */
396.  	    linect++;
397.  	    len = strlen(text) + 1; /* allow for an extra leading space */
398.  	    if(len > maxlen)
399.  		maxlen = len;
400.  	    tl = (struct line *)
401.  		alloc((unsigned)(len + sizeof(struct line) + 1));
402.  	    tl->next_line = 0;
403.  	    tl->line_text = (char *)(tl + 1);
404.  	    tl->line_text[0] = ' ';
405.  	    tl->line_text[1] = '\0';
406.  	    Strcat(tl->line_text, text);
407.  	    if(!texthead)
408.  		texthead = tl;
409.  	    else
410.  		texttail->next_line = tl;
411.  	    texttail = tl;
412.  	    return;
413.  	}
414.  
415.  	/* --- now we really do it --- */
416.  	if(mode == 2 && linect == 1)			    /* topline only */
417.  		pline(texthead->line_text);
418.  	else
419.  	if(mode == 2) {
420.  	    register int curline, lth;
421.  
422.  	    if(flags.toplin == 1) more();	/* ab@unido */
423.  	    remember_topl();
424.  
425.  	    lth = CO - maxlen - 2;		   /* Use full screen width */
426.  	    if (linect < LI && lth >= 10) {		     /* in a corner */
427.  		home ();
428.  		cl_end ();
429.  		flags.toplin = 0;
430.  		curline = 1;
431.  		for (tl = texthead; tl; tl = tl->next_line) {
432.  #if defined(MSDOS) && !defined(AMIGA)
433.  		    cmov (lth, curline);
434.  #else
435.  		    curs (lth, curline);
436.  #endif
437.  		    if(curline > 1)
438.  			cl_end ();
439.  		    xputs(tl->line_text);
440.  		    curx = curx + strlen(tl->line_text);
441.  		    curline++;
442.  		}
443.  		if(hmenu) hc = lowc(readchar()); /* help menu display */
444.  #if defined(MSDOS) && !defined(AMIGA)
445.  		cmov (lth, curline);
446.  #else
447.  		curs (lth, curline);
448.  #endif
449.  		cl_end ();
450.  		if (!hmenu) cmore (text);
451.  		home ();
452.  		cl_end ();
453.  		docorner (lth, curline-1);
454.  	    } else {					/* feed to pager */
455.  		set_pager(0);
456.  		for (tl = texthead; tl; tl = tl->next_line) {
457.  		    if (page_line (tl->line_text)) {
458.  			set_pager(2);
459.  			goto cleanup;
460.  		    }
461.  		}
462.  		if(text) {
463.  			cgetret(text);
464.  			set_pager(2);
465.  		} else
466.  			set_pager(1);
467.  	    }
468.  	}
469.  
470.  cleanup:
471.  	while(tl = texthead) {
472.  		texthead = tl->next_line;
473.  		free((genericptr_t) tl);
474.  	}
475.  }
476.  
477.  #ifdef WIZARD
478.  static
479.  void
480.  wiz_help()
481.  {
482.  	cornline(0, "Wizard-Mode Quick Reference:");
483.  	cornline(1, "^E  ==  detect secret doors and traps.");
484.  	cornline(1, "^F  ==  do magic mapping.");
485.  	cornline(1, "^G  ==  create monster.");
486.  	cornline(1, "^I  ==  identify items in pack.");
487.  	cornline(1, "^O  ==  tell locations of special levels.");
488.  	cornline(1, "^T  ==  do intra-level teleport.");
489.  	cornline(1, "^V  ==  do trans-level teleport.");
490.  	cornline(1, "^W  ==  make wish.");
491.  	cornline(1, "^X  ==  show intrinsic attributes.");
492.  	cornline(1, "");
493.  	cornline(2, "");
494.  }
495.  #endif
496.  
497.  static void
498.  help_menu() {
499.  	cornline(0, "Information available:");
500.  	cornline(1, "a.  Long description of the game and commands.");
501.  	cornline(1, "b.  List of game commands.");
502.  	cornline(1, "c.  Concise history of NetHack.");
503.  	cornline(1, "d.  Info on a character in the game display.");
504.  	cornline(1, "e.  Info on what a given key does.");
505.  	cornline(1, "f.  List of game options.");
506.  	cornline(1, "g.  Longer explanation of game options.");
507.  	cornline(1, "h.  List of extended commands.");
508.  	cornline(1, "i.  The NetHack license.");
509.  #ifdef WIZARD
510.  	if (wizard)
511.  		cornline(1, "j.  List of wizard-mode commands.");
512.  #endif
513.  	cornline(1, "");
514.  #ifdef WIZARD
515.  	if (wizard)
516.  		cornline(1, "Select one of a,b,c,d,e,f,g,h,i,j or ESC: ");
517.  	else
518.  #endif
519.  		cornline(1, "Select one of a,b,c,d,e,f,g,h,i or ESC: ");
520.  	cornline(-1,"");
521.  }
522.  
523.  int
524.  dohelp()
525.  {
526.  	char c;
527.  
528.  	do {
529.  	    help_menu();
530.  	    c = hc;
531.  #ifdef WIZARD
532.  	} while ((c < 'a' || c > (wizard ? 'j' : 'i')) && !index(quitchars,c));
533.  #else
534.  	} while ((c < 'a' || c > 'i') && !index(quitchars,c));
535.  #endif
536.  	if (!index(quitchars, c)) {
537.  		switch(c) {
538.  			case 'a':  (void) page_file(HELP, FALSE);  break;
539.  			case 'b':  (void) page_file(SHELP, FALSE);  break;
540.  			case 'c':  (void) dohistory();  break;
541.  			case 'd':  (void) dowhatis();  break;
542.  			case 'e':  (void) dowhatdoes();  break;
543.  			case 'f':  option_help();  break;
544.  			case 'g':  (void) page_file(OPTIONFILE, FALSE);  break;
545.  			case 'h':  (void) doextlist();  break;
546.  			case 'i':  (void) page_file(LICENSE, FALSE);  break;
547.  #ifdef WIZARD
548.  			case 'j':  wiz_help();  break;
549.  #endif
550.  		}
551.  	}
552.  	return 0;
553.  }
554.  
555.  int
556.  dohistory()
557.  {
558.  	(void) page_file(HISTORY, FALSE);
559.  	return 0;
560.  }
561.  
562.  int
563.  page_file(fnam, silent)	/* return: 0 - cannot open fnam; 1 - otherwise */
564.  register char *fnam;
565.  boolean silent;
566.  {
567.  #ifdef DEF_PAGER			/* this implies that UNIX is defined */
568.        {
569.  	/* use external pager; this may give security problems */
570.  
571.  	register int fd = open(fnam, 0);
572.  
573.  	if(fd < 0) {
574.  		if(!silent) pline("Cannot open %s.", fnam);
575.  		return(0);
576.  	}
577.  	if(child(1)){
578.  		/* Now that child() does a setuid(getuid()) and a chdir(),
579.  		   we may not be able to open file fnam anymore, so make
580.  		   it stdin. */
581.  		(void) close(0);
582.  		if(dup(fd)) {
583.  			if(!silent) Printf("Cannot open %s as stdin.\n", fnam);
584.  		} else {
585.  			(void) execl(catmore, "page", NULL);
586.  			if(!silent) Printf("Cannot exec %s.\n", catmore);
587.  		}
588.  		exit(1);
589.  	}
590.  	(void) close(fd);
591.        }
592.  #else
593.        {
594.  	FILE *f;			/* free after Robert Viduya */
595.  
596.  	if ((f = fopen (fnam, "r")) == (FILE *) 0) {
597.  		if(!silent) {
598.  			home(); perror (fnam); flags.toplin = 1;
599.  			pline ("Cannot open %s.", fnam);
600.  		}
601.  		return(0);
602.  	}
603.  	page_more(f, 0);
604.        }
605.  #endif /* DEF_PAGER /**/
606.  
607.  	return(1);
608.  }
609.  
610.  #ifdef UNIX
611.  #ifdef SHELL
612.  int
613.  dosh(){
614.  register char *str;
615.  	if(child(0)) {
616.  		if(str = getenv("SHELL"))
617.  			(void) execl(str, str, NULL);
618.  		else
619.  			(void) execl("/bin/sh", "sh", NULL);
620.  		pline("sh: cannot execute.");
621.  		exit(1);
622.  	}
623.  	return 0;
624.  }
625.  #endif /* SHELL /**/
626.  
627.  int
628.  child(wt)
629.  int wt;
630.  {
631.  register int f = fork();
632.  	if(f == 0){		/* child */
633.  		settty(NULL);		/* also calls end_screen() */
634.  		(void) setgid(getgid());
635.  		(void) setuid(getuid());
636.  #ifdef CHDIR
637.  		(void) chdir(getenv("HOME"));
638.  #endif
639.  		return(1);
640.  	}
641.  	if(f == -1) {	/* cannot fork */
642.  		pline("Fork failed.  Try again.");
643.  		return(0);
644.  	}
645.  	/* fork succeeded; wait for child to exit */
646.  	(void) signal(SIGINT,SIG_IGN);
647.  	(void) signal(SIGQUIT,SIG_IGN);
648.  	(void) wait(
649.  #if defined(BSD) || defined(ULTRIX)
650.  		(union wait *)
651.  #else
652.  		(int *)
653.  #endif
654.  		0);
655.  	gettty();
656.  	setftty();
657.  	(void) signal(SIGINT, (SIG_RET_TYPE) done1);
658.  #ifdef WIZARD
659.  	if(wizard) (void) signal(SIGQUIT,SIG_DFL);
660.  #endif
661.  	if(wt) getret();
662.  	docrt();
663.  	return(0);
664.  }
665.  #endif /* UNIX /**/