hack.pager.c revision 1.14 1 1.14 roy /* $NetBSD: hack.pager.c,v 1.14 2010/02/03 15:34:38 roy Exp $ */
2 1.5 christos
3 1.2 mycroft /*
4 1.7 jsm * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica,
5 1.7 jsm * Amsterdam
6 1.7 jsm * All rights reserved.
7 1.7 jsm *
8 1.7 jsm * Redistribution and use in source and binary forms, with or without
9 1.7 jsm * modification, are permitted provided that the following conditions are
10 1.7 jsm * met:
11 1.7 jsm *
12 1.7 jsm * - Redistributions of source code must retain the above copyright notice,
13 1.7 jsm * this list of conditions and the following disclaimer.
14 1.7 jsm *
15 1.7 jsm * - Redistributions in binary form must reproduce the above copyright
16 1.7 jsm * notice, this list of conditions and the following disclaimer in the
17 1.7 jsm * documentation and/or other materials provided with the distribution.
18 1.7 jsm *
19 1.7 jsm * - Neither the name of the Stichting Centrum voor Wiskunde en
20 1.7 jsm * Informatica, nor the names of its contributors may be used to endorse or
21 1.7 jsm * promote products derived from this software without specific prior
22 1.7 jsm * written permission.
23 1.7 jsm *
24 1.7 jsm * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 1.7 jsm * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26 1.7 jsm * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
27 1.7 jsm * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
28 1.7 jsm * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 1.7 jsm * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 1.7 jsm * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 1.7 jsm * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 1.7 jsm * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 1.7 jsm * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 1.7 jsm * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 1.7 jsm */
36 1.7 jsm
37 1.7 jsm /*
38 1.7 jsm * Copyright (c) 1982 Jay Fenlason <hack (at) gnu.org>
39 1.7 jsm * All rights reserved.
40 1.7 jsm *
41 1.7 jsm * Redistribution and use in source and binary forms, with or without
42 1.7 jsm * modification, are permitted provided that the following conditions
43 1.7 jsm * are met:
44 1.7 jsm * 1. Redistributions of source code must retain the above copyright
45 1.7 jsm * notice, this list of conditions and the following disclaimer.
46 1.7 jsm * 2. Redistributions in binary form must reproduce the above copyright
47 1.7 jsm * notice, this list of conditions and the following disclaimer in the
48 1.7 jsm * documentation and/or other materials provided with the distribution.
49 1.7 jsm * 3. The name of the author may not be used to endorse or promote products
50 1.7 jsm * derived from this software without specific prior written permission.
51 1.7 jsm *
52 1.7 jsm * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
53 1.7 jsm * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
54 1.7 jsm * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
55 1.7 jsm * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
56 1.7 jsm * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
57 1.7 jsm * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
58 1.7 jsm * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
59 1.7 jsm * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
60 1.7 jsm * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
61 1.7 jsm * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
62 1.2 mycroft */
63 1.2 mycroft
64 1.5 christos #include <sys/cdefs.h>
65 1.2 mycroft #ifndef lint
66 1.14 roy __RCSID("$NetBSD: hack.pager.c,v 1.14 2010/02/03 15:34:38 roy Exp $");
67 1.5 christos #endif /* not lint */
68 1.1 cgd
69 1.1 cgd /* This file contains the command routine dowhatis() and a pager. */
70 1.5 christos /*
71 1.5 christos * Also readmail() and doshell(), and generally the things that contact the
72 1.5 christos * outside world.
73 1.5 christos */
74 1.1 cgd
75 1.5 christos #include <sys/types.h>
76 1.5 christos #include <signal.h>
77 1.5 christos #include <stdlib.h>
78 1.5 christos #include <unistd.h>
79 1.1 cgd #include "hack.h"
80 1.5 christos #include "extern.h"
81 1.1 cgd
82 1.13 dholland static void intruph(int);
83 1.13 dholland static void page_more(FILE *, int);
84 1.13 dholland static int page_file(const char *, boolean);
85 1.13 dholland static int child(int);
86 1.13 dholland
87 1.5 christos int
88 1.12 dholland dowhatis(void)
89 1.1 cgd {
90 1.5 christos FILE *fp;
91 1.5 christos char bufr[BUFSZ + 6];
92 1.5 christos char *buf = &bufr[6], *ep, q;
93 1.1 cgd
94 1.5 christos if (!(fp = fopen(DATAFILE, "r")))
95 1.1 cgd pline("Cannot open data file!");
96 1.1 cgd else {
97 1.1 cgd pline("Specify what? ");
98 1.1 cgd q = readchar();
99 1.5 christos if (q != '\t')
100 1.5 christos while (fgets(buf, BUFSZ, fp))
101 1.5 christos if (*buf == q) {
102 1.5 christos ep = strchr(buf, '\n');
103 1.5 christos if (ep)
104 1.5 christos *ep = 0;
105 1.5 christos /* else: bad data file */
106 1.8 jnemeth else {
107 1.8 jnemeth pline("Bad data file!");
108 1.9 christos (void) fclose(fp);
109 1.8 jnemeth return(0);
110 1.8 jnemeth }
111 1.5 christos /* Expand tab 'by hand' */
112 1.5 christos if (buf[1] == '\t') {
113 1.5 christos buf = bufr;
114 1.5 christos buf[0] = q;
115 1.5 christos (void) strncpy(buf + 1, " ", 7);
116 1.5 christos }
117 1.5 christos pline(buf);
118 1.5 christos if (ep[-1] == ';') {
119 1.5 christos pline("More info? ");
120 1.5 christos if (readchar() == 'y') {
121 1.5 christos page_more(fp, 1); /* does fclose() */
122 1.5 christos return (0);
123 1.5 christos }
124 1.5 christos }
125 1.5 christos (void) fclose(fp); /* kopper@psuvax1 */
126 1.5 christos return (0);
127 1.1 cgd }
128 1.1 cgd pline("I've never heard of such things.");
129 1.1 cgd (void) fclose(fp);
130 1.1 cgd }
131 1.5 christos return (0);
132 1.1 cgd }
133 1.1 cgd
134 1.1 cgd /* make the paging of a file interruptible */
135 1.5 christos static int got_intrup;
136 1.1 cgd
137 1.13 dholland static void
138 1.12 dholland intruph(int n __unused)
139 1.5 christos {
140 1.1 cgd got_intrup++;
141 1.1 cgd }
142 1.1 cgd
143 1.1 cgd /* simple pager, also used from dohelp() */
144 1.12 dholland /* strip: nr of chars to be stripped from each line (0 or 1) */
145 1.13 dholland static void
146 1.12 dholland page_more(FILE *fp, int strip)
147 1.1 cgd {
148 1.5 christos char *bufr, *ep;
149 1.5 christos sig_t prevsig = signal(SIGINT, intruph);
150 1.1 cgd
151 1.1 cgd set_pager(0);
152 1.1 cgd bufr = (char *) alloc((unsigned) CO);
153 1.5 christos bufr[CO - 1] = 0;
154 1.5 christos while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) {
155 1.5 christos ep = strchr(bufr, '\n');
156 1.5 christos if (ep)
157 1.1 cgd *ep = 0;
158 1.5 christos if (page_line(bufr + strip)) {
159 1.1 cgd set_pager(2);
160 1.1 cgd goto ret;
161 1.1 cgd }
162 1.1 cgd }
163 1.1 cgd set_pager(1);
164 1.1 cgd ret:
165 1.1 cgd free(bufr);
166 1.1 cgd (void) fclose(fp);
167 1.1 cgd (void) signal(SIGINT, prevsig);
168 1.1 cgd got_intrup = 0;
169 1.1 cgd }
170 1.1 cgd
171 1.5 christos static boolean whole_screen = TRUE;
172 1.5 christos #define PAGMIN 12 /* minimum # of lines for page below level
173 1.5 christos * map */
174 1.1 cgd
175 1.5 christos void
176 1.12 dholland set_whole_screen(void)
177 1.5 christos { /* called in termcap as soon as LI is known */
178 1.5 christos whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD);
179 1.1 cgd }
180 1.1 cgd
181 1.1 cgd #ifdef NEWS
182 1.5 christos int
183 1.12 dholland readnews(void)
184 1.5 christos {
185 1.5 christos int ret;
186 1.1 cgd
187 1.1 cgd whole_screen = TRUE; /* force a docrt(), our first */
188 1.1 cgd ret = page_file(NEWS, TRUE);
189 1.1 cgd set_whole_screen();
190 1.5 christos return (ret); /* report whether we did docrt() */
191 1.1 cgd }
192 1.5 christos #endif /* NEWS */
193 1.1 cgd
194 1.12 dholland /* mode: 0: open 1: wait+close 2: close */
195 1.5 christos void
196 1.12 dholland set_pager(int mode)
197 1.1 cgd {
198 1.5 christos static boolean so;
199 1.5 christos if (mode == 0) {
200 1.5 christos if (!whole_screen) {
201 1.1 cgd /* clear topline */
202 1.1 cgd clrlin();
203 1.1 cgd /* use part of screen below level map */
204 1.5 christos curs(1, ROWNO + 4);
205 1.1 cgd } else {
206 1.1 cgd cls();
207 1.1 cgd }
208 1.1 cgd so = flags.standout;
209 1.1 cgd flags.standout = 1;
210 1.1 cgd } else {
211 1.5 christos if (mode == 1) {
212 1.1 cgd curs(1, LI);
213 1.1 cgd more();
214 1.1 cgd }
215 1.1 cgd flags.standout = so;
216 1.5 christos if (whole_screen)
217 1.1 cgd docrt();
218 1.1 cgd else {
219 1.5 christos curs(1, ROWNO + 4);
220 1.1 cgd cl_eos();
221 1.1 cgd }
222 1.1 cgd }
223 1.1 cgd }
224 1.1 cgd
225 1.5 christos int
226 1.12 dholland page_line(const char *s) /* returns 1 if we should quit */
227 1.1 cgd {
228 1.5 christos if (cury == LI - 1) {
229 1.5 christos if (!*s)
230 1.5 christos return (0); /* suppress blank lines at top */
231 1.1 cgd putchar('\n');
232 1.1 cgd cury++;
233 1.1 cgd cmore("q\033");
234 1.5 christos if (morc) {
235 1.1 cgd morc = 0;
236 1.5 christos return (1);
237 1.1 cgd }
238 1.5 christos if (whole_screen)
239 1.1 cgd cls();
240 1.1 cgd else {
241 1.5 christos curs(1, ROWNO + 4);
242 1.1 cgd cl_eos();
243 1.1 cgd }
244 1.1 cgd }
245 1.1 cgd puts(s);
246 1.1 cgd cury++;
247 1.5 christos return (0);
248 1.1 cgd }
249 1.1 cgd
250 1.1 cgd /*
251 1.1 cgd * Flexible pager: feed it with a number of lines and it will decide
252 1.1 cgd * whether these should be fed to the pager above, or displayed in a
253 1.1 cgd * corner.
254 1.1 cgd * Call:
255 1.1 cgd * cornline(0, title or 0) : initialize
256 1.1 cgd * cornline(1, text) : add text to the chain of texts
257 1.1 cgd * cornline(2, morcs) : output everything and cleanup
258 1.1 cgd * cornline(3, 0) : cleanup
259 1.1 cgd */
260 1.1 cgd
261 1.5 christos void
262 1.12 dholland cornline(int mode, const char *text)
263 1.1 cgd {
264 1.1 cgd static struct line {
265 1.5 christos struct line *next_line;
266 1.5 christos char *line_text;
267 1.5 christos } *texthead, *texttail;
268 1.5 christos static int maxlen;
269 1.5 christos static int linect;
270 1.5 christos struct line *tl;
271 1.1 cgd
272 1.5 christos if (mode == 0) {
273 1.1 cgd texthead = 0;
274 1.1 cgd maxlen = 0;
275 1.1 cgd linect = 0;
276 1.5 christos if (text) {
277 1.1 cgd cornline(1, text); /* title */
278 1.1 cgd cornline(1, ""); /* blank line */
279 1.1 cgd }
280 1.1 cgd return;
281 1.1 cgd }
282 1.5 christos if (mode == 1) {
283 1.5 christos int len;
284 1.1 cgd
285 1.5 christos if (!text)
286 1.5 christos return; /* superfluous, just to be sure */
287 1.5 christos linect++;
288 1.5 christos len = strlen(text);
289 1.5 christos if (len > maxlen)
290 1.5 christos maxlen = len;
291 1.5 christos tl = (struct line *)
292 1.5 christos alloc((unsigned) (len + sizeof(struct line) + 1));
293 1.5 christos tl->next_line = 0;
294 1.5 christos tl->line_text = (char *) (tl + 1);
295 1.5 christos (void) strcpy(tl->line_text, text);
296 1.5 christos if (!texthead)
297 1.5 christos texthead = tl;
298 1.5 christos else
299 1.5 christos texttail->next_line = tl;
300 1.5 christos texttail = tl;
301 1.5 christos return;
302 1.1 cgd }
303 1.1 cgd /* --- now we really do it --- */
304 1.5 christos if (mode == 2 && linect == 1) /* topline only */
305 1.1 cgd pline(texthead->line_text);
306 1.5 christos else if (mode == 2) {
307 1.5 christos int curline, lth;
308 1.5 christos
309 1.5 christos if (flags.toplin == 1)
310 1.5 christos more(); /* ab@unido */
311 1.5 christos remember_topl();
312 1.5 christos
313 1.5 christos lth = CO - maxlen - 2; /* Use full screen width */
314 1.5 christos if (linect < LI && lth >= 10) { /* in a corner */
315 1.5 christos home();
316 1.5 christos cl_end();
317 1.5 christos flags.toplin = 0;
318 1.5 christos curline = 1;
319 1.5 christos for (tl = texthead; tl; tl = tl->next_line) {
320 1.5 christos curs(lth, curline);
321 1.5 christos if (curline > 1)
322 1.5 christos cl_end();
323 1.5 christos putsym(' ');
324 1.5 christos putstr(tl->line_text);
325 1.5 christos curline++;
326 1.5 christos }
327 1.5 christos curs(lth, curline);
328 1.5 christos cl_end();
329 1.5 christos cmore(text);
330 1.5 christos home();
331 1.5 christos cl_end();
332 1.5 christos docorner(lth, curline - 1);
333 1.5 christos } else { /* feed to pager */
334 1.5 christos set_pager(0);
335 1.5 christos for (tl = texthead; tl; tl = tl->next_line) {
336 1.5 christos if (page_line(tl->line_text)) {
337 1.5 christos set_pager(2);
338 1.5 christos goto cleanup;
339 1.5 christos }
340 1.5 christos }
341 1.5 christos if (text) {
342 1.5 christos cgetret(text);
343 1.5 christos set_pager(2);
344 1.5 christos } else
345 1.5 christos set_pager(1);
346 1.1 cgd }
347 1.1 cgd }
348 1.1 cgd cleanup:
349 1.5 christos while ((tl = texthead) != NULL) {
350 1.1 cgd texthead = tl->next_line;
351 1.1 cgd free((char *) tl);
352 1.1 cgd }
353 1.1 cgd }
354 1.1 cgd
355 1.5 christos int
356 1.12 dholland dohelp(void)
357 1.1 cgd {
358 1.5 christos char c;
359 1.1 cgd
360 1.5 christos pline("Long or short help? ");
361 1.5 christos while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c))
362 1.14 roy sound_bell();
363 1.5 christos if (!strchr(quitchars, c))
364 1.1 cgd (void) page_file((c == 'l') ? HELP : SHELP, FALSE);
365 1.5 christos return (0);
366 1.1 cgd }
367 1.1 cgd
368 1.12 dholland /* return: 0 - cannot open fnam; 1 - otherwise */
369 1.13 dholland static int
370 1.12 dholland page_file(const char *fnam, boolean silent)
371 1.1 cgd {
372 1.5 christos #ifdef DEF_PAGER /* this implies that UNIX is defined */
373 1.5 christos {
374 1.5 christos /* use external pager; this may give security problems */
375 1.5 christos
376 1.6 jsm int fd = open(fnam, O_RDONLY);
377 1.5 christos
378 1.5 christos if (fd < 0) {
379 1.5 christos if (!silent)
380 1.5 christos pline("Cannot open %s.", fnam);
381 1.5 christos return (0);
382 1.5 christos }
383 1.5 christos if (child(1)) {
384 1.5 christos
385 1.5 christos /*
386 1.5 christos * Now that child() does a setuid(getuid()) and a
387 1.5 christos * chdir(), we may not be able to open file fnam
388 1.5 christos * anymore, so make it stdin.
389 1.5 christos */
390 1.5 christos (void) close(0);
391 1.5 christos if (dup(fd)) {
392 1.5 christos if (!silent)
393 1.5 christos printf("Cannot open %s as stdin.\n", fnam);
394 1.5 christos } else {
395 1.5 christos execl(catmore, "page", (char *) 0);
396 1.5 christos if (!silent)
397 1.5 christos printf("Cannot exec %s.\n", catmore);
398 1.5 christos }
399 1.5 christos exit(1);
400 1.5 christos }
401 1.5 christos (void) close(fd);
402 1.1 cgd }
403 1.5 christos #else /* DEF_PAGER */
404 1.5 christos {
405 1.5 christos FILE *f; /* free after Robert Viduya */
406 1.5 christos
407 1.5 christos if ((f = fopen(fnam, "r")) == (FILE *) 0) {
408 1.5 christos if (!silent) {
409 1.5 christos home();
410 1.5 christos perror(fnam);
411 1.5 christos flags.toplin = 1;
412 1.5 christos pline("Cannot open %s.", fnam);
413 1.5 christos }
414 1.5 christos return (0);
415 1.1 cgd }
416 1.5 christos page_more(f, 0);
417 1.1 cgd }
418 1.5 christos #endif /* DEF_PAGER */
419 1.1 cgd
420 1.5 christos return (1);
421 1.1 cgd }
422 1.1 cgd
423 1.1 cgd #ifdef UNIX
424 1.1 cgd #ifdef SHELL
425 1.5 christos int
426 1.12 dholland dosh(void)
427 1.5 christos {
428 1.5 christos char *str;
429 1.5 christos if (child(0)) {
430 1.5 christos if ((str = getenv("SHELL")) != NULL)
431 1.1 cgd execl(str, str, (char *) 0);
432 1.1 cgd else
433 1.1 cgd execl("/bin/sh", "sh", (char *) 0);
434 1.1 cgd pline("sh: cannot execute.");
435 1.1 cgd exit(1);
436 1.1 cgd }
437 1.5 christos return (0);
438 1.1 cgd }
439 1.5 christos #endif /* SHELL */
440 1.1 cgd
441 1.1 cgd #ifdef NOWAITINCLUDE
442 1.5 christos union wait { /* used only for the cast (union wait *) 0 */
443 1.5 christos int w_status;
444 1.1 cgd struct {
445 1.5 christos unsigned short w_Termsig:7;
446 1.5 christos unsigned short w_Coredump:1;
447 1.5 christos unsigned short w_Retcode:8;
448 1.5 christos } w_T;
449 1.1 cgd };
450 1.1 cgd
451 1.1 cgd #else
452 1.1 cgd
453 1.1 cgd #ifdef BSD
454 1.1 cgd #include <sys/wait.h>
455 1.1 cgd #else
456 1.1 cgd #include <wait.h>
457 1.5 christos #endif /* BSD */
458 1.5 christos #endif /* NOWAITINCLUDE */
459 1.1 cgd
460 1.13 dholland static int
461 1.6 jsm child(int wt)
462 1.5 christos {
463 1.5 christos int status;
464 1.5 christos int f;
465 1.1 cgd
466 1.1 cgd f = fork();
467 1.5 christos if (f == 0) { /* child */
468 1.5 christos settty((char *) 0); /* also calls end_screen() */
469 1.1 cgd (void) setuid(getuid());
470 1.1 cgd (void) setgid(getgid());
471 1.1 cgd #ifdef CHDIR
472 1.1 cgd (void) chdir(getenv("HOME"));
473 1.5 christos #endif /* CHDIR */
474 1.5 christos return (1);
475 1.1 cgd }
476 1.5 christos if (f == -1) { /* cannot fork */
477 1.1 cgd pline("Fork failed. Try again.");
478 1.5 christos return (0);
479 1.1 cgd }
480 1.1 cgd /* fork succeeded; wait for child to exit */
481 1.5 christos (void) signal(SIGINT, SIG_IGN);
482 1.5 christos (void) signal(SIGQUIT, SIG_IGN);
483 1.1 cgd (void) wait(&status);
484 1.1 cgd gettty();
485 1.1 cgd setftty();
486 1.5 christos (void) signal(SIGINT, done1);
487 1.1 cgd #ifdef WIZARD
488 1.5 christos if (wizard)
489 1.5 christos (void) signal(SIGQUIT, SIG_DFL);
490 1.5 christos #endif /* WIZARD */
491 1.5 christos if (wt)
492 1.5 christos getret();
493 1.1 cgd docrt();
494 1.5 christos return (0);
495 1.1 cgd }
496 1.5 christos #endif /* UNIX */
497