[BACK]Return to forward.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tail

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: }