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