1 /*
   2  * Copyright (c) 1991, 1993
   3  *      The Regents of the University of California.  All rights reserved.
   4  *
   5  * This code is derived from software contributed to Berkeley by
   6  * Edward Sze-Tyan Wang.
   7  *
   8  * Redistribution and use in source and binary forms, with or without
   9  * modification, are permitted provided that the following conditions
  10  * are met:
  11  * 1. Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  * 2. Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in the
  15  *    documentation and/or other materials provided with the distribution.
  16  * 4. Neither the name of the University nor the names of its contributors
  17  *    may be used to endorse or promote products derived from this software
  18  *    without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30  * SUCH DAMAGE.
  31  */
  32 
  33 /*
  34  * Solaris porting notes:  the original FreeBSD version made use of the
  35  *                         BSD kqueue event notification framework; this
  36  *                         was changed to use usleep()
  37  */
  38 
  39 #include <sys/param.h>
  40 #include <sys/mount.h>
  41 #include <sys/types.h>
  42 #include <sys/stat.h>
  43 #include <sys/statfs.h>
  44 #include <sys/statvfs.h>
  45 #include <sys/time.h>
  46 #include <sys/mman.h>
  47 #include <sys/poll.h>
  48 #include <port.h>
  49 #include <err.h>
  50 #include <errno.h>
  51 #include <fcntl.h>
  52 #include <limits.h>
  53 #include <stdio.h>
  54 #include <stdlib.h>
  55 #include <string.h>
  56 #include <unistd.h>
  57 
  58 #include "extern.h"
  59 
  60 static void rlines(FILE *, const char *fn, off_t, struct stat *);
  61 static int show(file_info_t *);
  62 static void set_events(file_info_t *files);
  63 
  64 /* defines for inner loop actions */
  65 #define USE_SLEEP       0
  66 #define ADD_EVENTS      2
  67 
  68 int action = USE_SLEEP;
  69 
  70 static const file_info_t *last;
  71 
  72 /*
  73  * forward -- display the file, from an offset, forward.
  74  *
  75  * There are eight separate cases for this -- regular and non-regular
  76  * files, by bytes or lines and from the beginning or end of the file.
  77  *
  78  * FBYTES       byte offset from the beginning of the file
  79  *      REG     seek
  80  *      NOREG   read, counting bytes
  81  *
  82  * FLINES       line offset from the beginning of the file
  83  *      REG     read, counting lines
  84  *      NOREG   read, counting lines
  85  *
  86  * RBYTES       byte offset from the end of the file
  87  *      REG     seek
  88  *      NOREG   cyclically read characters into a wrap-around buffer
  89  *
  90  * RLINES
  91  *      REG     mmap the file and step back until reach the correct offset.
  92  *      NOREG   cyclically read lines into a wrap-around array of buffers
  93  */
  94 void
  95 forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
  96 {
  97         int ch;
  98 
  99         switch (style) {
 100         case FBYTES:
 101                 if (off == 0)
 102                         break;
 103                 if (S_ISREG(sbp->st_mode)) {
 104                         if (sbp->st_size < off)
 105                                 off = sbp->st_size;
 106                         if (fseeko(fp, off, SEEK_SET) == -1) {
 107                                 ierr(fn);
 108                                 return;
 109                         }
 110                 } else while (off--)
 111                         if ((ch = getc(fp)) == EOF) {
 112                                 if (ferror(fp)) {
 113                                         ierr(fn);
 114                                         return;
 115                                 }
 116                                 break;
 117                         }
 118                 break;
 119         case FLINES:
 120                 if (off == 0)
 121                         break;
 122                 for (;;) {
 123                         if ((ch = getc(fp)) == EOF) {
 124                                 if (ferror(fp)) {
 125                                         ierr(fn);
 126                                         return;
 127                                 }
 128                                 break;
 129                         }
 130                         if (ch == '\n' && !--off)
 131                                 break;
 132                 }
 133                 break;
 134         case RBYTES:
 135                 if (S_ISREG(sbp->st_mode)) {
 136                         if (sbp->st_size >= off &&
 137                             fseeko(fp, -off, SEEK_END) == -1) {
 138                                 ierr(fn);
 139                                 return;
 140                         }
 141                 } else if (off == 0) {
 142                         while (getc(fp) != EOF)
 143                                 ;
 144                         if (ferror(fp)) {
 145                                 ierr(fn);
 146                                 return;
 147                         }
 148                 } else
 149                         if (bytes(fp, fn, off))
 150                                 return;
 151                 break;
 152         case RLINES:
 153                 if (S_ISREG(sbp->st_mode))
 154                         if (!off) {
 155                                 if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
 156                                         ierr(fn);
 157                                         return;
 158                                 }
 159                         } else
 160                                 rlines(fp, fn, off, sbp);
 161                 else if (off == 0) {
 162                         while (getc(fp) != EOF)
 163                                 ;
 164                         if (ferror(fp)) {
 165                                 ierr(fn);
 166                                 return;
 167                         }
 168                 } else
 169                         if (lines(fp, fn, off))
 170                                 return;
 171                 break;
 172         default:
 173                 break;
 174         }
 175 
 176         while ((ch = getc(fp)) != EOF)
 177                 if (putchar(ch) == EOF)
 178                         oerr();
 179         if (ferror(fp)) {
 180                 ierr(fn);
 181                 return;
 182         }
 183         (void) fflush(stdout);
 184 }
 185 
 186 /*
 187  * rlines -- display the last offset lines of the file.
 188  */
 189 static void
 190 rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
 191 {
 192         struct mapinfo map;
 193         off_t curoff, size;
 194         int i;
 195 
 196         if ((size = sbp->st_size) == 0)
 197                 return;
 198         map.start = NULL;
 199         map.fd = fileno(fp);
 200         map.mapoff = map.maxoff = size;
 201 
 202         /*
 203          * Last char is special, ignore whether newline or not. Note that
 204          * size == 0 is dealt with above, and size == 1 sets curoff to -1.
 205          */
 206         curoff = size - 2;
 207         while (curoff >= 0) {
 208                 if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
 209                         ierr(fn);
 210                         return;
 211                 }
 212                 for (i = curoff - map.mapoff; i >= 0; i--)
 213                         if (map.start[i] == '\n' && --off == 0)
 214                                 break;
 215                 /* `i' is either the map offset of a '\n', or -1. */
 216                 curoff = map.mapoff + i;
 217                 if (i >= 0)
 218                         break;
 219         }
 220         curoff++;
 221         if (mapprint(&map, curoff, size - curoff) != 0) {
 222                 ierr(fn);
 223                 exit(1);
 224         }
 225 
 226         /* Set the file pointer to reflect the length displayed. */
 227         if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
 228                 ierr(fn);
 229                 return;
 230         }
 231         if (map.start != NULL && munmap(map.start, map.maplen)) {
 232                 ierr(fn);
 233                 return;
 234         }
 235 }
 236 
 237 static int
 238 show(file_info_t *file)
 239 {
 240         int ch;
 241 
 242         while ((ch = getc(file->fp)) != EOF) {
 243                 if (last != file && no_files > 1) {
 244                         if (!qflag)
 245                                 (void) printf("\n==> %s <==\n",
 246                                     file->file_name);
 247                         last = file;
 248                 }
 249                 if (putchar(ch) == EOF)
 250                         oerr();
 251         }
 252         (void) fflush(stdout);
 253         if (ferror(file->fp)) {
 254                 (void) fclose(file->fp);
 255                 file->fp = NULL;
 256                 ierr(file->file_name);
 257                 return (0);
 258         }
 259         clearerr(file->fp);
 260         return (1);
 261 }
 262 
 263 static void
 264 set_events(file_info_t *files)
 265 {
 266         int i;
 267         file_info_t *file;
 268 
 269         for (i = 0, file = files; i < no_files; i++, file++) {
 270                 if (! file->fp)
 271                         continue;
 272 
 273                 (void) fstat(fileno(file->fp), &file->st);
 274         }
 275 }
 276 
 277 /*
 278  * follow -- display the file, from an offset, forward.
 279  *
 280  */
 281 void
 282 follow(file_info_t *files, enum STYLE style, off_t off)
 283 {
 284         int active, ev_change, i, n = -1;
 285         struct stat sb2;
 286         file_info_t *file;
 287 
 288         /* Position each of the files */
 289 
 290         file = files;
 291         active = 0;
 292         n = 0;
 293         for (i = 0; i < no_files; i++, file++) {
 294                 if (file->fp) {
 295                         active = 1;
 296                         n++;
 297                         if (no_files > 1 && !qflag)
 298                                 (void) printf("\n==> %s <==\n",
 299                                     file->file_name);
 300                         forward(file->fp, file->file_name, style, off,
 301                             &file->st);
 302                         if (Fflag && fileno(file->fp) != STDIN_FILENO)
 303                                 n++;
 304                 }
 305         }
 306         if (!Fflag && !active)
 307                 return;
 308 
 309         last = --file;
 310         set_events(files);
 311 
 312         for (;;) {
 313                 ev_change = 0;
 314                 if (Fflag) {
 315                         for (i = 0, file = files; i < no_files; i++, file++) {
 316                                 if (!file->fp) {
 317                                         file->fp = fopen(file->file_name, "r");
 318                                         if (file->fp != NULL &&
 319                                             fstat(fileno(file->fp), &file->st)
 320                                             == -1) {
 321                                                 (void) fclose(file->fp);
 322                                                 file->fp = NULL;
 323                                         }
 324                                         if (file->fp != NULL)
 325                                                 ev_change++;
 326                                         continue;
 327                                 }
 328                                 if (fileno(file->fp) == STDIN_FILENO)
 329                                         continue;
 330                                 if (stat(file->file_name, &sb2) == -1) {
 331                                         if (errno != ENOENT)
 332                                                 ierr(file->file_name);
 333                                         (void) show(file);
 334                                         (void) fclose(file->fp);
 335                                         file->fp = NULL;
 336                                         ev_change++;
 337                                         continue;
 338                                 }
 339 
 340                                 if (sb2.st_ino != file->st.st_ino ||
 341                                     sb2.st_dev != file->st.st_dev ||
 342                                     sb2.st_nlink == 0) {
 343                                         (void) show(file);
 344                                         file->fp = freopen(file->file_name, "r",
 345                                             file->fp);
 346                                         if (file->fp != NULL)
 347                                                 (void) memcpy(&file->st, &sb2,
 348                                                     sizeof (struct stat));
 349                                         else if (errno != ENOENT)
 350                                                 ierr(file->file_name);
 351                                         ev_change++;
 352                                 }
 353                         }
 354                 }
 355 
 356                 for (i = 0, file = files; i < no_files; i++, file++)
 357                         if (file->fp && !show(file))
 358                                 ev_change++;
 359 
 360                 if (ev_change)
 361                         set_events(files);
 362 
 363                 switch (action) {
 364                 case USE_SLEEP:
 365                         (void) usleep(250000);
 366                         break;
 367                 }
 368         }
 369 }