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