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