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