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 */ 420