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