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