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