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