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