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