Home | History | Annotate | Line # | Download | only in tail
forward.c revision 1.31.8.1
      1  1.31.8.1       tls /*	$NetBSD: forward.c,v 1.31.8.1 2014/08/20 00:05:04 tls Exp $	*/
      2       1.6       jtc 
      3       1.1     glass /*-
      4       1.6       jtc  * Copyright (c) 1991, 1993
      5       1.6       jtc  *	The Regents of the University of California.  All rights reserved.
      6       1.1     glass  *
      7       1.1     glass  * This code is derived from software contributed to Berkeley by
      8       1.1     glass  * Edward Sze-Tyan Wang.
      9       1.1     glass  *
     10       1.1     glass  * Redistribution and use in source and binary forms, with or without
     11       1.1     glass  * modification, are permitted provided that the following conditions
     12       1.1     glass  * are met:
     13       1.1     glass  * 1. Redistributions of source code must retain the above copyright
     14       1.1     glass  *    notice, this list of conditions and the following disclaimer.
     15       1.1     glass  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.1     glass  *    notice, this list of conditions and the following disclaimer in the
     17       1.1     glass  *    documentation and/or other materials provided with the distribution.
     18      1.25       agc  * 3. Neither the name of the University nor the names of its contributors
     19       1.1     glass  *    may be used to endorse or promote products derived from this software
     20       1.1     glass  *    without specific prior written permission.
     21       1.1     glass  *
     22       1.1     glass  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23       1.1     glass  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24       1.1     glass  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25       1.1     glass  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26       1.1     glass  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27       1.1     glass  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28       1.1     glass  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29       1.1     glass  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30       1.1     glass  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31       1.1     glass  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32       1.1     glass  * SUCH DAMAGE.
     33       1.1     glass  */
     34       1.1     glass 
     35       1.8     lukem #include <sys/cdefs.h>
     36       1.1     glass #ifndef lint
     37       1.6       jtc #if 0
     38       1.6       jtc static char sccsid[] = "@(#)forward.c	8.1 (Berkeley) 6/6/93";
     39       1.6       jtc #endif
     40  1.31.8.1       tls __RCSID("$NetBSD: forward.c,v 1.31.8.1 2014/08/20 00:05:04 tls Exp $");
     41       1.1     glass #endif /* not lint */
     42       1.1     glass 
     43       1.1     glass #include <sys/types.h>
     44       1.1     glass #include <sys/stat.h>
     45       1.1     glass #include <sys/time.h>
     46       1.1     glass #include <sys/mman.h>
     47      1.23  jdolecek #include <sys/event.h>
     48       1.6       jtc 
     49       1.6       jtc #include <limits.h>
     50       1.1     glass #include <fcntl.h>
     51       1.1     glass #include <errno.h>
     52       1.1     glass #include <unistd.h>
     53       1.1     glass #include <stdio.h>
     54       1.1     glass #include <stdlib.h>
     55       1.1     glass #include <string.h>
     56       1.1     glass #include "extern.h"
     57       1.1     glass 
     58      1.26    itojun static int rlines(FILE *, off_t, struct stat *);
     59       1.1     glass 
     60      1.23  jdolecek /* defines for inner loop actions */
     61      1.23  jdolecek #define	USE_SLEEP	0
     62      1.23  jdolecek #define	USE_KQUEUE	1
     63      1.23  jdolecek #define	ADD_EVENTS	2
     64      1.23  jdolecek 
     65       1.1     glass /*
     66       1.1     glass  * forward -- display the file, from an offset, forward.
     67       1.1     glass  *
     68       1.1     glass  * There are eight separate cases for this -- regular and non-regular
     69       1.1     glass  * files, by bytes or lines and from the beginning or end of the file.
     70       1.1     glass  *
     71       1.1     glass  * FBYTES	byte offset from the beginning of the file
     72       1.1     glass  *	REG	seek
     73       1.1     glass  *	NOREG	read, counting bytes
     74       1.1     glass  *
     75       1.1     glass  * FLINES	line offset from the beginning of the file
     76       1.1     glass  *	REG	read, counting lines
     77       1.1     glass  *	NOREG	read, counting lines
     78       1.1     glass  *
     79       1.1     glass  * RBYTES	byte offset from the end of the file
     80       1.1     glass  *	REG	seek
     81       1.1     glass  *	NOREG	cyclically read characters into a wrap-around buffer
     82       1.1     glass  *
     83       1.1     glass  * RLINES
     84       1.1     glass  *	REG	mmap the file and step back until reach the correct offset.
     85       1.1     glass  *	NOREG	cyclically read lines into a wrap-around array of buffers
     86       1.1     glass  */
     87       1.1     glass void
     88      1.26    itojun forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
     89       1.1     glass {
     90      1.23  jdolecek 	int ch, n;
     91      1.23  jdolecek 	int kq=-1, action=USE_SLEEP;
     92       1.9       cjs 	struct stat statbuf;
     93      1.23  jdolecek 	struct kevent ev[2];
     94       1.9       cjs 
     95       1.1     glass 	switch(style) {
     96       1.1     glass 	case FBYTES:
     97       1.1     glass 		if (off == 0)
     98       1.1     glass 			break;
     99       1.1     glass 		if (S_ISREG(sbp->st_mode)) {
    100       1.1     glass 			if (sbp->st_size < off)
    101       1.1     glass 				off = sbp->st_size;
    102      1.26    itojun 			if (fseeko(fp, off, SEEK_SET) == -1) {
    103       1.1     glass 				ierr();
    104       1.6       jtc 				return;
    105       1.6       jtc 			}
    106       1.1     glass 		} else while (off--)
    107       1.1     glass 			if ((ch = getc(fp)) == EOF) {
    108       1.6       jtc 				if (ferror(fp)) {
    109       1.1     glass 					ierr();
    110       1.6       jtc 					return;
    111       1.1     glass 				}
    112       1.6       jtc 				break;
    113       1.6       jtc 			}
    114       1.1     glass 		break;
    115       1.1     glass 	case FLINES:
    116       1.1     glass 		if (off == 0)
    117       1.1     glass 			break;
    118       1.1     glass 		for (;;) {
    119       1.1     glass 			if ((ch = getc(fp)) == EOF) {
    120       1.6       jtc 				if (ferror(fp)) {
    121       1.1     glass 					ierr();
    122       1.6       jtc 					return;
    123       1.6       jtc 				}
    124       1.1     glass 				break;
    125       1.1     glass 			}
    126       1.1     glass 			if (ch == '\n' && !--off)
    127       1.1     glass 				break;
    128       1.1     glass 		}
    129       1.1     glass 		break;
    130       1.1     glass 	case RBYTES:
    131       1.1     glass 		if (S_ISREG(sbp->st_mode)) {
    132       1.1     glass 			if (sbp->st_size >= off &&
    133      1.26    itojun 			    fseeko(fp, -off, SEEK_END) == -1) {
    134       1.1     glass 				ierr();
    135       1.6       jtc 				return;
    136       1.6       jtc 			}
    137       1.1     glass 		} else if (off == 0) {
    138       1.1     glass 			while (getc(fp) != EOF);
    139       1.6       jtc 			if (ferror(fp)) {
    140       1.1     glass 				ierr();
    141       1.6       jtc 				return;
    142       1.6       jtc 			}
    143      1.16       cgd 		} else {
    144      1.29     lukem 			if (displaybytes(fp, off))
    145      1.16       cgd 				return;
    146      1.16       cgd 		}
    147       1.1     glass 		break;
    148       1.1     glass 	case RLINES:
    149      1.14  christos 		if (S_ISREG(sbp->st_mode)) {
    150       1.1     glass 			if (!off) {
    151       1.6       jtc 				if (fseek(fp, 0L, SEEK_END) == -1) {
    152       1.1     glass 					ierr();
    153       1.6       jtc 					return;
    154       1.6       jtc 				}
    155      1.16       cgd 			} else {
    156      1.16       cgd 				if (rlines(fp, off, sbp))
    157      1.16       cgd 					return;
    158      1.16       cgd 			}
    159      1.15  christos 		} else if (off == 0) {
    160       1.1     glass 			while (getc(fp) != EOF);
    161       1.6       jtc 			if (ferror(fp)) {
    162       1.1     glass 				ierr();
    163       1.6       jtc 				return;
    164       1.6       jtc 			}
    165      1.16       cgd 		} else {
    166      1.29     lukem 			if (displaylines(fp, off))
    167      1.16       cgd 				return;
    168      1.16       cgd 		}
    169       1.1     glass 		break;
    170       1.8     lukem 	default:
    171       1.8     lukem 		break;
    172       1.1     glass 	}
    173       1.1     glass 
    174      1.23  jdolecek 	if (fflag) {
    175      1.23  jdolecek 		kq = kqueue();
    176      1.23  jdolecek 		if (kq < 0)
    177      1.30  christos 			xerr(1, "kqueue");
    178      1.23  jdolecek 		action = ADD_EVENTS;
    179      1.23  jdolecek 	}
    180      1.23  jdolecek 
    181       1.1     glass 	for (;;) {
    182       1.9       cjs 		while ((ch = getc(fp)) != EOF)  {
    183       1.1     glass 			if (putchar(ch) == EOF)
    184       1.1     glass 				oerr();
    185       1.9       cjs 		}
    186       1.6       jtc 		if (ferror(fp)) {
    187       1.1     glass 			ierr();
    188       1.6       jtc 			return;
    189       1.6       jtc 		}
    190       1.1     glass 		(void)fflush(stdout);
    191       1.1     glass 		if (!fflag)
    192       1.1     glass 			break;
    193      1.23  jdolecek 
    194       1.1     glass 		clearerr(fp);
    195       1.9       cjs 
    196      1.23  jdolecek 		switch (action) {
    197      1.23  jdolecek 		case ADD_EVENTS:
    198      1.23  jdolecek 			n = 0;
    199      1.23  jdolecek 
    200      1.23  jdolecek 			memset(ev, 0, sizeof(ev));
    201      1.23  jdolecek 			if (fflag == 2 && fileno(fp) != STDIN_FILENO) {
    202      1.23  jdolecek 				EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
    203      1.31  christos 				    EV_ADD | EV_ENABLE | EV_CLEAR,
    204      1.31  christos 				    NOTE_DELETE | NOTE_RENAME, 0, 0);
    205      1.23  jdolecek 				n++;
    206      1.23  jdolecek 			}
    207      1.23  jdolecek 			EV_SET(&ev[n], fileno(fp), EVFILT_READ,
    208      1.31  christos 			    EV_ADD | EV_ENABLE, 0, 0, 0);
    209      1.23  jdolecek 			n++;
    210      1.23  jdolecek 
    211      1.31  christos 			if (kevent(kq, ev, n, NULL, 0, NULL) == -1) {
    212      1.23  jdolecek 				close(kq);
    213      1.23  jdolecek 				kq = -1;
    214      1.23  jdolecek 				action = USE_SLEEP;
    215      1.23  jdolecek 			} else {
    216      1.23  jdolecek 				action = USE_KQUEUE;
    217      1.23  jdolecek 			}
    218      1.23  jdolecek 			break;
    219      1.23  jdolecek 
    220      1.23  jdolecek 		case USE_KQUEUE:
    221      1.31  christos 			if (kevent(kq, NULL, 0, ev, 1, NULL) == -1)
    222      1.30  christos 				xerr(1, "kevent");
    223      1.23  jdolecek 
    224      1.23  jdolecek 			if (ev[0].filter == EVFILT_VNODE) {
    225      1.23  jdolecek 				/* file was rotated, wait until it reappears */
    226      1.23  jdolecek 				action = USE_SLEEP;
    227      1.23  jdolecek 			} else if (ev[0].data < 0) {
    228      1.23  jdolecek 				/* file shrank, reposition to end */
    229      1.23  jdolecek 				if (fseek(fp, 0L, SEEK_END) == -1) {
    230      1.23  jdolecek 					ierr();
    231      1.23  jdolecek 					return;
    232       1.9       cjs 				}
    233       1.9       cjs 			}
    234      1.23  jdolecek 			break;
    235      1.23  jdolecek 
    236      1.23  jdolecek 		case USE_SLEEP:
    237      1.23  jdolecek 			/*
    238      1.23  jdolecek 			 * We pause for one second after displaying any data
    239      1.23  jdolecek 			 * that has accumulated since we read the file.
    240      1.23  jdolecek 			 */
    241      1.24     lukem                 	(void) sleep(1);
    242      1.23  jdolecek 
    243      1.23  jdolecek 			if (fflag == 2 && fileno(fp) != STDIN_FILENO &&
    244      1.23  jdolecek 			    stat(fname, &statbuf) != -1) {
    245      1.23  jdolecek 				if (statbuf.st_ino != sbp->st_ino ||
    246      1.23  jdolecek 				    statbuf.st_dev != sbp->st_dev ||
    247      1.23  jdolecek 				    statbuf.st_rdev != sbp->st_rdev ||
    248      1.23  jdolecek 				    statbuf.st_nlink == 0) {
    249      1.23  jdolecek 					fp = freopen(fname, "r", fp);
    250      1.23  jdolecek 					if (fp == NULL) {
    251      1.23  jdolecek 						ierr();
    252      1.28  christos 						goto out;
    253      1.23  jdolecek 					}
    254      1.23  jdolecek 					*sbp = statbuf;
    255      1.23  jdolecek 					if (kq != -1)
    256      1.23  jdolecek 						action = ADD_EVENTS;
    257      1.23  jdolecek 				} else if (kq != -1)
    258      1.23  jdolecek 					action = USE_KQUEUE;
    259      1.23  jdolecek 			}
    260      1.23  jdolecek 			break;
    261       1.9       cjs 		}
    262       1.1     glass 	}
    263      1.28  christos out:
    264      1.23  jdolecek 	if (fflag && kq != -1)
    265      1.23  jdolecek 		close(kq);
    266       1.1     glass }
    267       1.1     glass 
    268       1.1     glass /*
    269       1.1     glass  * rlines -- display the last offset lines of the file.
    270      1.16       cgd  *
    271      1.16       cgd  * Non-zero return means than a (non-fatal) error occurred.
    272       1.1     glass  */
    273      1.16       cgd static int
    274      1.26    itojun rlines(FILE *fp, off_t off, struct stat *sbp)
    275       1.1     glass {
    276      1.17  explorer 	off_t file_size;
    277      1.17  explorer 	off_t file_remaining;
    278      1.27     lukem 	char *p = NULL;
    279      1.27     lukem 	char *start = NULL;
    280      1.17  explorer 	off_t mmap_size;
    281      1.17  explorer 	off_t mmap_offset;
    282      1.27     lukem 	off_t mmap_remaining = 0;
    283       1.1     glass 
    284      1.17  explorer #define MMAP_MAXSIZE  (10 * 1024 * 1024)
    285      1.17  explorer 
    286      1.17  explorer 	if (!(file_size = sbp->st_size))
    287      1.31  christos 		return 0;
    288      1.17  explorer 	file_remaining = file_size;
    289       1.1     glass 
    290      1.18  explorer 	if (file_remaining > MMAP_MAXSIZE) {
    291      1.17  explorer 		mmap_size = MMAP_MAXSIZE;
    292      1.18  explorer 		mmap_offset = file_remaining - MMAP_MAXSIZE;
    293      1.17  explorer 	} else {
    294      1.18  explorer 		mmap_size = file_remaining;
    295      1.17  explorer 		mmap_offset = 0;
    296       1.6       jtc 	}
    297       1.6       jtc 
    298      1.17  explorer 	while (off) {
    299      1.17  explorer 		start = mmap(NULL, (size_t)mmap_size, PROT_READ,
    300      1.17  explorer 			     MAP_FILE|MAP_SHARED, fileno(fp), mmap_offset);
    301      1.17  explorer 		if (start == MAP_FAILED) {
    302      1.30  christos 			xerr(0, "%s", fname);
    303      1.31  christos 			return 1;
    304      1.17  explorer 		}
    305      1.17  explorer 
    306      1.17  explorer 		mmap_remaining = mmap_size;
    307      1.17  explorer 		/* Last char is special, ignore whether newline or not. */
    308      1.17  explorer 		for (p = start + mmap_remaining - 1 ; --mmap_remaining ; )
    309      1.17  explorer 			if (*--p == '\n' && !--off) {
    310      1.17  explorer 				++p;
    311      1.17  explorer 				break;
    312      1.17  explorer 			}
    313      1.17  explorer 
    314      1.17  explorer 		file_remaining -= mmap_size - mmap_remaining;
    315       1.1     glass 
    316      1.17  explorer 		if (off == 0)
    317      1.18  explorer 			break;
    318      1.18  explorer 
    319      1.18  explorer 		if (file_remaining == 0)
    320       1.1     glass 			break;
    321      1.17  explorer 
    322      1.17  explorer 		if (munmap(start, mmap_size)) {
    323      1.30  christos 			xerr(0, "%s", fname);
    324      1.31  christos 			return 1;
    325       1.1     glass 		}
    326       1.1     glass 
    327      1.17  explorer 		if (mmap_offset >= MMAP_MAXSIZE) {
    328      1.17  explorer 			mmap_offset -= MMAP_MAXSIZE;
    329      1.17  explorer 		} else {
    330      1.17  explorer 			mmap_offset = 0;
    331      1.17  explorer 			mmap_size = file_remaining;
    332      1.17  explorer 		}
    333      1.17  explorer 	}
    334      1.17  explorer 
    335      1.17  explorer 	/*
    336      1.17  explorer 	 * Output the (perhaps partial) data in this mmap'd block.
    337      1.17  explorer 	 */
    338      1.17  explorer 	WR(p, mmap_size - mmap_remaining);
    339      1.17  explorer 	file_remaining += mmap_size - mmap_remaining;
    340      1.17  explorer 	if (munmap(start, mmap_size)) {
    341      1.30  christos 		xerr(0, "%s", fname);
    342      1.31  christos 		return 1;
    343       1.6       jtc 	}
    344      1.17  explorer 
    345      1.17  explorer 	/*
    346      1.17  explorer 	 * Set the file pointer to reflect the length displayed.
    347      1.17  explorer 	 * This will cause the caller to redisplay the data if/when
    348      1.17  explorer 	 * needed.
    349      1.17  explorer 	 */
    350      1.17  explorer 	if (fseeko(fp, file_remaining, SEEK_SET) == -1) {
    351      1.17  explorer 		ierr();
    352      1.31  christos 		return 1;
    353       1.5       jtc 	}
    354      1.31  christos 	return 0;
    355       1.1     glass }
    356