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