1 /*
   2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions
   7  * are met:
   8  * 1. Redistributions of source code must retain the above copyright
   9  *    notice, this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright
  11  *    notice, this list of conditions and the following disclaimer in the
  12  *    documentation and/or other materials provided with the distribution.
  13  *
  14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24  * SUCH DAMAGE.
  25  */
  26 /*
  27  * Simple paged-output and paged-viewing functions
  28  */
  29 
  30 #include <stdio.h>
  31 #include <stdlib.h>
  32 #include <string.h>
  33 #include <sys/types.h>
  34 #include <sys/stat.h>
  35 #include <fcntl.h>
  36 #include <errno.h>
  37 #include <termios.h>
  38 #include <unistd.h>
  39 #include <stropts.h>
  40 
  41 static int p_maxlines = -1;
  42 static int p_freelines;
  43 
  44 static struct termios orig_termios;
  45 static char *pager_prompt1 = \
  46         " --more--  <space> page down <enter> line down <q> quit ";
  47 static char *pager_blank = \
  48         "                                                        ";
  49 
  50 /*
  51  * 'open' the pager
  52  */
  53 void
  54 pager_open(void)
  55 {
  56         int     nlines;
  57         char *cp, *lp;
  58         struct termios raw;
  59         struct winsize ws;
  60 
  61         tcgetattr(0, &orig_termios);
  62         raw = orig_termios;
  63         raw.c_lflag &= ~(ICANON | ECHO);
  64         raw.c_cc[VMIN] = 1;
  65         raw.c_cc[VTIME] = 0;
  66         (void) tcsetattr(0, TCSAFLUSH, &raw);
  67 
  68         nlines = 24;            /* sensible default */
  69         if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_row == 0) {
  70                 if ((cp = getenv("LINES")) != NULL) {
  71                         nlines = strtol(cp, &lp, 0);
  72                 }
  73         } else
  74                 nlines = ws.ws_row;
  75 
  76         p_maxlines = nlines - 1;
  77         if (p_maxlines < 1)
  78                 p_maxlines = 1;
  79         p_freelines = p_maxlines;
  80 }
  81 
  82 /*
  83  * 'close' the pager
  84  */
  85 void
  86 pager_close(void)
  87 {
  88         (void) fflush(stdout);
  89         p_maxlines = -1;
  90         (void) tcsetattr(0, TCSAFLUSH, &orig_termios);
  91 }
  92 
  93 /*
  94  * Emit lines to the pager; may not return until the user
  95  * has responded to the prompt.
  96  *
  97  * Will return nonzero if the user enters 'q' or 'Q' at the prompt.
  98  *
  99  * XXX note that this watches outgoing newlines (and eats them), but
 100  *     does not handle wrap detection (req. count of columns).
 101  */
 102 int
 103 pager_output(const char *cp)
 104 {
 105         int action;
 106 
 107         if (cp == NULL)
 108                 return (0);
 109 
 110         for (;;) {
 111                 if (*cp == 0)
 112                         return (0);
 113 
 114                 putchar(*cp);           /* always emit character */
 115 
 116                 if (*(cp++) == '\n') {  /* got a newline? */
 117                         p_freelines--;
 118                         if (p_freelines <= 0) {
 119                                 printf("%s", pager_prompt1);
 120                                 action = 0;
 121                                 while (action == 0) {
 122                                         switch (getchar()) {
 123                                         case '\r':
 124                                         case '\n':
 125                                                 p_freelines = 1;
 126                                                 action = 1;
 127                                         break;
 128                                         case ' ':
 129                                                 p_freelines = p_maxlines;
 130                                                 action = 1;
 131                                         break;
 132                                         case 'q':
 133                                         case 'Q':
 134                                                 action = 2;
 135                                         break;
 136                                         default:
 137                                         break;
 138                                         }
 139                                 }
 140                                 printf("\r%s\r", pager_blank);
 141                                 if (action == 2)
 142                                         return (1);
 143                         }
 144                 }
 145         }
 146 }
 147 
 148 /*
 149  * Display from (fd).
 150  */
 151 int
 152 pager_file(const char *fname)
 153 {
 154         char buf[80];
 155         size_t hmuch;
 156         int fd;
 157         int result;
 158 
 159         if ((fd = open(fname, O_RDONLY)) == -1) {
 160                 printf("can't open '%s': %s\n", fname, strerror(errno));
 161                 return (-1);
 162         }
 163 
 164         for (;;) {
 165                 hmuch = read(fd, buf, sizeof (buf) - 1);
 166                 if (hmuch == -1) {
 167                         result = -1;
 168                         break;
 169                 }
 170                 if (hmuch == 0) {
 171                         result = 0;
 172                         break;
 173                 }
 174                 buf[hmuch] = 0;
 175                 if (pager_output(buf)) {
 176                         result = 1;
 177                         break;
 178                 }
 179         }
 180         close(fd);
 181         return (result);
 182 }