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