Change the behaviour of -F slightly; it now persists (forever) in
trying to open files rather than giving up when it encounters an error. ENOENT errors are not reported. As a result, files that are moved away then recreated are not at risk of being 'lost' to tail. Files that are recreated and temporarily have unreadable permissions will be shown when they are fixed. This behaviour is consistent with the GNU version of tail but without the verbiage that goes with the GNU version. This change also fixes error messages accompanying -f and -F. They no longer report problems with (null)! MFC after: 3 weeks
This commit is contained in:
parent
ca67260eba
commit
22da50cfc8
@ -61,16 +61,15 @@ 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 *);
|
||||
void forward(FILE *, const char *, enum STYLE, off_t, struct stat *);
|
||||
void reverse(FILE *, const char *, enum STYLE, off_t, struct stat *);
|
||||
|
||||
int bytes(FILE *, off_t);
|
||||
int lines(FILE *, off_t);
|
||||
int bytes(FILE *, const char *, off_t);
|
||||
int lines(FILE *, const char *, off_t);
|
||||
|
||||
void ierr(void);
|
||||
void ierr(const char *);
|
||||
void oerr(void);
|
||||
int mapprint(struct mapinfo *, off_t, off_t);
|
||||
int maparound(struct mapinfo *, off_t);
|
||||
|
||||
extern int Fflag, fflag, qflag, rflag, rval, no_files;
|
||||
extern const char *fname;
|
||||
|
@ -61,8 +61,8 @@ static const char sccsid[] = "@(#)forward.c 8.1 (Berkeley) 6/6/93";
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
static void rlines(FILE *, off_t, struct stat *);
|
||||
static void show(file_info_t *);
|
||||
static void rlines(FILE *, const char *fn, off_t, struct stat *);
|
||||
static int show(file_info_t *);
|
||||
static void set_events(file_info_t *files);
|
||||
|
||||
/* defines for inner loop actions */
|
||||
@ -99,7 +99,7 @@ static const file_info_t *last;
|
||||
* NOREG cyclically read lines into a wrap-around array of buffers
|
||||
*/
|
||||
void
|
||||
forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
forward(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
|
||||
{
|
||||
int ch;
|
||||
|
||||
@ -111,13 +111,13 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
if (sbp->st_size < off)
|
||||
off = sbp->st_size;
|
||||
if (fseeko(fp, off, SEEK_SET) == -1) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
} else while (off--)
|
||||
if ((ch = getc(fp)) == EOF) {
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -129,7 +129,7 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
for (;;) {
|
||||
if ((ch = getc(fp)) == EOF) {
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -142,36 +142,36 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
if (S_ISREG(sbp->st_mode)) {
|
||||
if (sbp->st_size >= off &&
|
||||
fseeko(fp, -off, SEEK_END) == -1) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
} else if (off == 0) {
|
||||
while (getc(fp) != EOF);
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
if (bytes(fp, off))
|
||||
if (bytes(fp, fn, off))
|
||||
return;
|
||||
break;
|
||||
case RLINES:
|
||||
if (S_ISREG(sbp->st_mode))
|
||||
if (!off) {
|
||||
if (fseeko(fp, (off_t)0, SEEK_END) == -1) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
rlines(fp, off, sbp);
|
||||
rlines(fp, fn, off, sbp);
|
||||
else if (off == 0) {
|
||||
while (getc(fp) != EOF);
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
if (lines(fp, off))
|
||||
if (lines(fp, fn, off))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
@ -182,7 +182,7 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
if (putchar(ch) == EOF)
|
||||
oerr();
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
(void)fflush(stdout);
|
||||
@ -192,10 +192,7 @@ forward(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
* rlines -- display the last offset lines of the file.
|
||||
*/
|
||||
static void
|
||||
rlines(fp, off, sbp)
|
||||
FILE *fp;
|
||||
off_t off;
|
||||
struct stat *sbp;
|
||||
rlines(FILE *fp, const char *fn, off_t off, struct stat *sbp)
|
||||
{
|
||||
struct mapinfo map;
|
||||
off_t curoff, size;
|
||||
@ -214,7 +211,7 @@ rlines(fp, off, sbp)
|
||||
curoff = size - 2;
|
||||
while (curoff >= 0) {
|
||||
if (curoff < map.mapoff && maparound(&map, curoff) != 0) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
for (i = curoff - map.mapoff; i >= 0; i--)
|
||||
@ -227,41 +224,44 @@ rlines(fp, off, sbp)
|
||||
}
|
||||
curoff++;
|
||||
if (mapprint(&map, curoff, size - curoff) != 0) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Set the file pointer to reflect the length displayed. */
|
||||
if (fseeko(fp, sbp->st_size, SEEK_SET) == -1) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
if (map.start != NULL && munmap(map.start, map.maplen)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
show(file_info_t *file)
|
||||
{
|
||||
int ch;
|
||||
int ch;
|
||||
|
||||
while ((ch = getc(file->fp)) != EOF) {
|
||||
if (last != file && no_files > 1) {
|
||||
if (!qflag)
|
||||
(void)printf("\n==> %s <==\n", file->file_name);
|
||||
last = file;
|
||||
while ((ch = getc(file->fp)) != EOF) {
|
||||
if (last != file && no_files > 1) {
|
||||
if (!qflag)
|
||||
(void)printf("\n==> %s <==\n", file->file_name);
|
||||
last = file;
|
||||
}
|
||||
if (putchar(ch) == EOF)
|
||||
oerr();
|
||||
}
|
||||
if (putchar(ch) == EOF)
|
||||
oerr();
|
||||
}
|
||||
(void)fflush(stdout);
|
||||
if (ferror(file->fp)) {
|
||||
file->fp = NULL;
|
||||
ierr();
|
||||
} else
|
||||
clearerr(file->fp);
|
||||
(void)fflush(stdout);
|
||||
if (ferror(file->fp)) {
|
||||
fclose(file->fp);
|
||||
file->fp = NULL;
|
||||
ierr(file->file_name);
|
||||
return 0;
|
||||
}
|
||||
clearerr(file->fp);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -309,7 +309,7 @@ set_events(file_info_t *files)
|
||||
void
|
||||
follow(file_info_t *files, enum STYLE style, off_t off)
|
||||
{
|
||||
int active, i, n = -1;
|
||||
int active, ev_change, i, n = -1;
|
||||
struct stat sb2;
|
||||
file_info_t *file;
|
||||
struct timespec ts;
|
||||
@ -325,12 +325,12 @@ follow(file_info_t *files, enum STYLE style, off_t off)
|
||||
n++;
|
||||
if (no_files > 1 && !qflag)
|
||||
(void)printf("\n==> %s <==\n", file->file_name);
|
||||
forward(file->fp, style, off, &file->st);
|
||||
forward(file->fp, file->file_name, style, off, &file->st);
|
||||
if (Fflag && fileno(file->fp) != STDIN_FILENO)
|
||||
n++;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (! active)
|
||||
if (!Fflag && !active)
|
||||
return;
|
||||
|
||||
last = --file;
|
||||
@ -344,28 +344,56 @@ follow(file_info_t *files, enum STYLE style, off_t off)
|
||||
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 &&
|
||||
(sb2.st_ino != file->st.st_ino ||
|
||||
sb2.st_dev != file->st.st_dev ||
|
||||
sb2.st_nlink == 0)) {
|
||||
show(file);
|
||||
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);
|
||||
ev_change = 0;
|
||||
if (Fflag) {
|
||||
for (i = 0, file = files; i < no_files; i++, file++) {
|
||||
if (!file->fp) {
|
||||
file->fp = fopen(file->file_name, "r");
|
||||
if (file->fp != NULL &&
|
||||
fstat(fileno(file->fp), &file->st)
|
||||
== -1) {
|
||||
fclose(file->fp);
|
||||
file->fp = NULL;
|
||||
}
|
||||
if (file->fp != NULL)
|
||||
ev_change++;
|
||||
continue;
|
||||
}
|
||||
if (fileno(file->fp) == STDIN_FILENO)
|
||||
continue;
|
||||
if (stat(file->file_name, &sb2) == -1) {
|
||||
if (errno != ENOENT)
|
||||
ierr(file->file_name);
|
||||
show(file);
|
||||
fclose(file->fp);
|
||||
file->fp = NULL;
|
||||
ev_change++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sb2.st_ino != file->st.st_ino ||
|
||||
sb2.st_dev != file->st.st_dev ||
|
||||
sb2.st_nlink == 0) {
|
||||
show(file);
|
||||
file->fp = freopen(file->file_name, "r",
|
||||
file->fp);
|
||||
if (file->fp != NULL)
|
||||
memcpy(&file->st, &sb2,
|
||||
sizeof(struct stat));
|
||||
else if (errno != ENOENT)
|
||||
ierr(file->file_name);
|
||||
ev_change++;
|
||||
}
|
||||
}
|
||||
show(file);
|
||||
}
|
||||
|
||||
for (i = 0, file = files; i < no_files; i++, file++)
|
||||
if (file->fp && !show(file))
|
||||
ev_change++;
|
||||
|
||||
if (ev_change)
|
||||
set_events(files);
|
||||
|
||||
switch (action) {
|
||||
case USE_KQUEUE:
|
||||
ts.tv_sec = 1;
|
||||
@ -381,9 +409,9 @@ follow(file_info_t *files, enum STYLE style, off_t off)
|
||||
/* timeout */
|
||||
break;
|
||||
} else if (ev->filter == EVFILT_READ && ev->data < 0) {
|
||||
/* file shrank, reposition to end */
|
||||
/* file shrank, reposition to end */
|
||||
if (lseek(ev->ident, (off_t)0, SEEK_END) == -1) {
|
||||
ierr();
|
||||
ierr(file->file_name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
|
||||
#include "extern.h"
|
||||
|
||||
void
|
||||
ierr()
|
||||
ierr(const char *fname)
|
||||
{
|
||||
warn("%s", fname);
|
||||
rval = 1;
|
||||
|
@ -66,7 +66,7 @@ static const char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/6/93";
|
||||
* the end.
|
||||
*/
|
||||
int
|
||||
bytes(FILE *fp, off_t off)
|
||||
bytes(FILE *fp, const char *fn, off_t off)
|
||||
{
|
||||
int ch, len, tlen;
|
||||
char *ep, *p, *t;
|
||||
@ -84,7 +84,7 @@ bytes(FILE *fp, off_t off)
|
||||
}
|
||||
}
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
free(sp);
|
||||
return 1;
|
||||
}
|
||||
@ -136,7 +136,7 @@ bytes(FILE *fp, off_t off)
|
||||
* the end.
|
||||
*/
|
||||
int
|
||||
lines(FILE *fp, off_t off)
|
||||
lines(FILE *fp, const char *fn, off_t off)
|
||||
{
|
||||
struct {
|
||||
int blen;
|
||||
@ -178,7 +178,7 @@ lines(FILE *fp, off_t off)
|
||||
}
|
||||
}
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
rc = 1;
|
||||
goto done;
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "extern.h"
|
||||
|
||||
static void r_buf(FILE *);
|
||||
static void r_reg(FILE *, enum STYLE, off_t, struct stat *);
|
||||
static void r_buf(FILE *, const char *);
|
||||
static void r_reg(FILE *, const char *, enum STYLE, off_t, struct stat *);
|
||||
|
||||
/*
|
||||
* reverse -- display input in reverse order by line.
|
||||
@ -80,25 +80,25 @@ static void r_reg(FILE *, enum STYLE, off_t, struct stat *);
|
||||
* NOREG cyclically read input into a linked list of buffers
|
||||
*/
|
||||
void
|
||||
reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
reverse(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
|
||||
{
|
||||
if (style != REVERSE && off == 0)
|
||||
return;
|
||||
|
||||
if (S_ISREG(sbp->st_mode))
|
||||
r_reg(fp, style, off, sbp);
|
||||
r_reg(fp, fn, style, off, sbp);
|
||||
else
|
||||
switch(style) {
|
||||
case FBYTES:
|
||||
case RBYTES:
|
||||
bytes(fp, off);
|
||||
bytes(fp, fn, off);
|
||||
break;
|
||||
case FLINES:
|
||||
case RLINES:
|
||||
lines(fp, off);
|
||||
lines(fp, fn, off);
|
||||
break;
|
||||
case REVERSE:
|
||||
r_buf(fp);
|
||||
r_buf(fp, fn);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -109,7 +109,7 @@ reverse(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
* r_reg -- display a regular file in reverse order by line.
|
||||
*/
|
||||
static void
|
||||
r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
r_reg(FILE *fp, const char *fn, enum STYLE style, off_t off, struct stat *sbp)
|
||||
{
|
||||
struct mapinfo map;
|
||||
off_t curoff, size, lineend;
|
||||
@ -132,7 +132,7 @@ r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
if (curoff < map.mapoff ||
|
||||
curoff >= map.mapoff + (off_t)map.maplen) {
|
||||
if (maparound(&map, curoff) != 0) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -149,7 +149,7 @@ r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
|
||||
/* Print the line and update offsets. */
|
||||
if (mapprint(&map, curoff + 1, lineend - curoff - 1) != 0) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
lineend = curoff + 1;
|
||||
@ -165,11 +165,11 @@ r_reg(FILE *fp, enum STYLE style, off_t off, struct stat *sbp)
|
||||
}
|
||||
}
|
||||
if (curoff < 0 && mapprint(&map, 0, lineend) != 0) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
if (map.start != NULL && munmap(map.start, map.maplen))
|
||||
ierr();
|
||||
ierr(fn);
|
||||
}
|
||||
|
||||
typedef struct bf {
|
||||
@ -190,7 +190,7 @@ typedef struct bf {
|
||||
* user warned).
|
||||
*/
|
||||
static void
|
||||
r_buf(FILE *fp)
|
||||
r_buf(FILE *fp, const char *fn)
|
||||
{
|
||||
BF *mark, *tl, *tr;
|
||||
int ch, len, llen;
|
||||
@ -227,7 +227,7 @@ r_buf(FILE *fp)
|
||||
*p++ = ch;
|
||||
|
||||
if (ferror(fp)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
.\" @(#)tail.1 8.1 (Berkeley) 6/6/93
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 29, 2006
|
||||
.Dd June 05, 2009
|
||||
.Dt TAIL 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -106,9 +106,16 @@ will also check to see if the file being followed has been renamed or rotated.
|
||||
The file is closed and reopened when
|
||||
.Nm
|
||||
detects that the filename being read from has a new inode number.
|
||||
.Pp
|
||||
If the file being followed does not (yet) exist or if it is removed, tail
|
||||
will keep looking and will display the file from the beginning if and when
|
||||
it is created.
|
||||
.Pp
|
||||
The
|
||||
.Fl F
|
||||
option is ignored if reading from standard input rather than a file.
|
||||
option is the same as the
|
||||
.Fl f
|
||||
option if reading from standard input rather than a file.
|
||||
.It Fl n Ar number
|
||||
The location is
|
||||
.Ar number
|
||||
|
@ -61,7 +61,6 @@ static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93";
|
||||
#include "extern.h"
|
||||
|
||||
int Fflag, fflag, qflag, rflag, rval, no_files;
|
||||
const char *fname;
|
||||
|
||||
file_info_t *files;
|
||||
|
||||
@ -72,6 +71,7 @@ int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct stat sb;
|
||||
const char *fn;
|
||||
FILE *fp;
|
||||
off_t off;
|
||||
enum STYLE style;
|
||||
@ -175,20 +175,23 @@ main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (*argv && fflag) {
|
||||
files = (struct file_info *) malloc(no_files * sizeof(struct file_info));
|
||||
if (! files)
|
||||
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);
|
||||
for (file = files; (fn = *argv++); file++) {
|
||||
file->file_name = strdup(fn);
|
||||
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;
|
||||
if (file->fp != NULL) {
|
||||
fclose(file->fp);
|
||||
file->fp = NULL;
|
||||
}
|
||||
if (!Fflag || errno != ENOENT)
|
||||
ierr(file->file_name);
|
||||
}
|
||||
}
|
||||
follow(files, style, off);
|
||||
@ -197,29 +200,29 @@ main(int argc, char *argv[])
|
||||
}
|
||||
free(files);
|
||||
} else if (*argv) {
|
||||
for (first = 1; (fname = *argv++);) {
|
||||
if ((fp = fopen(fname, "r")) == NULL ||
|
||||
for (first = 1; (fn = *argv++);) {
|
||||
if ((fp = fopen(fn, "r")) == NULL ||
|
||||
fstat(fileno(fp), &sb)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
continue;
|
||||
}
|
||||
if (argc > 1 && !qflag) {
|
||||
(void)printf("%s==> %s <==\n",
|
||||
first ? "" : "\n", fname);
|
||||
first ? "" : "\n", fn);
|
||||
first = 0;
|
||||
(void)fflush(stdout);
|
||||
}
|
||||
|
||||
if (rflag)
|
||||
reverse(fp, style, off, &sb);
|
||||
reverse(fp, fn, style, off, &sb);
|
||||
else
|
||||
forward(fp, style, off, &sb);
|
||||
forward(fp, fn, style, off, &sb);
|
||||
}
|
||||
} else {
|
||||
fname = "stdin";
|
||||
fn = "stdin";
|
||||
|
||||
if (fstat(fileno(stdin), &sb)) {
|
||||
ierr();
|
||||
ierr(fn);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -234,9 +237,9 @@ main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
if (rflag)
|
||||
reverse(stdin, style, off, &sb);
|
||||
reverse(stdin, fn, style, off, &sb);
|
||||
else
|
||||
forward(stdin, style, off, &sb);
|
||||
forward(stdin, fn, style, off, &sb);
|
||||
}
|
||||
exit(rval);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user