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@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 /* This file contains the command routine dowhatis() and a pager. */
65 /*
66 * Also readmail() and doshell(), and generally the things that contact the
67 * outside world.
68 */
69
70 #include <sys/types.h>
71 #include <sys/wait.h>
72 #include <signal.h>
73 #include <stdlib.h>
74 #include <unistd.h>
75 #include "hack.h"
76 #include "extern.h"
77
78 static void intruph(int);
79 static void page_more(FILE *, int);
80 static int page_file(const char *, boolean);
81 static int child(int);
82
83 int
dowhatis(void)84 dowhatis(void)
85 {
86 FILE *fp;
87 char bufr[BUFSZ + 6];
88 char *buf = &bufr[6], *ep, q;
89
90 if (!(fp = fopen(DATAFILE, "r")))
91 pline("Cannot open data file!");
92 else {
93 pline("Specify what? ");
94 q = readchar();
95 if (q != '\t')
96 while (fgets(buf, BUFSZ, fp))
97 if (*buf == q) {
98 ep = strchr(buf, '\n');
99 if (ep)
100 *ep = 0;
101 /* else: bad data file */
102 else {
103 pline("Bad data file!");
104 (void) fclose(fp);
105 return(0);
106 }
107 /* Expand tab 'by hand' */
108 if (buf[1] == '\t') {
109 buf = bufr;
110 buf[0] = q;
111 (void) strncpy(buf + 1, " ", 7);
112 }
113 pline("%s", buf);
114 if (ep[-1] == ';') {
115 pline("More info? ");
116 if (readchar() == 'y') {
117 page_more(fp, 1); /* does fclose() */
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 static void
intruph(int n __unused)134 intruph(int n __unused)
135 {
136 got_intrup++;
137 }
138
139 /* simple pager, also used from dohelp() */
140 /* strip: nr of chars to be stripped from each line (0 or 1) */
141 static void
page_more(FILE * fp,int strip)142 page_more(FILE *fp, int strip)
143 {
144 char *bufr, *ep;
145 sig_t prevsig = signal(SIGINT, intruph);
146
147 set_pager(0);
148 bufr = alloc(CO);
149 bufr[CO - 1] = 0;
150 while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) {
151 ep = strchr(bufr, '\n');
152 if (ep)
153 *ep = 0;
154 if (page_line(bufr + strip)) {
155 set_pager(2);
156 goto ret;
157 }
158 }
159 set_pager(1);
160 ret:
161 free(bufr);
162 (void) fclose(fp);
163 (void) signal(SIGINT, prevsig);
164 got_intrup = 0;
165 }
166
167 static boolean whole_screen = TRUE;
168 #define PAGMIN 12 /* minimum # of lines for page below level
169 * map */
170
171 void
set_whole_screen(void)172 set_whole_screen(void)
173 { /* called in termcap as soon as LI is known */
174 whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD);
175 }
176
177 #ifdef NEWS
178 int
readnews(void)179 readnews(void)
180 {
181 int ret;
182
183 whole_screen = TRUE; /* force a docrt(), our first */
184 ret = page_file(NEWS, TRUE);
185 set_whole_screen();
186 return (ret); /* report whether we did docrt() */
187 }
188 #endif /* NEWS */
189
190 /* mode: 0: open 1: wait+close 2: close */
191 void
set_pager(int mode)192 set_pager(int mode)
193 {
194 static boolean so;
195 if (mode == 0) {
196 if (!whole_screen) {
197 /* clear topline */
198 clrlin();
199 /* use part of screen below level map */
200 curs(1, ROWNO + 4);
201 } else {
202 cls();
203 }
204 so = flags.standout;
205 flags.standout = 1;
206 } else {
207 if (mode == 1) {
208 curs(1, LI);
209 more();
210 }
211 flags.standout = so;
212 if (whole_screen)
213 docrt();
214 else {
215 curs(1, ROWNO + 4);
216 cl_eos();
217 }
218 }
219 }
220
221 int
page_line(const char * s)222 page_line(const char *s) /* returns 1 if we should quit */
223 {
224 if (cury == LI - 1) {
225 if (!*s)
226 return (0); /* suppress blank lines at top */
227 putchar('\n');
228 cury++;
229 cmore("q\033");
230 if (morc) {
231 morc = 0;
232 return (1);
233 }
234 if (whole_screen)
235 cls();
236 else {
237 curs(1, ROWNO + 4);
238 cl_eos();
239 }
240 }
241 puts(s);
242 cury++;
243 return (0);
244 }
245
246 /*
247 * Flexible pager: feed it with a number of lines and it will decide
248 * whether these should be fed to the pager above, or displayed in a
249 * corner.
250 * Call:
251 * cornline(0, title or 0) : initialize
252 * cornline(1, text) : add text to the chain of texts
253 * cornline(2, morcs) : output everything and cleanup
254 * cornline(3, 0) : cleanup
255 */
256
257 void
cornline(int mode,const char * text)258 cornline(int mode, const char *text)
259 {
260 static struct line {
261 struct line *next_line;
262 char *line_text;
263 } *texthead, *texttail;
264 static int maxlen;
265 static int linect;
266 struct line *tl;
267
268 if (mode == 0) {
269 texthead = 0;
270 maxlen = 0;
271 linect = 0;
272 if (text) {
273 cornline(1, text); /* title */
274 cornline(1, ""); /* blank line */
275 }
276 return;
277 }
278 if (mode == 1) {
279 int len;
280
281 if (!text)
282 return; /* superfluous, just to be sure */
283 linect++;
284 len = strlen(text);
285 if (len > maxlen)
286 maxlen = len;
287 tl = alloc(len + sizeof(*tl) + 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("%s", 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(tl);
347 }
348 }
349
350 int
dohelp(void)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 sound_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 static int
page_file(const char * fnam,boolean silent)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 *)NULL);
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
dosh(void)421 dosh(void)
422 {
423 char *str;
424 if (child(0)) {
425 if ((str = getenv("SHELL")) != NULL)
426 execl(str, str, (char *)NULL);
427 else
428 execl("/bin/sh", "sh", (char *)NULL);
429 pline("sh: cannot execute.");
430 exit(1);
431 }
432 return (0);
433 }
434 #endif /* SHELL */
435
436 static int
child(int wt)437 child(int wt)
438 {
439 int status;
440 int f;
441
442 f = fork();
443 if (f == 0) { /* child */
444 settty(NULL); /* also calls end_screen() */
445 (void) setuid(getuid());
446 (void) setgid(getgid());
447 #ifdef CHDIR
448 (void) chdir(getenv("HOME"));
449 #endif /* CHDIR */
450 return (1);
451 }
452 if (f == -1) { /* cannot fork */
453 pline("Fork failed. Try again.");
454 return (0);
455 }
456 /* fork succeeded; wait for child to exit */
457 (void) signal(SIGINT, SIG_IGN);
458 (void) signal(SIGQUIT, SIG_IGN);
459 (void) wait(&status);
460 gettty();
461 setftty();
462 (void) signal(SIGINT, done1);
463 #ifdef WIZARD
464 if (wizard)
465 (void) signal(SIGQUIT, SIG_DFL);
466 #endif /* WIZARD */
467 if (wt)
468 getret();
469 docrt();
470 return (0);
471 }
472 #endif /* UNIX */
473