1 /* $NetBSD: hack.pager.c,v 1.11 2007/12/15 19:44:41 perry Exp $ */ 2 3 /* 4 * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, 5 * Amsterdam 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are 10 * met: 11 * 12 * - Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * - Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * - Neither the name of the Stichting Centrum voor Wiskunde en 20 * Informatica, nor the names of its contributors may be used to endorse or 21 * promote products derived from this software without specific prior 22 * written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /* 38 * Copyright (c) 1982 Jay Fenlason <hack@gnu.org> 39 * All rights reserved. 40 * 41 * Redistribution and use in source and binary forms, with or without 42 * modification, are permitted provided that the following conditions 43 * are met: 44 * 1. Redistributions of source code must retain the above copyright 45 * notice, this list of conditions and the following disclaimer. 46 * 2. Redistributions in binary form must reproduce the above copyright 47 * notice, this list of conditions and the following disclaimer in the 48 * documentation and/or other materials provided with the distribution. 49 * 3. The name of the author may not be used to endorse or promote products 50 * derived from this software without specific prior written permission. 51 * 52 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 53 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 54 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 55 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 56 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 57 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 58 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 59 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 60 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 61 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 62 */ 63 64 #include <sys/cdefs.h> 65 #ifndef lint 66 __RCSID("$NetBSD: hack.pager.c,v 1.11 2007/12/15 19:44:41 perry Exp $"); 67 #endif /* not lint */ 68 69 /* This file contains the command routine dowhatis() and a pager. */ 70 /* 71 * Also readmail() and doshell(), and generally the things that contact the 72 * outside world. 73 */ 74 75 #include <sys/types.h> 76 #include <signal.h> 77 #include <stdlib.h> 78 #include <unistd.h> 79 #include "hack.h" 80 #include "extern.h" 81 82 int 83 dowhatis() 84 { 85 FILE *fp; 86 char bufr[BUFSZ + 6]; 87 char *buf = &bufr[6], *ep, q; 88 89 if (!(fp = fopen(DATAFILE, "r"))) 90 pline("Cannot open data file!"); 91 else { 92 pline("Specify what? "); 93 q = readchar(); 94 if (q != '\t') 95 while (fgets(buf, BUFSZ, fp)) 96 if (*buf == q) { 97 ep = strchr(buf, '\n'); 98 if (ep) 99 *ep = 0; 100 /* else: bad data file */ 101 else { 102 pline("Bad data file!"); 103 (void) fclose(fp); 104 return(0); 105 } 106 /* Expand tab 'by hand' */ 107 if (buf[1] == '\t') { 108 buf = bufr; 109 buf[0] = q; 110 (void) strncpy(buf + 1, " ", 7); 111 } 112 pline(buf); 113 if (ep[-1] == ';') { 114 pline("More info? "); 115 if (readchar() == 'y') { 116 page_more(fp, 1); /* does fclose() */ 117 return (0); 118 } 119 } 120 (void) fclose(fp); /* kopper@psuvax1 */ 121 return (0); 122 } 123 pline("I've never heard of such things."); 124 (void) fclose(fp); 125 } 126 return (0); 127 } 128 129 /* make the paging of a file interruptible */ 130 static int got_intrup; 131 132 void 133 intruph(n) 134 int n __unused; 135 { 136 got_intrup++; 137 } 138 139 /* simple pager, also used from dohelp() */ 140 void 141 page_more(fp, strip) 142 FILE *fp; 143 int strip; /* nr of chars to be stripped from each line 144 * (0 or 1) */ 145 { 146 char *bufr, *ep; 147 sig_t prevsig = signal(SIGINT, intruph); 148 149 set_pager(0); 150 bufr = (char *) alloc((unsigned) CO); 151 bufr[CO - 1] = 0; 152 while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) { 153 ep = strchr(bufr, '\n'); 154 if (ep) 155 *ep = 0; 156 if (page_line(bufr + strip)) { 157 set_pager(2); 158 goto ret; 159 } 160 } 161 set_pager(1); 162 ret: 163 free(bufr); 164 (void) fclose(fp); 165 (void) signal(SIGINT, prevsig); 166 got_intrup = 0; 167 } 168 169 static boolean whole_screen = TRUE; 170 #define PAGMIN 12 /* minimum # of lines for page below level 171 * map */ 172 173 void 174 set_whole_screen() 175 { /* called in termcap as soon as LI is known */ 176 whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD); 177 } 178 179 #ifdef NEWS 180 int 181 readnews() 182 { 183 int ret; 184 185 whole_screen = TRUE; /* force a docrt(), our first */ 186 ret = page_file(NEWS, TRUE); 187 set_whole_screen(); 188 return (ret); /* report whether we did docrt() */ 189 } 190 #endif /* NEWS */ 191 192 void 193 set_pager(mode) 194 int mode; /* 0: open 1: wait+close 2: close */ 195 { 196 static boolean so; 197 if (mode == 0) { 198 if (!whole_screen) { 199 /* clear topline */ 200 clrlin(); 201 /* use part of screen below level map */ 202 curs(1, ROWNO + 4); 203 } else { 204 cls(); 205 } 206 so = flags.standout; 207 flags.standout = 1; 208 } else { 209 if (mode == 1) { 210 curs(1, LI); 211 more(); 212 } 213 flags.standout = so; 214 if (whole_screen) 215 docrt(); 216 else { 217 curs(1, ROWNO + 4); 218 cl_eos(); 219 } 220 } 221 } 222 223 int 224 page_line(s) /* returns 1 if we should quit */ 225 const char *s; 226 { 227 if (cury == LI - 1) { 228 if (!*s) 229 return (0); /* suppress blank lines at top */ 230 putchar('\n'); 231 cury++; 232 cmore("q\033"); 233 if (morc) { 234 morc = 0; 235 return (1); 236 } 237 if (whole_screen) 238 cls(); 239 else { 240 curs(1, ROWNO + 4); 241 cl_eos(); 242 } 243 } 244 puts(s); 245 cury++; 246 return (0); 247 } 248 249 /* 250 * Flexible pager: feed it with a number of lines and it will decide 251 * whether these should be fed to the pager above, or displayed in a 252 * corner. 253 * Call: 254 * cornline(0, title or 0) : initialize 255 * cornline(1, text) : add text to the chain of texts 256 * cornline(2, morcs) : output everything and cleanup 257 * cornline(3, 0) : cleanup 258 */ 259 260 void 261 cornline(mode, text) 262 int mode; 263 const char *text; 264 { 265 static struct line { 266 struct line *next_line; 267 char *line_text; 268 } *texthead, *texttail; 269 static int maxlen; 270 static int linect; 271 struct line *tl; 272 273 if (mode == 0) { 274 texthead = 0; 275 maxlen = 0; 276 linect = 0; 277 if (text) { 278 cornline(1, text); /* title */ 279 cornline(1, ""); /* blank line */ 280 } 281 return; 282 } 283 if (mode == 1) { 284 int len; 285 286 if (!text) 287 return; /* superfluous, just to be sure */ 288 linect++; 289 len = strlen(text); 290 if (len > maxlen) 291 maxlen = len; 292 tl = (struct line *) 293 alloc((unsigned) (len + sizeof(struct line) + 1)); 294 tl->next_line = 0; 295 tl->line_text = (char *) (tl + 1); 296 (void) strcpy(tl->line_text, text); 297 if (!texthead) 298 texthead = tl; 299 else 300 texttail->next_line = tl; 301 texttail = tl; 302 return; 303 } 304 /* --- now we really do it --- */ 305 if (mode == 2 && linect == 1) /* topline only */ 306 pline(texthead->line_text); 307 else if (mode == 2) { 308 int curline, lth; 309 310 if (flags.toplin == 1) 311 more(); /* ab@unido */ 312 remember_topl(); 313 314 lth = CO - maxlen - 2; /* Use full screen width */ 315 if (linect < LI && lth >= 10) { /* in a corner */ 316 home(); 317 cl_end(); 318 flags.toplin = 0; 319 curline = 1; 320 for (tl = texthead; tl; tl = tl->next_line) { 321 curs(lth, curline); 322 if (curline > 1) 323 cl_end(); 324 putsym(' '); 325 putstr(tl->line_text); 326 curline++; 327 } 328 curs(lth, curline); 329 cl_end(); 330 cmore(text); 331 home(); 332 cl_end(); 333 docorner(lth, curline - 1); 334 } else { /* feed to pager */ 335 set_pager(0); 336 for (tl = texthead; tl; tl = tl->next_line) { 337 if (page_line(tl->line_text)) { 338 set_pager(2); 339 goto cleanup; 340 } 341 } 342 if (text) { 343 cgetret(text); 344 set_pager(2); 345 } else 346 set_pager(1); 347 } 348 } 349 cleanup: 350 while ((tl = texthead) != NULL) { 351 texthead = tl->next_line; 352 free((char *) tl); 353 } 354 } 355 356 int 357 dohelp() 358 { 359 char c; 360 361 pline("Long or short help? "); 362 while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c)) 363 bell(); 364 if (!strchr(quitchars, c)) 365 (void) page_file((c == 'l') ? HELP : SHELP, FALSE); 366 return (0); 367 } 368 369 int 370 page_file(fnam, silent) /* return: 0 - cannot open fnam; 1 - 371 * otherwise */ 372 const char *fnam; 373 boolean silent; 374 { 375 #ifdef DEF_PAGER /* this implies that UNIX is defined */ 376 { 377 /* use external pager; this may give security problems */ 378 379 int fd = open(fnam, O_RDONLY); 380 381 if (fd < 0) { 382 if (!silent) 383 pline("Cannot open %s.", fnam); 384 return (0); 385 } 386 if (child(1)) { 387 388 /* 389 * Now that child() does a setuid(getuid()) and a 390 * chdir(), we may not be able to open file fnam 391 * anymore, so make it stdin. 392 */ 393 (void) close(0); 394 if (dup(fd)) { 395 if (!silent) 396 printf("Cannot open %s as stdin.\n", fnam); 397 } else { 398 execl(catmore, "page", (char *) 0); 399 if (!silent) 400 printf("Cannot exec %s.\n", catmore); 401 } 402 exit(1); 403 } 404 (void) close(fd); 405 } 406 #else /* DEF_PAGER */ 407 { 408 FILE *f; /* free after Robert Viduya */ 409 410 if ((f = fopen(fnam, "r")) == (FILE *) 0) { 411 if (!silent) { 412 home(); 413 perror(fnam); 414 flags.toplin = 1; 415 pline("Cannot open %s.", fnam); 416 } 417 return (0); 418 } 419 page_more(f, 0); 420 } 421 #endif /* DEF_PAGER */ 422 423 return (1); 424 } 425 426 #ifdef UNIX 427 #ifdef SHELL 428 int 429 dosh() 430 { 431 char *str; 432 if (child(0)) { 433 if ((str = getenv("SHELL")) != NULL) 434 execl(str, str, (char *) 0); 435 else 436 execl("/bin/sh", "sh", (char *) 0); 437 pline("sh: cannot execute."); 438 exit(1); 439 } 440 return (0); 441 } 442 #endif /* SHELL */ 443 444 #ifdef NOWAITINCLUDE 445 union wait { /* used only for the cast (union wait *) 0 */ 446 int w_status; 447 struct { 448 unsigned short w_Termsig:7; 449 unsigned short w_Coredump:1; 450 unsigned short w_Retcode:8; 451 } w_T; 452 }; 453 454 #else 455 456 #ifdef BSD 457 #include <sys/wait.h> 458 #else 459 #include <wait.h> 460 #endif /* BSD */ 461 #endif /* NOWAITINCLUDE */ 462 463 int 464 child(int wt) 465 { 466 int status; 467 int f; 468 469 f = fork(); 470 if (f == 0) { /* child */ 471 settty((char *) 0); /* also calls end_screen() */ 472 (void) setuid(getuid()); 473 (void) setgid(getgid()); 474 #ifdef CHDIR 475 (void) chdir(getenv("HOME")); 476 #endif /* CHDIR */ 477 return (1); 478 } 479 if (f == -1) { /* cannot fork */ 480 pline("Fork failed. Try again."); 481 return (0); 482 } 483 /* fork succeeded; wait for child to exit */ 484 (void) signal(SIGINT, SIG_IGN); 485 (void) signal(SIGQUIT, SIG_IGN); 486 (void) wait(&status); 487 gettty(); 488 setftty(); 489 (void) signal(SIGINT, done1); 490 #ifdef WIZARD 491 if (wizard) 492 (void) signal(SIGQUIT, SIG_DFL); 493 #endif /* WIZARD */ 494 if (wt) 495 getret(); 496 docrt(); 497 return (0); 498 } 499 #endif /* UNIX */ 500