hack.pager.c revision 1.6 1 1.6 jsm /* $NetBSD: hack.pager.c,v 1.6 2001/03/25 20:44:02 jsm Exp $ */
2 1.5 christos
3 1.2 mycroft /*
4 1.2 mycroft * Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985.
5 1.2 mycroft */
6 1.2 mycroft
7 1.5 christos #include <sys/cdefs.h>
8 1.2 mycroft #ifndef lint
9 1.6 jsm __RCSID("$NetBSD: hack.pager.c,v 1.6 2001/03/25 20:44:02 jsm Exp $");
10 1.5 christos #endif /* not lint */
11 1.1 cgd
12 1.1 cgd /* This file contains the command routine dowhatis() and a pager. */
13 1.5 christos /*
14 1.5 christos * Also readmail() and doshell(), and generally the things that contact the
15 1.5 christos * outside world.
16 1.5 christos */
17 1.1 cgd
18 1.5 christos #include <sys/types.h>
19 1.5 christos #include <signal.h>
20 1.5 christos #include <stdlib.h>
21 1.5 christos #include <unistd.h>
22 1.1 cgd #include "hack.h"
23 1.5 christos #include "extern.h"
24 1.1 cgd
25 1.5 christos int
26 1.1 cgd dowhatis()
27 1.1 cgd {
28 1.5 christos FILE *fp;
29 1.5 christos char bufr[BUFSZ + 6];
30 1.5 christos char *buf = &bufr[6], *ep, q;
31 1.1 cgd
32 1.5 christos if (!(fp = fopen(DATAFILE, "r")))
33 1.1 cgd pline("Cannot open data file!");
34 1.1 cgd else {
35 1.1 cgd pline("Specify what? ");
36 1.1 cgd q = readchar();
37 1.5 christos if (q != '\t')
38 1.5 christos while (fgets(buf, BUFSZ, fp))
39 1.5 christos if (*buf == q) {
40 1.5 christos ep = strchr(buf, '\n');
41 1.5 christos if (ep)
42 1.5 christos *ep = 0;
43 1.5 christos /* else: bad data file */
44 1.5 christos /* Expand tab 'by hand' */
45 1.5 christos if (buf[1] == '\t') {
46 1.5 christos buf = bufr;
47 1.5 christos buf[0] = q;
48 1.5 christos (void) strncpy(buf + 1, " ", 7);
49 1.5 christos }
50 1.5 christos pline(buf);
51 1.5 christos if (ep[-1] == ';') {
52 1.5 christos pline("More info? ");
53 1.5 christos if (readchar() == 'y') {
54 1.5 christos page_more(fp, 1); /* does fclose() */
55 1.5 christos return (0);
56 1.5 christos }
57 1.5 christos }
58 1.5 christos (void) fclose(fp); /* kopper@psuvax1 */
59 1.5 christos return (0);
60 1.1 cgd }
61 1.1 cgd pline("I've never heard of such things.");
62 1.1 cgd (void) fclose(fp);
63 1.1 cgd }
64 1.5 christos return (0);
65 1.1 cgd }
66 1.1 cgd
67 1.1 cgd /* make the paging of a file interruptible */
68 1.5 christos static int got_intrup;
69 1.1 cgd
70 1.1 cgd void
71 1.5 christos intruph(n)
72 1.6 jsm int n __attribute__((__unused__));
73 1.5 christos {
74 1.1 cgd got_intrup++;
75 1.1 cgd }
76 1.1 cgd
77 1.1 cgd /* simple pager, also used from dohelp() */
78 1.5 christos void
79 1.5 christos page_more(fp, strip)
80 1.5 christos FILE *fp;
81 1.5 christos int strip; /* nr of chars to be stripped from each line
82 1.5 christos * (0 or 1) */
83 1.1 cgd {
84 1.5 christos char *bufr, *ep;
85 1.5 christos sig_t prevsig = signal(SIGINT, intruph);
86 1.1 cgd
87 1.1 cgd set_pager(0);
88 1.1 cgd bufr = (char *) alloc((unsigned) CO);
89 1.5 christos bufr[CO - 1] = 0;
90 1.5 christos while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) {
91 1.5 christos ep = strchr(bufr, '\n');
92 1.5 christos if (ep)
93 1.1 cgd *ep = 0;
94 1.5 christos if (page_line(bufr + strip)) {
95 1.1 cgd set_pager(2);
96 1.1 cgd goto ret;
97 1.1 cgd }
98 1.1 cgd }
99 1.1 cgd set_pager(1);
100 1.1 cgd ret:
101 1.1 cgd free(bufr);
102 1.1 cgd (void) fclose(fp);
103 1.1 cgd (void) signal(SIGINT, prevsig);
104 1.1 cgd got_intrup = 0;
105 1.1 cgd }
106 1.1 cgd
107 1.5 christos static boolean whole_screen = TRUE;
108 1.5 christos #define PAGMIN 12 /* minimum # of lines for page below level
109 1.5 christos * map */
110 1.1 cgd
111 1.5 christos void
112 1.5 christos set_whole_screen()
113 1.5 christos { /* called in termcap as soon as LI is known */
114 1.5 christos whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD);
115 1.1 cgd }
116 1.1 cgd
117 1.1 cgd #ifdef NEWS
118 1.5 christos int
119 1.5 christos readnews()
120 1.5 christos {
121 1.5 christos int ret;
122 1.1 cgd
123 1.1 cgd whole_screen = TRUE; /* force a docrt(), our first */
124 1.1 cgd ret = page_file(NEWS, TRUE);
125 1.1 cgd set_whole_screen();
126 1.5 christos return (ret); /* report whether we did docrt() */
127 1.1 cgd }
128 1.5 christos #endif /* NEWS */
129 1.1 cgd
130 1.5 christos void
131 1.1 cgd set_pager(mode)
132 1.5 christos int mode; /* 0: open 1: wait+close 2: close */
133 1.1 cgd {
134 1.5 christos static boolean so;
135 1.5 christos if (mode == 0) {
136 1.5 christos if (!whole_screen) {
137 1.1 cgd /* clear topline */
138 1.1 cgd clrlin();
139 1.1 cgd /* use part of screen below level map */
140 1.5 christos curs(1, ROWNO + 4);
141 1.1 cgd } else {
142 1.1 cgd cls();
143 1.1 cgd }
144 1.1 cgd so = flags.standout;
145 1.1 cgd flags.standout = 1;
146 1.1 cgd } else {
147 1.5 christos if (mode == 1) {
148 1.1 cgd curs(1, LI);
149 1.1 cgd more();
150 1.1 cgd }
151 1.1 cgd flags.standout = so;
152 1.5 christos if (whole_screen)
153 1.1 cgd docrt();
154 1.1 cgd else {
155 1.5 christos curs(1, ROWNO + 4);
156 1.1 cgd cl_eos();
157 1.1 cgd }
158 1.1 cgd }
159 1.1 cgd }
160 1.1 cgd
161 1.5 christos int
162 1.5 christos page_line(s) /* returns 1 if we should quit */
163 1.6 jsm const char *s;
164 1.1 cgd {
165 1.5 christos if (cury == LI - 1) {
166 1.5 christos if (!*s)
167 1.5 christos return (0); /* suppress blank lines at top */
168 1.1 cgd putchar('\n');
169 1.1 cgd cury++;
170 1.1 cgd cmore("q\033");
171 1.5 christos if (morc) {
172 1.1 cgd morc = 0;
173 1.5 christos return (1);
174 1.1 cgd }
175 1.5 christos if (whole_screen)
176 1.1 cgd cls();
177 1.1 cgd else {
178 1.5 christos curs(1, ROWNO + 4);
179 1.1 cgd cl_eos();
180 1.1 cgd }
181 1.1 cgd }
182 1.1 cgd puts(s);
183 1.1 cgd cury++;
184 1.5 christos return (0);
185 1.1 cgd }
186 1.1 cgd
187 1.1 cgd /*
188 1.1 cgd * Flexible pager: feed it with a number of lines and it will decide
189 1.1 cgd * whether these should be fed to the pager above, or displayed in a
190 1.1 cgd * corner.
191 1.1 cgd * Call:
192 1.1 cgd * cornline(0, title or 0) : initialize
193 1.1 cgd * cornline(1, text) : add text to the chain of texts
194 1.1 cgd * cornline(2, morcs) : output everything and cleanup
195 1.1 cgd * cornline(3, 0) : cleanup
196 1.1 cgd */
197 1.1 cgd
198 1.5 christos void
199 1.1 cgd cornline(mode, text)
200 1.5 christos int mode;
201 1.6 jsm const char *text;
202 1.1 cgd {
203 1.1 cgd static struct line {
204 1.5 christos struct line *next_line;
205 1.5 christos char *line_text;
206 1.5 christos } *texthead, *texttail;
207 1.5 christos static int maxlen;
208 1.5 christos static int linect;
209 1.5 christos struct line *tl;
210 1.1 cgd
211 1.5 christos if (mode == 0) {
212 1.1 cgd texthead = 0;
213 1.1 cgd maxlen = 0;
214 1.1 cgd linect = 0;
215 1.5 christos if (text) {
216 1.1 cgd cornline(1, text); /* title */
217 1.1 cgd cornline(1, ""); /* blank line */
218 1.1 cgd }
219 1.1 cgd return;
220 1.1 cgd }
221 1.5 christos if (mode == 1) {
222 1.5 christos int len;
223 1.1 cgd
224 1.5 christos if (!text)
225 1.5 christos return; /* superfluous, just to be sure */
226 1.5 christos linect++;
227 1.5 christos len = strlen(text);
228 1.5 christos if (len > maxlen)
229 1.5 christos maxlen = len;
230 1.5 christos tl = (struct line *)
231 1.5 christos alloc((unsigned) (len + sizeof(struct line) + 1));
232 1.5 christos tl->next_line = 0;
233 1.5 christos tl->line_text = (char *) (tl + 1);
234 1.5 christos (void) strcpy(tl->line_text, text);
235 1.5 christos if (!texthead)
236 1.5 christos texthead = tl;
237 1.5 christos else
238 1.5 christos texttail->next_line = tl;
239 1.5 christos texttail = tl;
240 1.5 christos return;
241 1.1 cgd }
242 1.1 cgd /* --- now we really do it --- */
243 1.5 christos if (mode == 2 && linect == 1) /* topline only */
244 1.1 cgd pline(texthead->line_text);
245 1.5 christos else if (mode == 2) {
246 1.5 christos int curline, lth;
247 1.5 christos
248 1.5 christos if (flags.toplin == 1)
249 1.5 christos more(); /* ab@unido */
250 1.5 christos remember_topl();
251 1.5 christos
252 1.5 christos lth = CO - maxlen - 2; /* Use full screen width */
253 1.5 christos if (linect < LI && lth >= 10) { /* in a corner */
254 1.5 christos home();
255 1.5 christos cl_end();
256 1.5 christos flags.toplin = 0;
257 1.5 christos curline = 1;
258 1.5 christos for (tl = texthead; tl; tl = tl->next_line) {
259 1.5 christos curs(lth, curline);
260 1.5 christos if (curline > 1)
261 1.5 christos cl_end();
262 1.5 christos putsym(' ');
263 1.5 christos putstr(tl->line_text);
264 1.5 christos curline++;
265 1.5 christos }
266 1.5 christos curs(lth, curline);
267 1.5 christos cl_end();
268 1.5 christos cmore(text);
269 1.5 christos home();
270 1.5 christos cl_end();
271 1.5 christos docorner(lth, curline - 1);
272 1.5 christos } else { /* feed to pager */
273 1.5 christos set_pager(0);
274 1.5 christos for (tl = texthead; tl; tl = tl->next_line) {
275 1.5 christos if (page_line(tl->line_text)) {
276 1.5 christos set_pager(2);
277 1.5 christos goto cleanup;
278 1.5 christos }
279 1.5 christos }
280 1.5 christos if (text) {
281 1.5 christos cgetret(text);
282 1.5 christos set_pager(2);
283 1.5 christos } else
284 1.5 christos set_pager(1);
285 1.1 cgd }
286 1.1 cgd }
287 1.1 cgd cleanup:
288 1.5 christos while ((tl = texthead) != NULL) {
289 1.1 cgd texthead = tl->next_line;
290 1.1 cgd free((char *) tl);
291 1.1 cgd }
292 1.1 cgd }
293 1.1 cgd
294 1.5 christos int
295 1.1 cgd dohelp()
296 1.1 cgd {
297 1.5 christos char c;
298 1.1 cgd
299 1.5 christos pline("Long or short help? ");
300 1.5 christos while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c))
301 1.5 christos bell();
302 1.5 christos if (!strchr(quitchars, c))
303 1.1 cgd (void) page_file((c == 'l') ? HELP : SHELP, FALSE);
304 1.5 christos return (0);
305 1.1 cgd }
306 1.1 cgd
307 1.5 christos int
308 1.5 christos page_file(fnam, silent) /* return: 0 - cannot open fnam; 1 -
309 1.5 christos * otherwise */
310 1.6 jsm const char *fnam;
311 1.5 christos boolean silent;
312 1.1 cgd {
313 1.5 christos #ifdef DEF_PAGER /* this implies that UNIX is defined */
314 1.5 christos {
315 1.5 christos /* use external pager; this may give security problems */
316 1.5 christos
317 1.6 jsm int fd = open(fnam, O_RDONLY);
318 1.5 christos
319 1.5 christos if (fd < 0) {
320 1.5 christos if (!silent)
321 1.5 christos pline("Cannot open %s.", fnam);
322 1.5 christos return (0);
323 1.5 christos }
324 1.5 christos if (child(1)) {
325 1.5 christos
326 1.5 christos /*
327 1.5 christos * Now that child() does a setuid(getuid()) and a
328 1.5 christos * chdir(), we may not be able to open file fnam
329 1.5 christos * anymore, so make it stdin.
330 1.5 christos */
331 1.5 christos (void) close(0);
332 1.5 christos if (dup(fd)) {
333 1.5 christos if (!silent)
334 1.5 christos printf("Cannot open %s as stdin.\n", fnam);
335 1.5 christos } else {
336 1.5 christos execl(catmore, "page", (char *) 0);
337 1.5 christos if (!silent)
338 1.5 christos printf("Cannot exec %s.\n", catmore);
339 1.5 christos }
340 1.5 christos exit(1);
341 1.5 christos }
342 1.5 christos (void) close(fd);
343 1.1 cgd }
344 1.5 christos #else /* DEF_PAGER */
345 1.5 christos {
346 1.5 christos FILE *f; /* free after Robert Viduya */
347 1.5 christos
348 1.5 christos if ((f = fopen(fnam, "r")) == (FILE *) 0) {
349 1.5 christos if (!silent) {
350 1.5 christos home();
351 1.5 christos perror(fnam);
352 1.5 christos flags.toplin = 1;
353 1.5 christos pline("Cannot open %s.", fnam);
354 1.5 christos }
355 1.5 christos return (0);
356 1.1 cgd }
357 1.5 christos page_more(f, 0);
358 1.1 cgd }
359 1.5 christos #endif /* DEF_PAGER */
360 1.1 cgd
361 1.5 christos return (1);
362 1.1 cgd }
363 1.1 cgd
364 1.1 cgd #ifdef UNIX
365 1.1 cgd #ifdef SHELL
366 1.5 christos int
367 1.5 christos dosh()
368 1.5 christos {
369 1.5 christos char *str;
370 1.5 christos if (child(0)) {
371 1.5 christos if ((str = getenv("SHELL")) != NULL)
372 1.1 cgd execl(str, str, (char *) 0);
373 1.1 cgd else
374 1.1 cgd execl("/bin/sh", "sh", (char *) 0);
375 1.1 cgd pline("sh: cannot execute.");
376 1.1 cgd exit(1);
377 1.1 cgd }
378 1.5 christos return (0);
379 1.1 cgd }
380 1.5 christos #endif /* SHELL */
381 1.1 cgd
382 1.1 cgd #ifdef NOWAITINCLUDE
383 1.5 christos union wait { /* used only for the cast (union wait *) 0 */
384 1.5 christos int w_status;
385 1.1 cgd struct {
386 1.5 christos unsigned short w_Termsig:7;
387 1.5 christos unsigned short w_Coredump:1;
388 1.5 christos unsigned short w_Retcode:8;
389 1.5 christos } w_T;
390 1.1 cgd };
391 1.1 cgd
392 1.1 cgd #else
393 1.1 cgd
394 1.1 cgd #ifdef BSD
395 1.1 cgd #include <sys/wait.h>
396 1.1 cgd #else
397 1.1 cgd #include <wait.h>
398 1.5 christos #endif /* BSD */
399 1.5 christos #endif /* NOWAITINCLUDE */
400 1.1 cgd
401 1.5 christos int
402 1.6 jsm child(int wt)
403 1.5 christos {
404 1.5 christos int status;
405 1.5 christos int f;
406 1.1 cgd
407 1.1 cgd f = fork();
408 1.5 christos if (f == 0) { /* child */
409 1.5 christos settty((char *) 0); /* also calls end_screen() */
410 1.1 cgd (void) setuid(getuid());
411 1.1 cgd (void) setgid(getgid());
412 1.1 cgd #ifdef CHDIR
413 1.1 cgd (void) chdir(getenv("HOME"));
414 1.5 christos #endif /* CHDIR */
415 1.5 christos return (1);
416 1.1 cgd }
417 1.5 christos if (f == -1) { /* cannot fork */
418 1.1 cgd pline("Fork failed. Try again.");
419 1.5 christos return (0);
420 1.1 cgd }
421 1.1 cgd /* fork succeeded; wait for child to exit */
422 1.5 christos (void) signal(SIGINT, SIG_IGN);
423 1.5 christos (void) signal(SIGQUIT, SIG_IGN);
424 1.1 cgd (void) wait(&status);
425 1.1 cgd gettty();
426 1.1 cgd setftty();
427 1.5 christos (void) signal(SIGINT, done1);
428 1.1 cgd #ifdef WIZARD
429 1.5 christos if (wizard)
430 1.5 christos (void) signal(SIGQUIT, SIG_DFL);
431 1.5 christos #endif /* WIZARD */
432 1.5 christos if (wt)
433 1.5 christos getret();
434 1.1 cgd docrt();
435 1.5 christos return (0);
436 1.1 cgd }
437 1.5 christos #endif /* UNIX */
438