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