xref: /csrg-svn/local/tac/tac.c (revision 29382)
123469Slepreau #ifndef lint
2*29382Slepreau static char sccsid[] = "@(#)tac.c	1.3 06/05/86";
323469Slepreau #endif
423469Slepreau 
523469Slepreau /*
623469Slepreau  * tac.c - Print file segments in reverse order
723469Slepreau  *
823469Slepreau  * Original line-only version by unknown author off the net.
9*29382Slepreau  * Rewritten in 1985 by Jay Lepreau, Univ of Utah, to allocate memory
10*29382Slepreau  * dynamically, handle string bounded segments (suggested by Rob Pike),
11*29382Slepreau  * and handle pipes.
1223469Slepreau  */
1323469Slepreau 
1423469Slepreau #include <sys/types.h>
1523469Slepreau #include <sys/stat.h>
1623469Slepreau #include <stdio.h>
17*29382Slepreau #include <signal.h>
1823469Slepreau 
1923469Slepreau /*
2023469Slepreau  * This should be defined for BSD releases later than 4.2 and for Sys V.2,
2123469Slepreau  * at least.  fwrite is faster than putc only if you have a new speedy fwrite.
2223469Slepreau  */
2323469Slepreau #define FAST_FWRITE
2423469Slepreau 
2523469Slepreau #ifdef DEBUG				/* dbx can't handle registers */
2623469Slepreau #include <ctype.h>
2723469Slepreau # define register
2823469Slepreau #endif
2923469Slepreau 
3023469Slepreau /* Default target string and bound */
3123469Slepreau int right = 1;				/* right or left bounded segments? */
3223469Slepreau char *targ = "\n";
3323469Slepreau 
34*29382Slepreau char *tfile;
35*29382Slepreau char *buf;
36*29382Slepreau 
3723469Slepreau int readsize = 4096;			/* significantly faster than 1024 */
3823469Slepreau int bufsize;
3923469Slepreau int targlen;
4023469Slepreau int numerr;
4123469Slepreau 
42*29382Slepreau int cleanup();
4323469Slepreau extern off_t lseek();
44*29382Slepreau extern char *malloc(), *realloc(), *mktemp();
4523469Slepreau 
4623469Slepreau main(argc, argv)
4723469Slepreau     int argc;
4823469Slepreau     char **argv;
4923469Slepreau {
5023469Slepreau 
5123469Slepreau #ifdef DEBUG
52*29382Slepreau     if (argc > 1 && isdigit(*argv[1])) {
5323469Slepreau 	readsize = atoi(argv[1]);
5423469Slepreau 	argc--, argv++;
5523469Slepreau     }
5623469Slepreau #endif
5723469Slepreau 
58*29382Slepreau     if (argc > 1 && (argv[1][0] == '+' || argv[1][0] == '-') && argv[1][1]) {
5923469Slepreau 	targ = &argv[1][1];
6023469Slepreau 	right = (argv[1][0] == '+');
6123469Slepreau 	argc--; argv++;
6223469Slepreau     }
6323469Slepreau     targlen = strlen(targ);
6423469Slepreau 
6523469Slepreau     bufsize = (readsize << 1) + targlen + 2;
6623469Slepreau     if ((buf = malloc((unsigned) bufsize)) == NULL) {
6723469Slepreau 	perror("tac: initial malloc");
6823469Slepreau 	exit(1);
6923469Slepreau     }
7023469Slepreau 
7123469Slepreau     (void) strcpy(buf, targ);		/* stop string at beginning */
7223469Slepreau     buf += targlen;
7323469Slepreau 
74*29382Slepreau     if (argc == 1)
75*29382Slepreau 	tacstdin();
7623469Slepreau     while (--argc) {
77*29382Slepreau 	if (strcmp(*++argv, "-") == 0)
78*29382Slepreau 	    tacstdin();
79*29382Slepreau 	else
80*29382Slepreau 	    tacit(*argv);
81*29382Slepreau     }
82*29382Slepreau     exit(numerr > 0 ? 1 : 0);
83*29382Slepreau }
84*29382Slepreau 
85*29382Slepreau tacstdin()
86*29382Slepreau {
87*29382Slepreau 
88*29382Slepreau     int (*sigint)(), (*sighup)(), (*sigterm)();
89*29382Slepreau 
90*29382Slepreau     if ((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
91*29382Slepreau 	signal(SIGINT, cleanup);
92*29382Slepreau     if ((sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
93*29382Slepreau 	signal(SIGHUP, cleanup);
94*29382Slepreau     if ((sigterm = signal(SIGTERM, SIG_IGN)) != SIG_IGN)
95*29382Slepreau 	signal(SIGTERM, cleanup);
96*29382Slepreau 
97*29382Slepreau     savestdin();
98*29382Slepreau     tacit(tfile);
99*29382Slepreau     unlink(tfile);
100*29382Slepreau 
101*29382Slepreau     signal(SIGINT, sigint);
102*29382Slepreau     signal(SIGHUP, sighup);
103*29382Slepreau     signal(SIGTERM, sigterm);
104*29382Slepreau }
105*29382Slepreau 
106*29382Slepreau char template[] = "/tmp/tacXXXXXX";
107*29382Slepreau char workplate[sizeof template];
108*29382Slepreau 
109*29382Slepreau savestdin()
110*29382Slepreau {
111*29382Slepreau     int fd;
112*29382Slepreau     register int n;
113*29382Slepreau 
114*29382Slepreau     strcpy(workplate, template);
115*29382Slepreau     tfile = mktemp(workplate);
116*29382Slepreau     if ((fd = creat(tfile, 0600)) < 0) {
117*29382Slepreau 	prterr(tfile);
118*29382Slepreau 	cleanup();
119*29382Slepreau     }
120*29382Slepreau     while ((n = read(0, buf, readsize)) > 0)
121*29382Slepreau 	if (write(fd, buf, n) != n) {
122*29382Slepreau 	    prterr(tfile);
123*29382Slepreau 	    cleanup();
12423469Slepreau 	}
125*29382Slepreau     close(fd);
126*29382Slepreau     if (n < 0) {
127*29382Slepreau 	prterr("stdin read");
128*29382Slepreau 	cleanup();
129*29382Slepreau     }
130*29382Slepreau }
13123469Slepreau 
132*29382Slepreau tacit(name)
133*29382Slepreau     char *name;
134*29382Slepreau {
135*29382Slepreau     register char *p, *pastend;
136*29382Slepreau     register int firstchar, targm1len;	/* target length minus 1 */
137*29382Slepreau     struct stat st;
138*29382Slepreau     off_t off;
139*29382Slepreau     int fd, i;
14023469Slepreau 
141*29382Slepreau     firstchar = *targ;
142*29382Slepreau     targm1len = targlen - 1;
143*29382Slepreau 
144*29382Slepreau     if (stat(name, &st) < 0) {
145*29382Slepreau 	prterr(name);
146*29382Slepreau 	numerr++;
147*29382Slepreau 	return;
148*29382Slepreau     }
149*29382Slepreau     if ((off = st.st_size) == 0)
150*29382Slepreau 	return;
151*29382Slepreau     if ((fd = open(name, 0)) < 0) {
152*29382Slepreau 	prterr(name);
153*29382Slepreau 	numerr++;
154*29382Slepreau 	return;
155*29382Slepreau     }
156*29382Slepreau 
157*29382Slepreau     /*
158*29382Slepreau      * Arrange for the first read to lop off enough to
159*29382Slepreau      * leave the rest of the file a multiple of readsize.
160*29382Slepreau      * Since readsize can change, this may not always hold during
161*29382Slepreau      * the pgm run, but since it usually will, leave it here
162*29382Slepreau      * for i/o efficiency (page/sector boundaries and all that).
163*29382Slepreau      * Note: the efficiency gain has not been verified.
164*29382Slepreau      */
165*29382Slepreau     if ((i = off % readsize) == 0)
166*29382Slepreau 	i = readsize;
167*29382Slepreau     off -= i;
168*29382Slepreau 
169*29382Slepreau     (void) lseek(fd, off, 0);
170*29382Slepreau     if (read(fd, buf, i) != i) {
171*29382Slepreau 	prterr(name);
172*29382Slepreau 	(void) close(fd);
173*29382Slepreau 	numerr++;
174*29382Slepreau 	return;
175*29382Slepreau     }
176*29382Slepreau     p = pastend = buf + i;		/* pastend always points to end+1 */
177*29382Slepreau     p -= targm1len;
178*29382Slepreau 
179*29382Slepreau     for (;;) {
180*29382Slepreau 	while ( *--p != firstchar ||
181*29382Slepreau 	  (targm1len && strncmp(p+1, targ+1, targm1len)) )
18223469Slepreau 	    continue;
183*29382Slepreau 	if (p < buf) {		/* backed off front of buffer */
184*29382Slepreau 	    if (off == 0) {
185*29382Slepreau 		/* beginning of file: dump last segment */
186*29382Slepreau 		output(p + targlen, pastend);
187*29382Slepreau 		(void) close(fd);
188*29382Slepreau 		break;
189*29382Slepreau 	    }
190*29382Slepreau 	    if ((i = pastend - buf) > readsize) {
191*29382Slepreau 		char *tbuf;
192*29382Slepreau 		int newbufsize = (readsize << 2) + targlen + 2;
193*29382Slepreau 
194*29382Slepreau 		if ((tbuf = realloc(buf-targlen, (unsigned) newbufsize)) == NULL) {
195*29382Slepreau 		    /* If realloc fails, old buf contents may be lost. */
196*29382Slepreau 		    perror("tac: segment too long; may have garbage here");
197*29382Slepreau 		    numerr++;
198*29382Slepreau 		    i = readsize;
19923469Slepreau 		}
200*29382Slepreau 		else {
201*29382Slepreau 		    tbuf += targlen;	/* skip over the stop string */
202*29382Slepreau 		    p += tbuf - buf;
203*29382Slepreau 		    pastend += tbuf - buf;
204*29382Slepreau 		    buf = tbuf;
205*29382Slepreau 		    bufsize = newbufsize;
206*29382Slepreau 		    readsize = readsize << 1;
207*29382Slepreau 		    /* guaranteed to fit now (I think!) */
20823469Slepreau 		}
20923469Slepreau 	    }
210*29382Slepreau 	    if (off - readsize < 0) {
211*29382Slepreau 		readsize = off;
212*29382Slepreau 		off = 0;
213*29382Slepreau 	    }
214*29382Slepreau 	    else
215*29382Slepreau 		off -= readsize;
216*29382Slepreau 	    (void) lseek(fd, off, 0);	/* back up */
217*29382Slepreau 	    /* Shift pending old data right to make room for new */
218*29382Slepreau 	    bcopy(buf, p = buf + readsize, i);
219*29382Slepreau 	    pastend = p + i;
220*29382Slepreau 	    if (read(fd, buf, readsize) != readsize) {
221*29382Slepreau 		prterr(name);
222*29382Slepreau 		numerr++;
223*29382Slepreau 		(void) close(fd);
224*29382Slepreau 		break;
225*29382Slepreau 	    }
226*29382Slepreau 	    continue;
22723469Slepreau 	}
228*29382Slepreau 	/* Found a real instance of the target string */
229*29382Slepreau 	output(right ? p + targlen : p, pastend);
230*29382Slepreau 	pastend = p;
231*29382Slepreau 	p -= targm1len;
23223469Slepreau     }
23323469Slepreau }
23423469Slepreau 
23523469Slepreau /*
23623469Slepreau  * Dump chars from p to pastend-1.  If right-bounded by target
23723469Slepreau  * and not the first time through, append the target string.
23823469Slepreau  */
23923469Slepreau output(p, pastend)
24023469Slepreau     register char *p;
24123469Slepreau     char *pastend;
24223469Slepreau {
24323469Slepreau     static short first = 1;
24423469Slepreau 
24523469Slepreau #ifdef FAST_FWRITE
24623469Slepreau     (void) fwrite(p, 1, pastend - p, stdout);
24723469Slepreau #else
24823469Slepreau     while (p < pastend)
24923469Slepreau 	(void) putc(*p++, stdout);
25023469Slepreau #endif
25123469Slepreau     if (right && !first)
25223469Slepreau 	(void) fwrite(targ, 1, targlen, stdout);
25323469Slepreau     first = 0;
25423469Slepreau     if ferror(stdout) {
25523469Slepreau 	perror("tac: fwrite/putc");
25623469Slepreau 	exit(++numerr > 1 ? numerr : 1);
25723469Slepreau     }
25823469Slepreau }
25923469Slepreau 
26023469Slepreau prterr(s)
26123469Slepreau     char *s;
26223469Slepreau {
26323469Slepreau 
26423469Slepreau     fprintf(stderr, "tac: ");
26523469Slepreau     perror(s);
26623469Slepreau }
267*29382Slepreau 
268*29382Slepreau cleanup()
269*29382Slepreau {
270*29382Slepreau 
271*29382Slepreau     unlink(tfile);
272*29382Slepreau     exit(1);
273*29382Slepreau }
274