xref: /dragonfly/games/hack/hack.pager.c (revision 4318c66eac379e15105fe145d406dfef81b795f6)
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