/* $NetBSD: hack.pager.c,v 1.21 2011/09/01 07:18:50 plunky Exp $ */ /* * Copyright (c) 1985, Stichting Centrum voor Wiskunde en Informatica, * Amsterdam * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the Stichting Centrum voor Wiskunde en * Informatica, nor the names of its contributors may be used to endorse or * promote products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Copyright (c) 1982 Jay Fenlason * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifndef lint __RCSID("$NetBSD: hack.pager.c,v 1.21 2011/09/01 07:18:50 plunky Exp $"); #endif /* not lint */ /* This file contains the command routine dowhatis() and a pager. */ /* * Also readmail() and doshell(), and generally the things that contact the * outside world. */ #include #include #include #include #include #include "hack.h" #include "extern.h" static void intruph(int); static void page_more(FILE *, int); static int page_file(const char *, boolean); static int child(int); int dowhatis(void) { FILE *fp; char bufr[BUFSZ + 6]; char *buf = &bufr[6], *ep, q; if (!(fp = fopen(DATAFILE, "r"))) pline("Cannot open data file!"); else { pline("Specify what? "); q = readchar(); if (q != '\t') while (fgets(buf, BUFSZ, fp)) if (*buf == q) { ep = strchr(buf, '\n'); if (ep) *ep = 0; /* else: bad data file */ else { pline("Bad data file!"); (void) fclose(fp); return(0); } /* Expand tab 'by hand' */ if (buf[1] == '\t') { buf = bufr; buf[0] = q; (void) strncpy(buf + 1, " ", 7); } pline("%s", buf); if (ep[-1] == ';') { pline("More info? "); if (readchar() == 'y') { page_more(fp, 1); /* does fclose() */ return (0); } } (void) fclose(fp); /* kopper@psuvax1 */ return (0); } pline("I've never heard of such things."); (void) fclose(fp); } return (0); } /* make the paging of a file interruptible */ static int got_intrup; static void intruph(int n __unused) { got_intrup++; } /* simple pager, also used from dohelp() */ /* strip: nr of chars to be stripped from each line (0 or 1) */ static void page_more(FILE *fp, int strip) { char *bufr, *ep; sig_t prevsig = signal(SIGINT, intruph); set_pager(0); bufr = alloc(CO); bufr[CO - 1] = 0; while (fgets(bufr, CO - 1, fp) && (!strip || *bufr == '\t') && !got_intrup) { ep = strchr(bufr, '\n'); if (ep) *ep = 0; if (page_line(bufr + strip)) { set_pager(2); goto ret; } } set_pager(1); ret: free(bufr); (void) fclose(fp); (void) signal(SIGINT, prevsig); got_intrup = 0; } static boolean whole_screen = TRUE; #define PAGMIN 12 /* minimum # of lines for page below level * map */ void set_whole_screen(void) { /* called in termcap as soon as LI is known */ whole_screen = (LI - ROWNO - 2 <= PAGMIN || !CD); } #ifdef NEWS int readnews(void) { int ret; whole_screen = TRUE; /* force a docrt(), our first */ ret = page_file(NEWS, TRUE); set_whole_screen(); return (ret); /* report whether we did docrt() */ } #endif /* NEWS */ /* mode: 0: open 1: wait+close 2: close */ void set_pager(int mode) { static boolean so; if (mode == 0) { if (!whole_screen) { /* clear topline */ clrlin(); /* use part of screen below level map */ curs(1, ROWNO + 4); } else { cls(); } so = flags.standout; flags.standout = 1; } else { if (mode == 1) { curs(1, LI); more(); } flags.standout = so; if (whole_screen) docrt(); else { curs(1, ROWNO + 4); cl_eos(); } } } int page_line(const char *s) /* returns 1 if we should quit */ { if (cury == LI - 1) { if (!*s) return (0); /* suppress blank lines at top */ putchar('\n'); cury++; cmore("q\033"); if (morc) { morc = 0; return (1); } if (whole_screen) cls(); else { curs(1, ROWNO + 4); cl_eos(); } } puts(s); cury++; return (0); } /* * Flexible pager: feed it with a number of lines and it will decide * whether these should be fed to the pager above, or displayed in a * corner. * Call: * cornline(0, title or 0) : initialize * cornline(1, text) : add text to the chain of texts * cornline(2, morcs) : output everything and cleanup * cornline(3, 0) : cleanup */ void cornline(int mode, const char *text) { static struct line { struct line *next_line; char *line_text; } *texthead, *texttail; static int maxlen; static int linect; struct line *tl; if (mode == 0) { texthead = 0; maxlen = 0; linect = 0; if (text) { cornline(1, text); /* title */ cornline(1, ""); /* blank line */ } return; } if (mode == 1) { int len; if (!text) return; /* superfluous, just to be sure */ linect++; len = strlen(text); if (len > maxlen) maxlen = len; tl = alloc(len + sizeof(*tl) + 1); tl->next_line = 0; tl->line_text = (char *) (tl + 1); (void) strcpy(tl->line_text, text); if (!texthead) texthead = tl; else texttail->next_line = tl; texttail = tl; return; } /* --- now we really do it --- */ if (mode == 2 && linect == 1) /* topline only */ pline("%s", texthead->line_text); else if (mode == 2) { int curline, lth; if (flags.toplin == 1) more(); /* ab@unido */ remember_topl(); lth = CO - maxlen - 2; /* Use full screen width */ if (linect < LI && lth >= 10) { /* in a corner */ home(); cl_end(); flags.toplin = 0; curline = 1; for (tl = texthead; tl; tl = tl->next_line) { curs(lth, curline); if (curline > 1) cl_end(); putsym(' '); putstr(tl->line_text); curline++; } curs(lth, curline); cl_end(); cmore(text); home(); cl_end(); docorner(lth, curline - 1); } else { /* feed to pager */ set_pager(0); for (tl = texthead; tl; tl = tl->next_line) { if (page_line(tl->line_text)) { set_pager(2); goto cleanup; } } if (text) { cgetret(text); set_pager(2); } else set_pager(1); } } cleanup: while ((tl = texthead) != NULL) { texthead = tl->next_line; free(tl); } } int dohelp(void) { char c; pline("Long or short help? "); while (((c = readchar()) != 'l') && (c != 's') && !strchr(quitchars, c)) sound_bell(); if (!strchr(quitchars, c)) (void) page_file((c == 'l') ? HELP : SHELP, FALSE); return (0); } /* return: 0 - cannot open fnam; 1 - otherwise */ static int page_file(const char *fnam, boolean silent) { #ifdef DEF_PAGER /* this implies that UNIX is defined */ { /* use external pager; this may give security problems */ int fd = open(fnam, O_RDONLY); if (fd < 0) { if (!silent) pline("Cannot open %s.", fnam); return (0); } if (child(1)) { /* * Now that child() does a setuid(getuid()) and a * chdir(), we may not be able to open file fnam * anymore, so make it stdin. */ (void) close(0); if (dup(fd)) { if (!silent) printf("Cannot open %s as stdin.\n", fnam); } else { execl(catmore, "page", (char *)NULL); if (!silent) printf("Cannot exec %s.\n", catmore); } exit(1); } (void) close(fd); } #else /* DEF_PAGER */ { FILE *f; /* free after Robert Viduya */ if ((f = fopen(fnam, "r")) == (FILE *) 0) { if (!silent) { home(); perror(fnam); flags.toplin = 1; pline("Cannot open %s.", fnam); } return (0); } page_more(f, 0); } #endif /* DEF_PAGER */ return (1); } #ifdef UNIX #ifdef SHELL int dosh(void) { char *str; if (child(0)) { if ((str = getenv("SHELL")) != NULL) execl(str, str, (char *)NULL); else execl("/bin/sh", "sh", (char *)NULL); pline("sh: cannot execute."); exit(1); } return (0); } #endif /* SHELL */ static int child(int wt) { int status; int f; f = fork(); if (f == 0) { /* child */ settty(NULL); /* also calls end_screen() */ (void) setuid(getuid()); (void) setgid(getgid()); #ifdef CHDIR (void) chdir(getenv("HOME")); #endif /* CHDIR */ return (1); } if (f == -1) { /* cannot fork */ pline("Fork failed. Try again."); return (0); } /* fork succeeded; wait for child to exit */ (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); (void) wait(&status); gettty(); setftty(); (void) signal(SIGINT, done1); #ifdef WIZARD if (wizard) (void) signal(SIGQUIT, SIG_DFL); #endif /* WIZARD */ if (wt) getret(); docrt(); return (0); } #endif /* UNIX */