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