[alt.sources] xtail - "tail -f" for multiple files

chip@vector.Dallas.TX.US (Chip Rosenthal) (05/03/89)

I always found "tail -f /usr/spool/uucp/LOGFILE" helpful for monitoring
what uucp is doing.  Suddenly along comes HDB, and this trick no longer
works.  The attached now lets me do "xtail /usr/spool/uucp/.Log/*/*".

#! /bin/sh
# this is a "shar" archive - run through "/bin/sh" to extract 4 files:
#   xtail.c xtail.man Makefile Install
# Wrapped by bin@vector on Tue May  2 13:15:58 CDT 1989
# Unpacking this archive requires:  sed test wc (possibly mkdir)
# Existing files will not be clobbered unless "-c" is specified on the cmd line.
if test -f xtail.c -a "$1" != "-c" ; then
    echo "xtail.c: file exists - will not be overwritten"
else
    echo "x - xtail.c (file 1 of 4, 2778 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_xtail.c' > xtail.c
X/*
X * xtail - monitor the growth of files
X *
X * usage: xtail file ...
X *
X * Mon May  1 14:44:30 1989 - Chip Rosenthal <chip@vector.Dallas.TX.US>
X *	Original composition.
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/fcntl.h>
X
X#define MAXFILES	128	/* max number of files to watch		*/
X#define SLEEPTIME	1	/* time between checks, in seconds	*/
X
Xint Nfiles;			/* number of files being watched	*/
Xint Lastfile;			/* Flist[] index of last file displayed	*/
X
Xstruct {
X    char *name;			/* path to file, or NULL for no file	*/
X    struct stat sbuf;		/* recent stat info on this file	*/
X} Flist[MAXFILES];
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X    int fd, ifile, argi;
X    char c;
X    long pos;
X    struct stat sbuf;
X    extern long lseek();
X    extern unsigned sleep();
X
X    /*
X     * Initialize.
X     */
X    Nfiles = 0;			/* no files specified		*/
X    Lastfile = -1;		/* no files printed		*/
X
X    /*
X     * Get files from command line.  Save in list and get initial stat info.
X     */
X    for ( argi = 1 ; argi < argc ; ++argi ) {
X	if ( Nfiles >= MAXFILES ) {
X	    fprintf(stderr,"%s: too many files\n",argv[0]);
X	    exit(1);
X	}
X	if ( stat(Flist[Nfiles].name=argv[argi],&Flist[Nfiles].sbuf) != 0 )
X	    perror(Flist[Nfiles].name);
X	else
X	    ++Nfiles;
X    }
X    if ( Nfiles == 0 ) {
X	fprintf(stderr,"%s: no files specified\n",argv[0]);
X	exit(1);
X    }
X
X    /*
X     * Loop forever.
X     */
X    for (;;) {
X
X	/*
X	 * Go through each file in the list.
X	 */
X	for ( ifile = 0 ; ifile < Nfiles ; ++ifile ) {
X
X	    /*
X	     * Skip entries removed from list.
X	     */
X	    if ( Flist[ifile].name == NULL )
X		continue;
X
X	    /*
X	     * Stat the file.  Skip it if nothing has changed.
X	     */
X	    if ( stat(Flist[ifile].name,&sbuf) != 0 ) {
X		perror(Flist[ifile].name);
X		Flist[ifile].name = NULL;
X		continue;
X	    }
X	    if ( Flist[ifile].sbuf.st_size == sbuf.st_size )
X		continue;
X
X	    /*
X	     * Open the file.
X	     */
X	    if ( (fd=open(Flist[ifile].name,O_RDONLY)) < 0 ) {
X		perror(Flist[ifile].name);
X		Flist[ifile].name = NULL;
X		continue;
X	    }
X
X	    /*
X	     * Display banner if we are switching files.
X	     */
X	    if ( Lastfile != ifile )
X		printf("\n***** %s *****\n",Flist[ifile].name);
X
X	    /*
X	     * Dump the recently added info.
X	     */
X	    if ( (pos=Flist[ifile].sbuf.st_size) > 0 )
X		(void) lseek(fd,pos,0);
X	    for ( ; pos < sbuf.st_size ; ++pos ) {
X		if ( read(fd,&c,1) != 1 ) {
X		    perror(Flist[ifile].name);
X		    exit(1);
X		}
X		putchar((int)c);
X	    }
X
X	    /*
X	     * Done with file.  Close it and update information.
X	     */
X	    (void) close(fd);
X	    Flist[ifile].sbuf = sbuf;
X	    Lastfile = ifile;
X
X	}				/* next file in list */
X
X	(void) sleep(SLEEPTIME);
X
X    }					/* next iteration */
X
X    /*NOTREACHED*/
X
X}
END_OF_FILE_xtail.c
    size="`wc -c < xtail.c`"
    if test 2778 -ne "$size" ; then
	echo "xtail.c: extraction error - got $size chars"
    fi
