Annotation of src/usr.bin/tail/forward.c, Revision 1.24
1.24 ! landry 1: /* $OpenBSD: forward.c,v 1.23 2007/10/22 01:14:26 deraadt Exp $ */
1.2 niklas 2: /* $NetBSD: forward.c,v 1.7 1996/02/13 16:49:10 ghudson Exp $ */
1.1 deraadt 3:
4: /*-
5: * Copyright (c) 1991, 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * This code is derived from software contributed to Berkeley by
9: * Edward Sze-Tyan Wang.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
1.15 millert 19: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #ifndef lint
37: #if 0
38: static char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
39: #endif
1.24 ! landry 40: static char rcsid[] = "$OpenBSD: forward.c,v 1.23 2007/10/22 01:14:26 deraadt Exp $";
1.1 deraadt 41: #endif /* not lint */
42:
43: #include <sys/types.h>
44: #include <sys/stat.h>
1.12 art 45: #include <sys/event.h>
1.1 deraadt 46:
1.4 millert 47: #include <err.h>
1.1 deraadt 48: #include <stdio.h>
49: #include <string.h>
1.4 millert 50: #include <unistd.h>
1.24 ! landry 51: #include <stdlib.h>
1.4 millert 52:
1.1 deraadt 53: #include "extern.h"
54:
1.18 otto 55: static int rlines(FILE *, off_t, struct stat *);
1.1 deraadt 56:
57: /*
58: * forward -- display the file, from an offset, forward.
59: *
60: * There are eight separate cases for this -- regular and non-regular
61: * files, by bytes or lines and from the beginning or end of the file.
62: *
63: * FBYTES byte offset from the beginning of the file
64: * REG seek
65: * NOREG read, counting bytes
66: *
67: * FLINES line offset from the beginning of the file
68: * REG read, counting lines
69: * NOREG read, counting lines
70: *
71: * RBYTES byte offset from the end of the file
72: * REG seek
73: * NOREG cyclically read characters into a wrap-around buffer
74: *
75: * RLINES
1.16 henning 76: * REG step back until the correct offset is reached.
1.1 deraadt 77: * NOREG cyclically read lines into a wrap-around array of buffers
78: */
79: void
1.21 kjell 80: forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
1.1 deraadt 81: {
1.12 art 82: int ch;
1.1 deraadt 83:
84: switch(style) {
85: case FBYTES:
86: if (off == 0)
87: break;
88: if (S_ISREG(sbp->st_mode)) {
89: if (sbp->st_size < off)
90: off = sbp->st_size;
1.18 otto 91: if (fseeko(fp, off, SEEK_SET) == -1) {
1.1 deraadt 92: ierr();
93: return;
94: }
95: } else while (off--)
96: if ((ch = getc(fp)) == EOF) {
97: if (ferror(fp)) {
98: ierr();
99: return;
100: }
101: break;
102: }
103: break;
104: case FLINES:
105: if (off == 0)
106: break;
107: for (;;) {
108: if ((ch = getc(fp)) == EOF) {
109: if (ferror(fp)) {
110: ierr();
111: return;
112: }
113: break;
114: }
115: if (ch == '\n' && !--off)
116: break;
117: }
118: break;
119: case RBYTES:
120: if (S_ISREG(sbp->st_mode)) {
121: if (sbp->st_size >= off &&
1.18 otto 122: fseeko(fp, -off, SEEK_END) == -1) {
1.1 deraadt 123: ierr();
124: return;
125: }
126: } else if (off == 0) {
1.7 millert 127: while (getc(fp) != EOF)
128: ;
1.1 deraadt 129: if (ferror(fp)) {
130: ierr();
131: return;
132: }
1.9 ericj 133: } else {
134: if (bytes(fp, off))
135: return;
136: }
1.1 deraadt 137: break;
138: case RLINES:
1.7 millert 139: if (S_ISREG(sbp->st_mode)) {
1.1 deraadt 140: if (!off) {
1.18 otto 141: if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
1.1 deraadt 142: ierr();
143: return;
144: }
1.7 millert 145: } else if (rlines(fp, off, sbp) != 0)
146: lines(fp, off);
147: } else if (off == 0) {
148: while (getc(fp) != EOF)
149: ;
1.1 deraadt 150: if (ferror(fp)) {
151: ierr();
152: return;
153: }
1.9 ericj 154: } else {
155: if (lines(fp, off))
156: return;
157: }
1.1 deraadt 158: break;
159: }
160:
1.24 ! landry 161: while (!feof(fp) && (ch = getc(fp)) != EOF)
! 162: if (putchar(ch) == EOF)
! 163: oerr();
! 164: if (ferror(fp)) {
! 165: ierr();
! 166: return;
! 167: }
! 168: (void)fflush(stdout);
! 169: }
! 170:
! 171: struct file_info {
! 172: FILE *fp;
! 173: char *fname;
! 174: struct stat fst;
! 175: };
! 176:
! 177: /*
! 178: * follow one or multiple files, i.e don't stop when end-of-file is reached,
! 179: * but rather wait for additional data to be appended to the input.
! 180: * this implements -f switch.
! 181: */
! 182: void
! 183: follow(char **fnames, int nbfiles, enum STYLE style, off_t off)
! 184: {
! 185: int ch, first, i;
! 186: int kq;
! 187: struct kevent ke;
! 188: struct stat cst;
! 189: FILE *fp;
! 190: struct file_info *files;
! 191:
1.12 art 192: kq = -1;
1.24 ! landry 193: if ((kq = kqueue()) < 0)
! 194: err(2, "kqueue() failed");
! 195:
! 196: if ((files = calloc(nbfiles, sizeof(struct file_info))) == NULL)
! 197: err(1, "calloc() failed");
! 198:
! 199: for (first = 1, i = 0; (files[i].fname = *fnames++); i++) {
! 200: if ((fp = fopen(files[i].fname, "r")) == NULL ||
! 201: fstat(fileno(fp), &(files[i].fst))) {
! 202: warn("%s",files[i].fname);
! 203: nbfiles--;
! 204: i--;
! 205: continue;
! 206: }
! 207: if (S_ISDIR(files[i].fst.st_mode)) {
! 208: warnx("%s is a directory, skipping.",files[i].fname);
! 209: nbfiles--;
! 210: i--;
! 211: continue;
! 212: }
! 213: files[i].fp = fp;
! 214: if (nbfiles > 1) {
! 215: (void)printf("%s==> %s <==\n",
! 216: first ? "" : "\n", files[i].fname);
! 217: first = 0;
! 218: }
! 219:
! 220: /* print from the given offset to the end */
! 221: if (off != 0) {
! 222: if (style == RBYTES) {
! 223: if (S_ISREG(files[i].fst.st_mode)) {
! 224: if (files[i].fst.st_size >= off &&
! 225: fseeko(fp, -off, SEEK_END) == -1) {
! 226: ierr();
! 227: goto cleanup;
! 228: }
! 229: }
! 230: if (bytes(fp, off))
! 231: goto cleanup;
! 232: } else if (rlines(fp, off, &(files[i].fst)) != 0)
! 233: lines(fp, off);
! 234: }
! 235: (void)fflush(stdout);
! 236:
! 237: /* one event to see if there is data to read */
! 238: ke.ident = fileno(fp);
1.23 deraadt 239: EV_SET(&ke, fileno(fp), EVFILT_READ,
240: EV_ENABLE | EV_ADD | EV_CLEAR,
1.24 ! landry 241: NULL, 0, NULL);
! 242: if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
! 243: goto cleanup;
! 244: else if (S_ISREG(files[i].fst.st_mode)) {
! 245: /* one event to detect if inode changed */
1.23 deraadt 246: EV_SET(&ke, fileno(fp), EVFILT_VNODE,
247: EV_ENABLE | EV_ADD | EV_CLEAR,
248: NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,
249: 0, NULL);
1.24 ! landry 250: if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
! 251: goto cleanup;
1.12 art 252: }
253: }
254:
1.24 ! landry 255: /* no files to read */
! 256: if (nbfiles == 0)
! 257: goto cleanup;
! 258:
! 259: if (fp == NULL)
! 260: fp = files[nbfiles - 1].fp;
! 261:
1.1 deraadt 262: for (;;) {
1.6 millert 263: while (!feof(fp) && (ch = getc(fp)) != EOF)
1.1 deraadt 264: if (putchar(ch) == EOF)
265: oerr();
1.24 ! landry 266: if (ferror(fp))
! 267: goto cleanup;
! 268:
1.1 deraadt 269: (void)fflush(stdout);
270: clearerr(fp);
1.24 ! landry 271: /* give it a chance to fail.. */
! 272: if (kevent(kq, NULL, 0, &ke, 1, NULL) <= 0) {
1.12 art 273: sleep(1);
274: continue;
1.24 ! landry 275: } else {
! 276: /* an event occured on file #i */
! 277: for (i = 0 ; i < nbfiles ; i++)
! 278: if (fileno(files[i].fp) == ke.ident)
! 279: break;
! 280:
! 281: /* EVFILT_READ event, check that it's on the current fp */
! 282: if (ke.filter == EVFILT_READ) {
! 283: if (fp != files[i].fp) {
! 284: (void)printf("\n==> %s <==\n",files[i].fname);
! 285: fp = files[i].fp;
! 286: clearerr(fp);
! 287: }
! 288: /* EVFILT_VNODE event and File was renamed or deleted */
! 289: } else if (ke.fflags & (NOTE_DELETE | NOTE_RENAME)) {
! 290: /* file didn't reappear */
! 291: if (stat(files[i].fname, &cst) != 0) {
! 292: warnx("%s has been renamed or deleted.", files[i].fname);
! 293: if (--nbfiles == 0)
! 294: goto cleanup;
! 295: /* overwrite with the latest file_info */
! 296: fp = files[nbfiles].fp;
! 297: (void)memcpy(&files[i], &files[nbfiles], sizeof(struct file_info));
! 298: } else {
! 299: /* Reopen file if the inode changed */
! 300: if (cst.st_ino != files[i].fst.st_ino) {
! 301: warnx("%s has been replaced, reopening.", files[i].fname);
! 302: if ((fp = freopen(files[i].fname, "r", files[i].fp)) == NULL) {
! 303: ierr();
! 304: goto cleanup;
! 305: }
! 306: /*
! 307: * on freopen(), events corresponding to the fp
! 308: * were deleted from kqueue, we readd them
! 309: */
! 310: ke.ident = fileno(fp);
! 311: EV_SET(&ke, fileno(fp), EVFILT_READ,
! 312: EV_ENABLE | EV_ADD | EV_CLEAR,
! 313: NULL, 0, NULL);
! 314: if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
! 315: goto cleanup;
! 316: else if (S_ISREG(files[i].fst.st_mode)) {
! 317: EV_SET(&ke, fileno(fp), EVFILT_VNODE,
! 318: EV_ENABLE | EV_ADD | EV_CLEAR,
! 319: NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE,
! 320: 0, NULL);
! 321: if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0)
! 322: goto cleanup;
! 323: }
! 324: files[i].fp = fp;
! 325: }
! 326: (void)memcpy(&(files[i].fst), &cst, sizeof(cst));
! 327: }
! 328: } else if (ke.fflags & NOTE_TRUNCATE) {
! 329: /* reset file if it was truncated */
! 330: warnx("%s has been truncated, resetting.", files[i].fname);
! 331: fpurge(files[i].fp);
! 332: rewind(files[i].fp);
! 333: continue;
1.6 millert 334: }
335: }
1.1 deraadt 336: }
1.24 ! landry 337:
! 338: cleanup:
1.12 art 339: if (kq >= 0)
340: close(kq);
1.24 ! landry 341: free(files);
! 342: return;
1.1 deraadt 343: }
344:
345: /*
346: * rlines -- display the last offset lines of the file.
347: */
1.7 millert 348: static int
1.18 otto 349: rlines(FILE *fp, off_t off, struct stat *sbp)
1.1 deraadt 350: {
1.16 henning 351: off_t pos;
352: int ch;
1.1 deraadt 353:
1.16 henning 354: pos = sbp->st_size;
355: if (pos == 0)
1.7 millert 356: return (0);
1.1 deraadt 357:
1.16 henning 358: /*
359: * Position before char.
1.17 otto 360: * Last char is special, ignore it whether newline or not.
1.16 henning 361: */
362: pos -= 2;
363: ch = EOF;
364: for (; off > 0 && pos >= 0; pos--) {
365: /* A seek per char isn't a problem with a smart stdio */
366: if (fseeko(fp, pos, SEEK_SET) == -1) {
367: ierr();
368: return (1);
369: }
370: if ((ch = getc(fp)) == '\n')
371: off--;
372: else if (ch == EOF) {
373: if (ferror(fp)) {
374: ierr();
375: return (1);
376: }
1.1 deraadt 377: break;
378: }
1.16 henning 379: }
380: /* If we read until start of file, put back last read char */
381: if (pos < 0 && off > 0 && ch != EOF && ungetc(ch, fp) == EOF) {
1.1 deraadt 382: ierr();
1.7 millert 383: return (1);
1.1 deraadt 384: }
1.16 henning 385:
386: while (!feof(fp) && (ch = getc(fp)) != EOF)
387: if (putchar(ch) == EOF)
388: oerr();
389: if (ferror(fp)) {
1.7 millert 390: ierr();
391: return (1);
1.1 deraadt 392: }
1.7 millert 393:
394: return (0);
1.1 deraadt 395: }