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