D 1.5 90/06/24 18:32:22 trent 5 4	00001/00001/00272
MRs:
COMMENTS:
signal int to void

D 1.4 86/06/05 23:06:42 lepreau 4 3	00011/00011/00262
MRs:
COMMENTS:
lint

D 1.3 86/06/05 22:59:31 lepreau 3 2	00167/00101/00106
MRs:
COMMENTS:
handle stdin via temp file.  This code needlessly uses temp file
in case 'tac <file' but who's  going to do that?  Also should stat the temp 
file to determine optimum klsize for write's to it.

D 1.2 86/01/11 00:51:35 lepreau 2 1	00004/00003/00203
MRs:
COMMENTS:
give pike some credit

D 1.1 85/06/09 01:42:35 lepreau 1 0	00206/00000/00000
MRs:
COMMENTS:
date and time created 85/06/09 01:42:35 by lepreau

1.1	#ifndef lint
1.1	static char sccsid[] = "@(#)tac.c	1.5 06/24/90";
1.1	#endif
1.1	
1.1	/*
1.1	 * tac.c - Print file segments in reverse order
1.1	 *
1.1	 * Original line-only version by unknown author off the net.
1.3	 * Rewritten in 1985 by Jay Lepreau, Univ of Utah, to allocate memory
1.3	 * dynamically, handle string bounded segments (suggested by Rob Pike),
1.3	 * and handle pipes.
1.1	 */
1.1	
1.1	#include <sys/types.h>
1.1	#include <sys/stat.h>
1.1	#include <stdio.h>
1.3	#include <signal.h>
1.1	
1.1	/*
1.1	 * This should be defined for BSD releases later than 4.2 and for Sys V.2,
1.1	 * at least.  fwrite is faster than putc only if you have a new speedy fwrite.
1.1	 */
1.1	#define FAST_FWRITE
1.1	
1.1	#ifdef DEBUG				/* dbx can't handle registers */
1.1	#include <ctype.h>
1.1	# define register
1.1	#endif
1.1	
1.1	/* Default target string and bound */
1.1	int right = 1;				/* right or left bounded segments? */
1.1	char *targ = "\n";
1.1	
1.3	char *tfile;
1.3	char *buf;
1.3	
1.1	int readsize = 4096;			/* significantly faster than 1024 */
1.1	int bufsize;
1.1	int targlen;
1.1	int numerr;
1.1	
1.3	int cleanup();
1.1	extern off_t lseek();
1.4	extern char *strcpy(), *malloc(), *realloc(), *mktemp();
1.1	
1.1	main(argc, argv)
1.1	    int argc;
1.1	    char **argv;
1.1	{
1.1	
1.1	#ifdef DEBUG
1.3	    if (argc > 1 && isdigit(*argv[1])) {
1.1		readsize = atoi(argv[1]);
1.1		argc--, argv++;
1.1	    }
1.1	#endif
1.1	    
1.3	    if (argc > 1 && (argv[1][0] == '+' || argv[1][0] == '-') && argv[1][1]) {
1.1		targ = &argv[1][1];
1.1		right = (argv[1][0] == '+');
1.1		argc--; argv++;
1.1	    }
1.1	    targlen = strlen(targ);
1.1	
1.1	    bufsize = (readsize << 1) + targlen + 2;
1.1	    if ((buf = malloc((unsigned) bufsize)) == NULL) {
1.1		perror("tac: initial malloc");
1.1		exit(1);
1.1	    }
1.1	
1.1	    (void) strcpy(buf, targ);		/* stop string at beginning */
1.1	    buf += targlen;
1.1	
1.3	    if (argc == 1)
1.3		tacstdin();
1.1	    while (--argc) {
1.3		if (strcmp(*++argv, "-") == 0)
1.3		    tacstdin();
1.3		else
1.3		    tacit(*argv);
1.3	    }
1.3	    exit(numerr > 0 ? 1 : 0);
1.3	}
1.1	
1.3	tacstdin()
1.3	{
1.1	
1.5	    void (*sigint)(), (*sighup)(), (*sigterm)();
1.3	
1.3	    if ((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
1.4		(void) signal(SIGINT, cleanup);
1.3	    if ((sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
1.4		(void) signal(SIGHUP, cleanup);
1.3	    if ((sigterm = signal(SIGTERM, SIG_IGN)) != SIG_IGN)
1.4		(void) signal(SIGTERM, cleanup);
1.3	
1.3	    savestdin();
1.3	    tacit(tfile);
1.4	    (void) unlink(tfile);
1.3	
1.4	    (void) signal(SIGINT, sigint);
1.4	    (void) signal(SIGHUP, sighup);
1.4	    (void) signal(SIGTERM, sigterm);
1.3	}
1.3	
1.3	char template[] = "/tmp/tacXXXXXX";
1.3	char workplate[sizeof template];
1.3	
1.3	savestdin()
1.3	{
1.3	    int fd;
1.3	    register int n;
1.3	
1.4	    (void) strcpy(workplate, template);
1.3	    tfile = mktemp(workplate);
1.3	    if ((fd = creat(tfile, 0600)) < 0) {
1.3		prterr(tfile);
1.3		cleanup();
1.3	    }
1.3	    while ((n = read(0, buf, readsize)) > 0)
1.3		if (write(fd, buf, n) != n) {
1.3		    prterr(tfile);
1.3		    cleanup();
1.1		}
1.4	    (void) close(fd);
1.3	    if (n < 0) {
1.3		prterr("stdin read");
1.3		cleanup();
1.3	    }
1.3	}
1.1	
1.3	tacit(name)
1.3	    char *name;
1.3	{
1.3	    register char *p, *pastend;
1.3	    register int firstchar, targm1len;	/* target length minus 1 */
1.3	    struct stat st;
1.3	    off_t off;
1.3	    int fd, i;
1.3	
1.3	    firstchar = *targ;
1.3	    targm1len = targlen - 1;
1.3	
1.3	    if (stat(name, &st) < 0) {
1.3		prterr(name);
1.3		numerr++;
1.3		return;
1.3	    }
1.3	    if ((off = st.st_size) == 0)
1.3		return;
1.3	    if ((fd = open(name, 0)) < 0) {
1.3		prterr(name);
1.3		numerr++;
1.3		return;
1.3	    }
1.3	
1.3	    /*
1.3	     * Arrange for the first read to lop off enough to
1.3	     * leave the rest of the file a multiple of readsize.
1.3	     * Since readsize can change, this may not always hold during
1.3	     * the pgm run, but since it usually will, leave it here
1.3	     * for i/o efficiency (page/sector boundaries and all that).
1.3	     * Note: the efficiency gain has not been verified.
1.3	     */
1.3	    if ((i = off % readsize) == 0)
1.3		i = readsize;
1.3	    off -= i;
1.3	
1.3	    (void) lseek(fd, off, 0);
1.3	    if (read(fd, buf, i) != i) {
1.3		prterr(name);
1.3		(void) close(fd);
1.3		numerr++;
1.3		return;
1.3	    }
1.3	    p = pastend = buf + i;		/* pastend always points to end+1 */
1.3	    p -= targm1len;
1.3	
1.3	    for (;;) {
1.3		while ( *--p != firstchar ||
1.3		  (targm1len && strncmp(p+1, targ+1, targm1len)) )
1.3		    continue;
1.3		if (p < buf) {		/* backed off front of buffer */
1.3		    if (off == 0) {
1.3			/* beginning of file: dump last segment */
1.3			output(p + targlen, pastend);
1.3			(void) close(fd);
1.3			break;
1.3		    }
1.3		    if ((i = pastend - buf) > readsize) {
1.3			char *tbuf;
1.3			int newbufsize = (readsize << 2) + targlen + 2;
1.3			
1.3			if ((tbuf = realloc(buf-targlen, (unsigned) newbufsize)) == NULL) {
1.3			    /* If realloc fails, old buf contents may be lost. */
1.3			    perror("tac: segment too long; may have garbage here");
1.1			    numerr++;
1.3			    i = readsize;
1.1			}
1.3			else {
1.3			    tbuf += targlen;	/* skip over the stop string */
1.3			    p += tbuf - buf;
1.3			    pastend += tbuf - buf;
1.3			    buf = tbuf;
1.3			    bufsize = newbufsize;
1.3			    readsize = readsize << 1;
1.3			    /* guaranteed to fit now (I think!) */
1.3			}
1.1		    }
1.3		    if (off - readsize < 0) {
1.3			readsize = off;
1.3			off = 0;
1.3		    }
1.3		    else
1.3			off -= readsize;
1.3		    (void) lseek(fd, off, 0);	/* back up */
1.3		    /* Shift pending old data right to make room for new */
1.3		    bcopy(buf, p = buf + readsize, i);
1.3		    pastend = p + i;
1.3		    if (read(fd, buf, readsize) != readsize) {
1.3			prterr(name);
1.3			numerr++;
1.3			(void) close(fd);
1.3			break;
1.3		    }
1.3		    continue;
1.1		}
1.3		/* Found a real instance of the target string */
1.3		output(right ? p + targlen : p, pastend);
1.3		pastend = p;
1.3		p -= targm1len;
1.1	    }
1.1	}
1.1	
1.1	/*
1.1	 * Dump chars from p to pastend-1.  If right-bounded by target
1.1	 * and not the first time through, append the target string.
1.1	 */
1.1	output(p, pastend)
1.1	    register char *p;
1.1	    char *pastend;
1.1	{
1.1	    static short first = 1;
1.1	
1.1	#ifdef FAST_FWRITE
1.1	    (void) fwrite(p, 1, pastend - p, stdout);
1.1	#else
1.1	    while (p < pastend)
1.1		(void) putc(*p++, stdout);
1.1	#endif
1.1	    if (right && !first)
1.1		(void) fwrite(targ, 1, targlen, stdout);
1.1	    first = 0;
1.1	    if ferror(stdout) {
1.1		perror("tac: fwrite/putc");
1.1		exit(++numerr > 1 ? numerr : 1);
1.1	    }
1.1	}
1.1	
1.1	prterr(s)
1.1	    char *s;
1.1	{
1.1	
1.1	    fprintf(stderr, "tac: ");
1.1	    perror(s);
1.3	}
1.3	
1.3	cleanup()
1.3	{
1.3	
1.4	    (void) unlink(tfile);
1.3	    exit(1);
1.1	}