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