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