xref: /netbsd-src/games/hack/hack.pager.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: hack.pager.c,v 1.7 2003/04/02 18:36:39 jsm 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.7 2003/04/02 18:36:39 jsm 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 int
83 dowhatis()
84 {
85 	FILE           *fp;
86 	char            bufr[BUFSZ + 6];
87 	char           *buf = &bufr[6], *ep, q;
88 
89 	if (!(fp = fopen(DATAFILE, "r")))
90 		pline("Cannot open data file!");
91 	else {
92 		pline("Specify what? ");
93 		q = readchar();
94 		if (q != '\t')
95 			while (fgets(buf, BUFSZ, fp))
96 				if (*buf == q) {
97 					ep = strchr(buf, '\n');
98 					if (ep)
99 						*ep = 0;
100 					/* else: bad data file */
101 					/* Expand tab 'by hand' */
102 					if (buf[1] == '\t') {
103 						buf = bufr;
104 						buf[0] = q;
105 						(void) strncpy(buf + 1, "       ", 7);
106 					}
107 					pline(buf);
108 					if (ep[-1] == ';') {
109 						pline("More info? ");
110 						if (readchar() == 'y') {
111 							page_more(fp, 1);	/* does fclose() */
112 							return (0);
113 						}
114 					}
115 					(void) fclose(fp);	/* kopper@psuvax1 */
116 					return (0);
117 				}
118 		pline("I've never heard of such things.");
119 		(void) fclose(fp);
120 	}
121 	return (0);
122 }
123 
124 /* make the paging of a file interruptible */
125 static int      got_intrup;
126 
127 void
128 intruph(n)
129 	int n __attribute__((__unused__));
130 {
131 	got_intrup++;
132 }
133 
134 /* simple pager, also used from dohelp() */
135 void
136 page_more(fp, strip)
137 	FILE           *fp;
138 	int             strip;	/* nr of chars to be stripped from each line
139 				 * (0 or 1) */
140 {
141 	char           *bufr, *ep;
142 	sig_t           prevsig = signal(SIGINT, intruph);
143 
144 	set_pager(0);
145 	bufr = (char *) alloc((unsigned) CO);
146 	bufr[CO - 1] = 0;
147 	while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) {
148 		ep = strchr(bufr, '\n');
149 		if (ep)
150 			*ep = 0;
151 		if (page_line(bufr + strip)) {
152 			set_pager(2);
153 			goto ret;
154 		}
155 	}
156 	set_pager(1);
157 ret:
158 	free(bufr);
159 	(void) fclose(fp);
160 	(void) signal(SIGINT, prevsig);
161 	got_intrup = 0;
162 }
163 
164 static boolean  whole_screen = TRUE;
165 #define	PAGMIN	12		/* minimum # of lines for page below level
166 				 * map */
167 
168 void
169 set_whole_screen()
170 {				/* called in termcap as soon as LI is known */
171 	whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD);
172 }
173 
174 #ifdef NEWS
175 int
176 readnews()
177 {
178 	int             ret;
179 
180 	whole_screen = TRUE;	/* force a docrt(), our first */
181 	ret = page_file(NEWS, TRUE);
182 	set_whole_screen();
183 	return (ret);		/* report whether we did docrt() */
184 }
185 #endif	/* NEWS */
186 
187 void
188 set_pager(mode)
189 	int             mode;	/* 0: open  1: wait+close  2: close */
190 {
191 	static boolean  so;
192 	if (mode == 0) {
193 		if (!whole_screen) {
194 			/* clear topline */
195 			clrlin();
196 			/* use part of screen below level map */
197 			curs(1, ROWNO + 4);
198 		} else {
199 			cls();
200 		}
201 		so = flags.standout;
202 		flags.standout = 1;
203 	} else {
204 		if (mode == 1) {
205 			curs(1, LI);
206 			more();
207 		}
208 		flags.standout = so;
209 		if (whole_screen)
210 			docrt();
211 		else {
212 			curs(1, ROWNO + 4);
213 			cl_eos();
214 		}
215 	}
216 }
217 
218 int
219 page_line(s)			/* returns 1 if we should quit */
220 	const char           *s;
221 {
222 	if (cury == LI - 1) {
223 		if (!*s)
224 			return (0);	/* suppress blank lines at top */
225 		putchar('\n');
226 		cury++;
227 		cmore("q\033");
228 		if (morc) {
229 			morc = 0;
230 			return (1);
231 		}
232 		if (whole_screen)
233 			cls();
234 		else {
235 			curs(1, ROWNO + 4);
236 			cl_eos();
237 		}
238 	}
239 	puts(s);
240 	cury++;
241 	return (0);
242 }
243 
244 /*
245  * Flexible pager: feed it with a number of lines and it will decide
246  * whether these should be fed to the pager above, or displayed in a
247  * corner.
248  * Call:
249  *	cornline(0, title or 0)	: initialize
250  *	cornline(1, text)	: add text to the chain of texts
251  *	cornline(2, morcs)	: output everything and cleanup
252  *	cornline(3, 0)		: cleanup
253  */
254 
255 void
256 cornline(mode, text)
257 	int             mode;
258 	const char           *text;
259 {
260 	static struct line {
261 		struct line    *next_line;
262 		char           *line_text;
263 	}              *texthead, *texttail;
264 	static int      maxlen;
265 	static int      linect;
266 	struct line    *tl;
267 
268 	if (mode == 0) {
269 		texthead = 0;
270 		maxlen = 0;
271 		linect = 0;
272 		if (text) {
273 			cornline(1, text);	/* title */
274 			cornline(1, "");	/* blank line */
275 		}
276 		return;
277 	}
278 	if (mode == 1) {
279 		int             len;
280 
281 		if (!text)
282 			return;	/* superfluous, just to be sure */
283 		linect++;
284 		len = strlen(text);
285 		if (len > maxlen)
286 			maxlen = len;
287 		tl = (struct line *)
288 			alloc((unsigned) (len + sizeof(struct line) + 1));
289 		tl->next_line = 0;
290 		tl->line_text = (char *) (tl + 1);
291 		(void) strcpy(tl->line_text, text);
292 		if (!texthead)
293 			texthead = tl;
294 		else
295 			texttail->next_line = tl;
296 		texttail = tl;
297 		return;
298 	}
299 	/* --- now we really do it --- */
300 	if (mode == 2 && linect == 1)	/* topline only */
301 		pline(texthead->line_text);
302 	else if (mode == 2) {
303 		int             curline, lth;
304 
305 		if (flags.toplin == 1)
306 			more();	/* ab@unido */
307 		remember_topl();
308 
309 		lth = CO - maxlen - 2;	/* Use full screen width */
310 		if (linect < LI && lth >= 10) {	/* in a corner */
311 			home();
312 			cl_end();
313 			flags.toplin = 0;
314 			curline = 1;
315 			for (tl = texthead; tl; tl = tl->next_line) {
316 				curs(lth, curline);
317 				if (curline > 1)
318 					cl_end();
319 				putsym(' ');
320 				putstr(tl->line_text);
321 				curline++;
322 			}
323 			curs(lth, curline);
324 			cl_end();
325 			cmore(text);
326 			home();
327 			cl_end();
328 			docorner(lth, curline - 1);
329 		} else {	/* feed to pager */
330 			set_pager(0);
331 			for (tl = texthead; tl; tl = tl->next_line) {
332 				if (page_line(tl->line_text)) {
333 					set_pager(2);
334 					goto cleanup;
335 				}
336 			}
337 			if (text) {
338 				cgetret(text);
339 				set_pager(2);
340 			} else
341 				set_pager(1);
342 		}
343 	}
344 cleanup:
345 	while ((tl = texthead) != NULL) {
346 		texthead = tl->next_line;
347 		free((char *) tl);
348 	}
349 }
350 
351 int
352 dohelp()
353 {
354 	char            c;
355 
356 	pline("Long or short help? ");
357 	while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c))
358 		bell();
359 	if (!strchr(quitchars, c))
360 		(void) page_file((c == 'l') ? HELP : SHELP, FALSE);
361 	return (0);
362 }
363 
364 int
365 page_file(fnam, silent)		/* return: 0 - cannot open fnam; 1 -
366 				 * otherwise */
367 	const char           *fnam;
368 	boolean         silent;
369 {
370 #ifdef DEF_PAGER		/* this implies that UNIX is defined */
371 	{
372 		/* use external pager; this may give security problems */
373 
374 		int             fd = open(fnam, O_RDONLY);
375 
376 		if (fd < 0) {
377 			if (!silent)
378 				pline("Cannot open %s.", fnam);
379 			return (0);
380 		}
381 		if (child(1)) {
382 
383 			/*
384 			 * Now that child() does a setuid(getuid()) and a
385 			 * chdir(), we may not be able to open file fnam
386 			 * anymore, so make it stdin.
387 			 */
388 			(void) close(0);
389 			if (dup(fd)) {
390 				if (!silent)
391 					printf("Cannot open %s as stdin.\n", fnam);
392 			} else {
393 				execl(catmore, "page", (char *) 0);
394 				if (!silent)
395 					printf("Cannot exec %s.\n", catmore);
396 			}
397 			exit(1);
398 		}
399 		(void) close(fd);
400 	}
401 #else	/* DEF_PAGER */
402 	{
403 		FILE           *f;	/* free after Robert Viduya */
404 
405 		if ((f = fopen(fnam, "r")) == (FILE *) 0) {
406 			if (!silent) {
407 				home();
408 				perror(fnam);
409 				flags.toplin = 1;
410 				pline("Cannot open %s.", fnam);
411 			}
412 			return (0);
413 		}
414 		page_more(f, 0);
415 	}
416 #endif	/* DEF_PAGER */
417 
418 	return (1);
419 }
420 
421 #ifdef UNIX
422 #ifdef SHELL
423 int
424 dosh()
425 {
426 	char           *str;
427 	if (child(0)) {
428 		if ((str = getenv("SHELL")) != NULL)
429 			execl(str, str, (char *) 0);
430 		else
431 			execl("/bin/sh", "sh", (char *) 0);
432 		pline("sh: cannot execute.");
433 		exit(1);
434 	}
435 	return (0);
436 }
437 #endif	/* SHELL */
438 
439 #ifdef NOWAITINCLUDE
440 union wait {			/* used only for the cast  (union wait *) 0  */
441 	int             w_status;
442 	struct {
443 		unsigned short  w_Termsig:7;
444 		unsigned short  w_Coredump:1;
445 		unsigned short  w_Retcode:8;
446 	}               w_T;
447 };
448 
449 #else
450 
451 #ifdef BSD
452 #include	<sys/wait.h>
453 #else
454 #include	<wait.h>
455 #endif	/* BSD */
456 #endif	/* NOWAITINCLUDE */
457 
458 int
459 child(int wt)
460 {
461 	int             status;
462 	int             f;
463 
464 	f = fork();
465 	if (f == 0) {		/* child */
466 		settty((char *) 0);	/* also calls end_screen() */
467 		(void) setuid(getuid());
468 		(void) setgid(getgid());
469 #ifdef CHDIR
470 		(void) chdir(getenv("HOME"));
471 #endif	/* CHDIR */
472 		return (1);
473 	}
474 	if (f == -1) {		/* cannot fork */
475 		pline("Fork failed. Try again.");
476 		return (0);
477 	}
478 	/* fork succeeded; wait for child to exit */
479 	(void) signal(SIGINT, SIG_IGN);
480 	(void) signal(SIGQUIT, SIG_IGN);
481 	(void) wait(&status);
482 	gettty();
483 	setftty();
484 	(void) signal(SIGINT, done1);
485 #ifdef WIZARD
486 	if (wizard)
487 		(void) signal(SIGQUIT, SIG_DFL);
488 #endif	/* WIZARD */
489 	if (wt)
490 		getret();
491 	docrt();
492 	return (0);
493 }
494 #endif	/* UNIX */
495