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