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