[alt.sources] fast xtail for BSD systems

James.Aspnes@CS.CMU.EDU (05/05/89)

This is a lookalike replacement for Chip Rosenthal's xtail program for
watching the growth of multiple files.  It tends to be much faster
than the original, as it avoids large amounts of system call overhead
by keeping an open file descriptor for each file being watched and by
buffering I/O a bit better.  There is, of course, a cost to the
increased performance:

 1. The number of files that can be watched is now limited by the
    per-process file descriptor limit.

 2. Since xtail never stats the filename, it can't tell if a file
    has been renamed (e.g., using mv).  It does detect when files are
    unlinked.

Except for a few error messages, the output of the program is
identical to the output of its predecessor.


Jim Aspnes <asp@cs.cmu.edu>

----------------------------------------------------------------
#!/bin/sh
sed -e 's:^X::' > xtail.c << EOF
X/* xtail -- fast BSD version of Chip Rosenthal's xtail program */
X
X/* Copyright 1989 by Jim Aspnes */
X/* distributed under the terms of the GNU General Public License */
X
X#include <stdio.h>
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/file.h>
X#include <sys/param.h>
X
X#define NEW_FILE_FORMAT "\n***** %s *****\n" /* banner for changing files */
X#define SLEEPTIME 1		/* seconds between checks */
X
Xstruct {
X    char *name;			/* name of file */
X    int fd;			/* file descriptor for file */
X    off_t size;			/* size of file */
X} files[NOFILE];
X
Xint topfile = 0;		/* number of files */
Xint curfile = -1;		/* file currently being displayed */
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X    int i;
X    struct stat sbuf;
X
X    /* check argc */
X    if(argc < 2) {
X	fprintf(stderr, "Usage: %s filenames\n", argv[0]);
X	exit(1);
X    }
X
X    /* load up file list */
X    for(i = 1; i < argc && topfile < NOFILE; i++) {
X	if((files[topfile].fd = open(argv[i], O_RDONLY, 0)) < 0
X	   || (files[topfile].size
X	       = lseek(files[topfile].fd, 0, L_XTND)) < 0) {
X	    /* tell him the file is bogus */
X	    perror(argv[i]);
X	} else {
X	    files[topfile].name = argv[i];
X	    topfile++;
X	}
X    }
X
X    while(topfile != 0) {
X	/* check each file */
X	for(i = 0; i < topfile; i++) {
X	    if(fstat(files[i].fd, &sbuf) < 0) {
X		/* fstat failed */
X		perror(files[i].name);
X		remove_file(i--);
X	    } else if(sbuf.st_nlink == 0) {
X		/* file is no longer linked */
X		fprintf(stderr,
X			"%s: file has been unlinked\n", files[i].name);
X		remove_file(i--);
X	    } else if(files[i].size < sbuf.st_size
X		      && show_file(i, sbuf.st_size) < 0) {
X		/* show_file failed */
X		perror(files[i].name);
X		remove_file(i--);
X	    }
X	}
X
X	sleep(SLEEPTIME);
X    }
X
X    fputs("No files.\n", stderr);
X    exit(2);
X}
X
Xremove_file(i)
Xint i;
X{
X    /* take it out of the list */
X    close(files[i].fd);
X    files[i] = files[--topfile];
X    if(curfile == i) curfile = -1;
X}
X
X#define Min(x, y) ((x) < (y) ? (x) : (y))
X
Xshow_file(i, newsize)
Xint i;
Xoff_t newsize;
X{
X    int bytes;
X    int bytesread;
X    static char buf[MAXBSIZE];
X
X    /* print new file banner if needed */
X    if(i != curfile) {
X	printf(NEW_FILE_FORMAT, files[i].name);
X	curfile = i;
X    }
X
X    /* suck off the bits */
X    while(files[i].size < newsize) {
X	bytes = Min(MAXBSIZE, newsize - files[i].size);
X	if(read(files[i].fd, buf, bytes) != bytes) {
X	    return(-1);
X	} else {
X	    /* print out buf and adjust file size */
X	    fwrite(buf, sizeof(char), bytes, stdout);
X	    files[i].size += bytes;
X	}
X    }
X
X    /* everything ok */
X    fflush(stdout);
X    return(0);
X}
X
EOF