fi
if test -f xtail.man -a "$1" != "-c" ; then
    echo "xtail.man: file exists - will not be overwritten"
else
    echo "x - xtail.man (file 2 of 4, 514 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_xtail.man' > xtail.man
X.TH XTAIL 1L
X.SH NAME
Xxtail - Watch the growth of files.
X.SH SYNTAX
X.B xtail
Xfile ...
X.SH DESCRIPTION
X.I Xtail
Xmonitors one or more files, and displays all data written to the file since
Xcommand invocation.  When switching files in the display, a banner showing
Xthe pathname of the file is printed.  This command is most useful for monitoring
Xmultiple logfiles simultaneously.
X.SH SEE ALSO
Xtail(1)
X.SH NOTES
XMy favorite use is "xtail /usr/spool/uucp/.Log/*/*".
X.SH AUTHOR
XChip Rosenthal (chip@vector.Dallas.TX.US)
END_OF_FILE_xtail.man
    size="`wc -c < xtail.man`"
    if test 514 -ne "$size" ; then
	echo "xtail.man: extraction error - got $size chars"
    fi
fi
if test -f Makefile -a "$1" != "-c" ; then
    echo "Makefile: file exists - will not be overwritten"
else
    echo "x - Makefile (file 3 of 4, 1305 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_Makefile' > Makefile
X
X# %Z% %M% %I% %E% %U%
X# Makefile for "xtail" (generated by /local/bin/makemake version 1.00.04)
X# Created by bin@vector on Mon May  1 14:54:56 CDT 1989
X
XSHELL = /bin/sh
XCC = cc
XDEFS = 
XLIBS = 
XDEBUG = #-g -DDEBUG
XCOPTS = -O
XLOPTS = 
XLINTFLAGS = -DLINT
XSHAR1 = 
XSHAR2 = xtail.man Makefile Install
X
XCMD = xtail
X
XSRCS = xtail.c
X
XOBJS = xtail.o
X
X# Any edits below this line will be lost if "makemake" is rerun!
X
XCFLAGS = $(COPTS) $(DEFS) $(DEBUG)
XLFLAGS = $(LOPTS) $(DEBUG)
X
Xall:		$(CMD)
Xinstall:	all		; inst Install
Xclean:				; rm -f $(CMD) $(OBJS) a.out core $(CMD).lint
Xclobber:	clean		; inst -u Install
Xlint:		$(CMD).lint
X
X$(CMD):		$(OBJS)
X		$(CC) $(LFLAGS) -o $@ $(OBJS) $(LIBS)
X
X$(CMD).lint:	$(CMD)
X		lint $(LINTFLAGS) $(DEFS) $(SRCS) $(LIBS) > $@
X
Xxtail.o: /usr/include/stdio.h /usr/include/sys/fcntl.h \
X        /usr/include/sys/lockcmn.h /usr/include/sys/stat.h \
X        /usr/include/sys/types.h xtail.c
X
Xshar:		;
X		shar $(SHAR1) $(SRCS) $(SHAR2) > $(CMD).shar
X
X
Xmake:		;
X		/local/bin/makemake -i -v1.00.04 \
X		    -DSHELL='$(SHELL)' \
X		    -DCC='$(CC)' \
X		    -DDEFS='$(DEFS)' \
X		    -DLIBS='$(LIBS)' \
X		    -DDEBUG='$(DEBUG)' \
X		    -DCOPTS='$(COPTS)' \
X		    -DLOPTS='$(LOPTS)' \
X		    -DLINTFLAGS='$(LINTFLAGS)' \
X		    -DSHAR1='$(SHAR1)' \
X		    -DSHAR2='$(SHAR2)' \
X		    $(CMD) $(SRCS)
END_OF_FILE_Makefile
    size="`wc -c < Makefile`"
    if test 1305 -ne "$size" ; then
	echo "Makefile: extraction error - got $size chars"
    fi
fi
if test -f Install -a "$1" != "-c" ; then
    echo "Install: file exists - will not be overwritten"
else
    echo "x - Install (file 4 of 4, 39 chars)"
    sed -e 's/^X//' << 'END_OF_FILE_Install' > Install
X$B/xtail		xtail
X$M1/xtail.1		xtail.man
END_OF_FILE_Install
    size="`wc -c < Install`"
    if test 39 -ne "$size" ; then
	echo "Install: extraction error - got $size chars"
    fi
fi
echo "done - 4 files extracted"
exit 0
-- 
Chip Rosenthal / chip@vector.Dallas.TX.US / Dallas Semiconductor / 214-450-5337