1/* $NetBSD: hack.pager.c,v 1.20 2011/08/31 16:24:56 plunky 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.20 2011/08/31 16:24:56 plunky 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 <sys/wait.h> 77#include <signal.h> 78#include <stdlib.h> 79#include <unistd.h> 80#include "hack.h" 81#include "extern.h" 82 83static void intruph(int); 84static void page_more(FILE *, int); 85static int page_file(const char *, boolean); 86static int child(int); 87 88int 89dowhatis(void) 90{ 91 FILE *fp; 92 char bufr[BUFSZ + 6]; 93 char *buf = &bufr[6], *ep, q; 94 95 if (!(fp = fopen(DATAFILE, "r"))) 96 pline("Cannot open data file!"); 97 else { 98 pline("Specify what? "); 99 q = readchar(); 100 if (q != '\t') 101 while (fgets(buf, BUFSZ, fp)) 102 if (*buf == q) { 103 ep = strchr(buf, '\n'); 104 if (ep) 105 *ep = 0; 106 /* else: bad data file */ 107 else { 108 pline("Bad data file!"); 109 (void) fclose(fp); 110 return(0); 111 } 112 /* Expand tab 'by hand' */ 113 if (buf[1] == '\t') { 114 buf = bufr; 115 buf[0] = q; 116 (void) strncpy(buf + 1, " ", 7); 117 } 118 pline("%s", buf); 119 if (ep[-1] == ';') { 120 pline("More info? "); 121 if (readchar() == 'y') { 122 page_more(fp, 1); /* does fclose() */ 123 return (0); 124 } 125 } 126 (void) fclose(fp); /* kopper@psuvax1 */ 127 return (0); 128 } 129 pline("I've never heard of such things."); 130 (void) fclose(fp); 131 } 132 return (0); 133} 134 135/* make the paging of a file interruptible */ 136static int got_intrup; 137 138static void 139intruph(int n __unused) 140{ 141 got_intrup++; 142} 143 144/* simple pager, also used from dohelp() */ 145/* strip: nr of chars to be stripped from each line (0 or 1) */ 146static void 147page_more(FILE *fp, int strip) 148{ 149 char *bufr, *ep; 150 sig_t prevsig = signal(SIGINT, intruph); 151 152 set_pager(0); 153 bufr = alloc(CO); 154 bufr[CO - 1] = 0; 155 while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) { 156 ep = strchr(bufr, '\n'); 157 if (ep) 158 *ep = 0; 159 if (page_line(bufr + strip)) { 160 set_pager(2); 161 goto ret; 162 } 163 } 164 set_pager(1); 165ret: 166 free(bufr); 167 (void) fclose(fp); 168 (void) signal(SIGINT, prevsig); 169 got_intrup = 0; 170} 171 172static boolean whole_screen = TRUE; 173#define PAGMIN 12 /* minimum # of lines for page below level 174 * map */ 175 176void 177set_whole_screen(void) 178{ /* called in termcap as soon as LI is known */ 179 whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD); 180} 181 182#ifdef NEWS 183int 184readnews(void) 185{ 186 int ret; 187 188 whole_screen = TRUE; /* force a docrt(), our first */ 189 ret = page_file(NEWS, TRUE); 190 set_whole_screen(); 191 return (ret); /* report whether we did docrt() */ 192} 193#endif /* NEWS */ 194 195/* mode: 0: open 1: wait+close 2: close */ 196void 197set_pager(int mode) 198{ 199 static boolean so; 200 if (mode == 0) { 201 if (!whole_screen) { 202 /* clear topline */ 203 clrlin(); 204 /* use part of screen below level map */ 205 curs(1, ROWNO + 4); 206 } else { 207 cls(); 208 } 209 so = flags.standout; 210 flags.standout = 1; 211 } else { 212 if (mode == 1) { 213 curs(1, LI); 214 more(); 215 } 216 flags.standout = so; 217 if (whole_screen) 218 docrt(); 219 else { 220 curs(1, ROWNO + 4); 221 cl_eos(); 222 } 223 } 224} 225 226int 227page_line(const char *s) /* returns 1 if we should quit */ 228{ 229 if (cury == LI - 1) { 230 if (!*s) 231 return (0); /* suppress blank lines at top */ 232 putchar('\n'); 233 cury++; 234 cmore("q\033"); 235 if (morc) { 236 morc = 0; 237 return (1); 238 } 239 if (whole_screen) 240 cls(); 241 else { 242 curs(1, ROWNO + 4); 243 cl_eos(); 244 } 245 } 246 puts(s); 247 cury++; 248 return (0); 249} 250 251/* 252 * Flexible pager: feed it with a number of lines and it will decide 253 * whether these should be fed to the pager above, or displayed in a 254 * corner. 255 * Call: 256 * cornline(0, title or 0) : initialize 257 * cornline(1, text) : add text to the chain of texts 258 * cornline(2, morcs) : output everything and cleanup 259 * cornline(3, 0) : cleanup 260 */ 261 262void 263cornline(int mode, 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 = alloc(len + sizeof(*tl) + 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("%s", 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 } 348cleanup: 349 while ((tl = texthead) != NULL) { 350 texthead = tl->next_line; 351 free(tl); 352 } 353} 354 355int 356dohelp(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 */ 369static int 370page_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 *)NULL); 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 425int 426dosh(void) 427{ 428 char *str; 429 if (child(0)) { 430 if ((str = getenv("SHELL")) != NULL) 431 execl(str, str, (char *)NULL); 432 else 433 execl("/bin/sh", "sh", (char *)NULL); 434 pline("sh: cannot execute."); 435 exit(1); 436 } 437 return (0); 438} 439#endif /* SHELL */ 440 441static int 442child(int wt) 443{ 444 int status; 445 int f; 446 447 f = fork(); 448 if (f == 0) { /* child */ 449 settty(NULL); /* also calls end_screen() */ 450 (void) setuid(getuid()); 451 (void) setgid(getgid()); 452#ifdef CHDIR 453 (void) chdir(getenv("HOME")); 454#endif /* CHDIR */ 455 return (1); 456 } 457 if (f == -1) { /* cannot fork */ 458 pline("Fork failed. Try again."); 459 return (0); 460 } 461 /* fork succeeded; wait for child to exit */ 462 (void) signal(SIGINT, SIG_IGN); 463 (void) signal(SIGQUIT, SIG_IGN); 464 (void) wait(&status); 465 gettty(); 466 setftty(); 467 (void) signal(SIGINT, done1); 468#ifdef WIZARD 469 if (wizard) 470 (void) signal(SIGQUIT, SIG_DFL); 471#endif /* WIZARD */ 472 if (wt) 473 getret(); 474 docrt(); 475 return (0); 476} 477#endif /* UNIX */ 478