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