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