summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.bin/tail/extern.h11
-rw-r--r--usr.bin/tail/forward.c248
-rw-r--r--usr.bin/tail/tail.c37
3 files changed, 200 insertions, 96 deletions
diff --git a/usr.bin/tail/extern.h b/usr.bin/tail/extern.h
index 8ad21ec..9c45a1b 100644
--- a/usr.bin/tail/extern.h
+++ b/usr.bin/tail/extern.h
@@ -50,8 +50,17 @@ struct mapinfo {
int fd;
};
+struct file_info {
+ FILE *fp;
+ char *file_name;
+ struct stat st;
+};
+
+typedef struct file_info file_info_t;
+
enum STYLE { NOTSET = 0, FBYTES, FLINES, RBYTES, RLINES, REVERSE };
+void follow(file_info_t *, enum STYLE, off_t);
void forward(FILE *, enum STYLE, off_t, struct stat *);
void reverse(FILE *, enum STYLE, off_t, struct stat *);
@@ -63,5 +72,5 @@ void oerr(void);
int mapprint(struct mapinfo *, off_t, off_t);
int maparound(struct mapinfo *, off_t);
-extern int Fflag, fflag, rflag, rval;
+extern int Fflag, fflag, rflag, rval, no_files;
extern const char *fname;
diff --git a/usr.bin/tail/forward.c b/usr.bin/tail/forward.c
index 6f9256f..29f486d 100644
--- a/usr.bin/tail/forward.c
+++ b/usr.bin/tail/forward.c
@@ -66,6 +66,10 @@ static void rlines(FILE *, off_t, struct stat *);
#define USE_KQUEUE 1
#define ADD_EVENTS 2
+struct kevent *ev;
+int action = USE_SLEEP;
+int kq;
+
/*
* forward -- display the file, from an offset, forward.
*
@@ -172,95 +176,14 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
break;
}
- if (fflag) {
- kq = kqueue();
- if (kq < 0)
- err(1, "kqueue");
- action = ADD_EVENTS;
- }
-
- for (;;) {
- while ((ch = getc(fp)) != EOF)
- if (putchar(ch) == EOF)
- oerr();
- if (ferror(fp)) {
- ierr();
- return;
- }
- (void)fflush(stdout);
- if (! fflag)
- break;
- clearerr(fp);
-
- switch (action) {
- case ADD_EVENTS:
- n = 0;
- ts.tv_sec = 0;
- ts.tv_nsec = 0;
-
- if (Fflag && fileno(fp) != STDIN_FILENO) {
- EV_SET(&ev[n], fileno(fp), EVFILT_VNODE,
- EV_ADD | EV_ENABLE | EV_CLEAR,
- NOTE_DELETE | NOTE_RENAME, 0, 0);
- n++;
- }
- EV_SET(&ev[n], fileno(fp), EVFILT_READ,
- EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0);
- n++;
-
- if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
- action = USE_SLEEP;
- } else {
- action = USE_KQUEUE;
- }
- break;
-
- case USE_KQUEUE:
- ts.tv_sec = 1;
- ts.tv_nsec = 0;
- /*
- * In the -F case we set a timeout to ensure that
- * we re-stat the file at least once every second.
- */
- n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL);
- if (n < 0)
- err(1, "kevent");
- if (n == 0) {
- /* timeout */
- break;
- } else if (ev->filter == EVFILT_READ && ev->data < 0) {
- /* file shrank, reposition to end */
- if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
- ierr();
- return;
- }
- }
- break;
-
- case USE_SLEEP:
- (void) usleep(250000);
- clearerr(fp);
- break;
- }
-
- if (Fflag && fileno(fp) != STDIN_FILENO) {
- while (stat(fname, &sb2) != 0)
- /* file was rotated, wait until it reappears */
- (void)sleep(1);
- if (sb2.st_ino != sbp->st_ino ||
- sb2.st_dev != sbp->st_dev ||
- sb2.st_nlink == 0) {
- fp = freopen(fname, "r", fp);
- if (fp == NULL) {
- ierr();
- return;
- } else {
- *sbp = sb2;
- action = ADD_EVENTS;
- }
- }
- }
+ while ((ch = getc(fp)) != EOF)
+ if (putchar(ch) == EOF)
+ oerr();
+ if (ferror(fp)) {
+ ierr();
+ return;
}
+ (void)fflush(stdout);
}
/*
@@ -316,3 +239,152 @@ rlines(fp, off, sbp)
return;
}
}
+
+/*
+ * follow -- display the file, from an offset, forward.
+ *
+ */
+
+void
+show(file_info_t *file)
+{
+ int ch, first;
+
+ first = 1;
+ while ((ch = getc(file->fp)) != EOF) {
+ if (first && no_files > 1) {
+ (void)printf("\n==> %s <==\n", file->file_name);
+ first = 0;
+ }
+ if (putchar(ch) == EOF)
+ oerr();
+ }
+ (void)fflush(stdout);
+ if (ferror(file->fp)) {
+ file->fp = NULL;
+ ierr();
+ } else
+ clearerr(file->fp);
+}
+
+void
+set_events(file_info_t *files)
+{
+ int i, n = 0;
+ file_info_t *file;
+ struct timespec ts;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 0;
+
+ action = USE_KQUEUE;
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ if (! file->fp)
+ continue;
+ if (Fflag && fileno(file->fp) != STDIN_FILENO) {
+ EV_SET(&ev[n], fileno(file->fp), EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_CLEAR,
+ NOTE_DELETE | NOTE_RENAME, 0, 0);
+ n++;
+ }
+ EV_SET(&ev[n], fileno(file->fp), EVFILT_READ,
+ EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0);
+ n++;
+ }
+
+ if (kevent(kq, ev, n, NULL, 0, &ts) < 0) {
+ action = USE_SLEEP;
+ }
+}
+
+void
+follow(file_info_t *files, enum STYLE style, off_t off)
+{
+ int active, i, n = -1;
+ struct stat sb2;
+ struct stat *sbp;
+ file_info_t *file;
+ long spin=1;
+ struct timespec ts;
+
+ /* Position each of the files */
+
+ file = files;
+ active = 0;
+ n = 0;
+ for (i = 0; i < no_files; i++, file++) {
+ if (file->fp) {
+ active = 1;
+ n++;
+ if (no_files > 1)
+ (void)printf("\n==> %s <==\n", file->file_name);
+ forward(file->fp, style, off, &file->st);
+ if (Fflag && fileno(file->fp) != STDIN_FILENO)
+ n++;
+ }
+ }
+ if (! active)
+ return;
+
+ kq = kqueue();
+ if (kq < 0)
+ err(1, "kqueue");
+ ev = malloc(n * sizeof(struct kevent));
+ if (! ev)
+ err(1, "Couldn't allocate memory for kevents.");
+ set_events(files);
+
+ for (;;) {
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ if (! file->fp)
+ continue;
+ if (Fflag && file->fp && fileno(file->fp) != STDIN_FILENO) {
+ if (stat(file->file_name, &sb2) != 0) {
+ /* file was rotated, skip it until it reappears */
+ continue;
+ }
+ if (sb2.st_ino != file->st.st_ino ||
+ sb2.st_dev != file->st.st_dev ||
+ sb2.st_nlink == 0) {
+ file->fp = freopen(file->file_name, "r", file->fp);
+ if (file->fp == NULL) {
+ ierr();
+ continue;
+ } else {
+ memcpy(&file->st, &sb2, sizeof(struct stat));
+ set_events(files);
+ }
+ }
+ }
+ show(file);
+ }
+
+ switch (action) {
+ case USE_KQUEUE:
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ /*
+ * In the -F case we set a timeout to ensure that
+ * we re-stat the file at least once every second.
+ */
+ n = kevent(kq, NULL, 0, ev, 1, Fflag ? &ts : NULL);
+ if (n < 0)
+ err(1, "kevent");
+ if (n == 0) {
+ /* timeout */
+ break;
+ } else if (ev->filter == EVFILT_READ && ev->data < 0) {
+ /* file shrank, reposition to end */
+ if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) {
+ ierr();
+ continue;
+ }
+ }
+ break;
+
+ case USE_SLEEP:
+ (void) usleep(250000);
+ break;
+ }
+ }
+}
diff --git a/usr.bin/tail/tail.c b/usr.bin/tail/tail.c
index 2a3ee13..9162d17 100644
--- a/usr.bin/tail/tail.c
+++ b/usr.bin/tail/tail.c
@@ -60,9 +60,11 @@ static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
#include "extern.h"
-int Fflag, fflag, rflag, rval;
+int Fflag, fflag, rflag, rval, no_files;
const char *fname;
+file_info_t *files;
+
static void obsolete(char **);
static void usage(void);
@@ -73,7 +75,8 @@ main(int argc, char *argv[])
FILE *fp;
off_t off;
enum STYLE style;
- int ch, first;
+ int i, ch, first;
+ file_info_t *file;
char *p;
/*
@@ -138,8 +141,7 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
- if (fflag && argc > 1)
- errx(1, "-f option only appropriate for a single file");
+ no_files = argc ? argc : 1;
/*
* If displaying in reverse, don't permit follow option, and convert
@@ -168,7 +170,29 @@ main(int argc, char *argv[])
}
}
- if (*argv)
+ if (*argv && fflag) {
+ files = (struct file_info *) malloc(no_files * sizeof(struct file_info));
+ if (! files)
+ err(1, "Couldn't malloc space for file descriptors.");
+
+ for (file = files; (fname = *argv++); file++) {
+ file->file_name = malloc(strlen(fname)+1);
+ if (! file->file_name)
+ errx(1, "Couldn't malloc space for file name.");
+ strncpy(file->file_name, fname, strlen(fname)+1);
+ if ((file->fp = fopen(file->file_name, "r")) == NULL ||
+ fstat(fileno(file->fp), &file->st)) {
+ file->fp = NULL;
+ ierr();
+ continue;
+ }
+ }
+ follow(files, style, off);
+ for (i = 0, file = files; i < no_files; i++, file++) {
+ free(file->file_name);
+ }
+ free(files);
+ } else if (*argv) {
for (first = 1; (fname = *argv++);) {
if ((fp = fopen(fname, "r")) == NULL ||
fstat(fileno(fp), &sb)) {
@@ -186,9 +210,8 @@ main(int argc, char *argv[])
reverse(fp, style, off, &sb);
else
forward(fp, style, off, &sb);
- (void)fclose(fp);
}
- else {
+ } else {
fname = "stdin";
if (fstat(fileno(stdin), &sb)) {
OpenPOWER on IntegriCloud