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