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