xref: /netbsd-src/games/hack/hack.pager.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: hack.pager.c,v 1.14 2010/02/03 15:34:38 roy Exp $	*/
2 
3 /*
4  * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5  * Amsterdam
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are
10  * met:
11  *
12  * - Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * - Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in the
17  * documentation and/or other materials provided with the distribution.
18  *
19  * - Neither the name of the Stichting Centrum voor Wiskunde en
20  * Informatica, nor the names of its contributors may be used to endorse or
21  * promote products derived from this software without specific prior
22  * written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * Copyright (c) 1982 Jay Fenlason <hack@gnu.org>
39  * All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. The name of the author may not be used to endorse or promote products
50  *    derived from this software without specific prior written permission.
51  *
52  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
55  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62  */
63 
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __RCSID("$NetBSD: hack.pager.c,v 1.14 2010/02/03 15:34:38 roy Exp $");
67 #endif				/* not lint */
68 
69 /* This file contains the command routine dowhatis() and a pager. */
70 /*
71  * Also readmail() and doshell(), and generally the things that contact the
72  * outside world.
73  */
74 
75 #include <sys/types.h>
76 #include <signal.h>
77 #include <stdlib.h>
78 #include <unistd.h>
79 #include "hack.h"
80 #include "extern.h"
81 
82 static void intruph(int);
83 static void page_more(FILE *, int);
84 static int page_file(const char *, boolean);
85 static int child(int);
86 
87 int
88 dowhatis(void)
89 {
90 	FILE           *fp;
91 	char            bufr[BUFSZ + 6];
92 	char           *buf = &bufr[6], *ep, q;
93 
94 	if (!(fp = fopen(DATAFILE, "r")))
95 		pline("Cannot open data file!");
96 	else {
97 		pline("Specify what? ");
98 		q = readchar();
99 		if (q != '\t')
100 			while (fgets(buf, BUFSZ, fp))
101 				if (*buf == q) {
102 					ep = strchr(buf, '\n');
103 					if (ep)
104 						*ep = 0;
105 					/* else: bad data file */
106 					else {
107 						pline("Bad data file!");
108 						(void) fclose(fp);
109 						return(0);
110 					}
111 					/* Expand tab 'by hand' */
112 					if (buf[1] == '\t') {
113 						buf = bufr;
114 						buf[0] = q;
115 						(void) strncpy(buf + 1, "       ", 7);
116 					}
117 					pline(buf);
118 					if (ep[-1] == ';') {
119 						pline("More info? ");
120 						if (readchar() == 'y') {
121 							page_more(fp, 1);	/* does fclose() */
122 							return (0);
123 						}
124 					}
125 					(void) fclose(fp);	/* kopper@psuvax1 */
126 					return (0);
127 				}
128 		pline("I've never heard of such things.");
129 		(void) fclose(fp);
130 	}
131 	return (0);
132 }
133 
134 /* make the paging of a file interruptible */
135 static int      got_intrup;
136 
137 static void
138 intruph(int n __unused)
139 {
140 	got_intrup++;
141 }
142 
143 /* simple pager, also used from dohelp() */
144 /* strip: nr of chars to be stripped from each line (0 or 1) */
145 static void
146 page_more(FILE *fp, int strip)
147 {
148 	char           *bufr, *ep;
149 	sig_t           prevsig = signal(SIGINT, intruph);
150 
151 	set_pager(0);
152 	bufr = (char *) alloc((unsigned) CO);
153 	bufr[CO - 1] = 0;
154 	while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) {
155 		ep = strchr(bufr, '\n');
156 		if (ep)
157 			*ep = 0;
158 		if (page_line(bufr + strip)) {
159 			set_pager(2);
160 			goto ret;
161 		}
162 	}
163 	set_pager(1);
164 ret:
165 	free(bufr);
166 	(void) fclose(fp);
167 	(void) signal(SIGINT, prevsig);
168 	got_intrup = 0;
169 }
170 
171 static boolean  whole_screen = TRUE;
172 #define	PAGMIN	12		/* minimum # of lines for page below level
173 				 * map */
174 
175 void
176 set_whole_screen(void)
177 {				/* called in termcap as soon as LI is known */
178 	whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD);
179 }
180 
181 #ifdef NEWS
182 int
183 readnews(void)
184 {
185 	int             ret;
186 
187 	whole_screen = TRUE;	/* force a docrt(), our first */
188 	ret = page_file(NEWS, TRUE);
189 	set_whole_screen();
190 	return (ret);		/* report whether we did docrt() */
191 }
192 #endif	/* NEWS */
193 
194 /* mode:  0: open  1: wait+close  2: close */
195 void
196 set_pager(int mode)
197 {
198 	static boolean  so;
199 	if (mode == 0) {
200 		if (!whole_screen) {
201 			/* clear topline */
202 			clrlin();
203 			/* use part of screen below level map */
204 			curs(1, ROWNO + 4);
205 		} else {
206 			cls();
207 		}
208 		so = flags.standout;
209 		flags.standout = 1;
210 	} else {
211 		if (mode == 1) {
212 			curs(1, LI);
213 			more();
214 		}
215 		flags.standout = so;
216 		if (whole_screen)
217 			docrt();
218 		else {
219 			curs(1, ROWNO + 4);
220 			cl_eos();
221 		}
222 	}
223 }
224 
225 int
226 page_line(const char *s)	/* returns 1 if we should quit */
227 {
228 	if (cury == LI - 1) {
229 		if (!*s)
230 			return (0);	/* suppress blank lines at top */
231 		putchar('\n');
232 		cury++;
233 		cmore("q\033");
234 		if (morc) {
235 			morc = 0;
236 			return (1);
237 		}
238 		if (whole_screen)
239 			cls();
240 		else {
241 			curs(1, ROWNO + 4);
242 			cl_eos();
243 		}
244 	}
245 	puts(s);
246 	cury++;
247 	return (0);
248 }
249 
250 /*
251  * Flexible pager: feed it with a number of lines and it will decide
252  * whether these should be fed to the pager above, or displayed in a
253  * corner.
254  * Call:
255  *	cornline(0, title or 0)	: initialize
256  *	cornline(1, text)	: add text to the chain of texts
257  *	cornline(2, morcs)	: output everything and cleanup
258  *	cornline(3, 0)		: cleanup
259  */
260 
261 void
262 cornline(int mode, const char *text)
263 {
264 	static struct line {
265 		struct line    *next_line;
266 		char           *line_text;
267 	}              *texthead, *texttail;
268 	static int      maxlen;
269 	static int      linect;
270 	struct line    *tl;
271 
272 	if (mode == 0) {
273 		texthead = 0;
274 		maxlen = 0;
275 		linect = 0;
276 		if (text) {
277 			cornline(1, text);	/* title */
278 			cornline(1, "");	/* blank line */
279 		}
280 		return;
281 	}
282 	if (mode == 1) {
283 		int             len;
284 
285 		if (!text)
286 			return;	/* superfluous, just to be sure */
287 		linect++;
288 		len = strlen(text);
289 		if (len > maxlen)
290 			maxlen = len;
291 		tl = (struct line *)
292 			alloc((unsigned) (len + sizeof(struct line) + 1));
293 		tl->next_line = 0;
294 		tl->line_text = (char *) (tl + 1);
295 		(void) strcpy(tl->line_text, text);
296 		if (!texthead)
297 			texthead = tl;
298 		else
299 			texttail->next_line = tl;
300 		texttail = tl;
301 		return;
302 	}
303 	/* --- now we really do it --- */
304 	if (mode == 2 && linect == 1)	/* topline only */
305 		pline(texthead->line_text);
306 	else if (mode == 2) {
307 		int             curline, lth;
308 
309 		if (flags.toplin == 1)
310 			more();	/* ab@unido */
311 		remember_topl();
312 
313 		lth = CO - maxlen - 2;	/* Use full screen width */
314 		if (linect < LI && lth >= 10) {	/* in a corner */
315 			home();
316 			cl_end();
317 			flags.toplin = 0;
318 			curline = 1;
319 			for (tl = texthead; tl; tl = tl->next_line) {
320 				curs(lth, curline);
321 				if (curline > 1)
322 					cl_end();
323 				putsym(' ');
324 				putstr(tl->line_text);
325 				curline++;
326 			}
327 			curs(lth, curline);
328 			cl_end();
329 			cmore(text);
330 			home();
331 			cl_end();
332 			docorner(lth, curline - 1);
333 		} else {	/* feed to pager */
334 			set_pager(0);
335 			for (tl = texthead; tl; tl = tl->next_line) {
336 				if (page_line(tl->line_text)) {
337 					set_pager(2);
338 					goto cleanup;
339 				}
340 			}
341 			if (text) {
342 				cgetret(text);
343 				set_pager(2);
344 			} else
345 				set_pager(1);
346 		}
347 	}
348 cleanup:
349 	while ((tl = texthead) != NULL) {
350 		texthead = tl->next_line;
351 		free((char *) tl);
352 	}
353 }
354 
355 int
356 dohelp(void)
357 {
358 	char            c;
359 
360 	pline("Long or short help? ");
361 	while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c))
362 		sound_bell();
363 	if (!strchr(quitchars, c))
364 		(void) page_file((c == 'l') ? HELP : SHELP, FALSE);
365 	return (0);
366 }
367 
368 /* return: 0 - cannot open fnam; 1 - otherwise */
369 static int
370 page_file(const char *fnam, boolean silent)
371 {
372 #ifdef DEF_PAGER		/* this implies that UNIX is defined */
373 	{
374 		/* use external pager; this may give security problems */
375 
376 		int             fd = open(fnam, O_RDONLY);
377 
378 		if (fd < 0) {
379 			if (!silent)
380 				pline("Cannot open %s.", fnam);
381 			return (0);
382 		}
383 		if (child(1)) {
384 
385 			/*
386 			 * Now that child() does a setuid(getuid()) and a
387 			 * chdir(), we may not be able to open file fnam
388 			 * anymore, so make it stdin.
389 			 */
390 			(void) close(0);
391 			if (dup(fd)) {
392 				if (!silent)
393 					printf("Cannot open %s as stdin.\n", fnam);
394 			} else {
395 				execl(catmore, "page", (char *) 0);
396 				if (!silent)
397 					printf("Cannot exec %s.\n", catmore);
398 			}
399 			exit(1);
400 		}
401 		(void) close(fd);
402 	}
403 #else	/* DEF_PAGER */
404 	{
405 		FILE           *f;	/* free after Robert Viduya */
406 
407 		if ((f = fopen(fnam, "r")) == (FILE *) 0) {
408 			if (!silent) {
409 				home();
410 				perror(fnam);
411 				flags.toplin = 1;
412 				pline("Cannot open %s.", fnam);
413 			}
414 			return (0);
415 		}
416 		page_more(f, 0);
417 	}
418 #endif	/* DEF_PAGER */
419 
420 	return (1);
421 }
422 
423 #ifdef UNIX
424 #ifdef SHELL
425 int
426 dosh(void)
427 {
428 	char           *str;
429 	if (child(0)) {
430 		if ((str = getenv("SHELL")) != NULL)
431 			execl(str, str, (char *) 0);
432 		else
433 			execl("/bin/sh", "sh", (char *) 0);
434 		pline("sh: cannot execute.");
435 		exit(1);
436 	}
437 	return (0);
438 }
439 #endif	/* SHELL */
440 
441 #ifdef NOWAITINCLUDE
442 union wait {			/* used only for the cast  (union wait *) 0  */
443 	int             w_status;
444 	struct {
445 		unsigned short  w_Termsig:7;
446 		unsigned short  w_Coredump:1;
447 		unsigned short  w_Retcode:8;
448 	}               w_T;
449 };
450 
451 #else
452 
453 #ifdef BSD
454 #include	<sys/wait.h>
455 #else
456 #include	<wait.h>
457 #endif	/* BSD */
458 #endif	/* NOWAITINCLUDE */
459 
460 static int
461 child(int wt)
462 {
463 	int             status;
464 	int             f;
465 
466 	f = fork();
467 	if (f == 0) {		/* child */
468 		settty((char *) 0);	/* also calls end_screen() */
469 		(void) setuid(getuid());
470 		(void) setgid(getgid());
471 #ifdef CHDIR
472 		(void) chdir(getenv("HOME"));
473 #endif	/* CHDIR */
474 		return (1);
475 	}
476 	if (f == -1) {		/* cannot fork */
477 		pline("Fork failed. Try again.");
478 		return (0);
479 	}
480 	/* fork succeeded; wait for child to exit */
481 	(void) signal(SIGINT, SIG_IGN);
482 	(void) signal(SIGQUIT, SIG_IGN);
483 	(void) wait(&status);
484 	gettty();
485 	setftty();
486 	(void) signal(SIGINT, done1);
487 #ifdef WIZARD
488 	if (wizard)
489 		(void) signal(SIGQUIT, SIG_DFL);
490 #endif	/* WIZARD */
491 	if (wt)
492 		getret();
493 	docrt();
494 	return (0);
495 }
496 #endif	/* UNIX */
497