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