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", NULL); 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")) == NULL) { 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, NULL); 353 else 354 execl("/bin/sh", "sh", NULL); 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(NULL); /* 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 */ 416