Home | History | Annotate | Line # | Download | only in hack
      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 (at) 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.21 2011/09/01 07:18:50 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 
     83 static void intruph(int);
     84 static void page_more(FILE *, int);
     85 static int page_file(const char *, boolean);
     86 static int child(int);
     87 
     88 int
     89 dowhatis(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 */
    136 static int      got_intrup;
    137 
    138 static void
    139 intruph(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) */
    146 static void
    147 page_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);
    165 ret:
    166 	free(bufr);
    167 	(void) fclose(fp);
    168 	(void) signal(SIGINT, prevsig);
    169 	got_intrup = 0;
    170 }
    171 
    172 static boolean  whole_screen = TRUE;
    173 #define	PAGMIN	12		/* minimum # of lines for page below level
    174 				 * map */
    175 
    176 void
    177 set_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
    183 int
    184 readnews(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 */
    196 void
    197 set_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 
    226 int
    227 page_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 
    262 void
    263 cornline(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 	}
    348 cleanup:
    349 	while ((tl = texthead) != NULL) {
    350 		texthead = tl->next_line;
    351 		free(tl);
    352 	}
    353 }
    354 
    355 int
    356 dohelp(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 */
    369 static int
    370 page_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
    425 int
    426 dosh(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 
    441 static int
    442 child(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