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