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