xref: /original-bsd/local/tac/tac.c (revision e61fc7ea)
1 #ifndef lint
2 static char sccsid[] = "@(#)tac.c	1.5 06/24/90";
3 #endif
4 
5 /*
6  * tac.c - Print file segments in reverse order
7  *
8  * Original line-only version by unknown author off the net.
9  * Rewritten in 1985 by Jay Lepreau, Univ of Utah, to allocate memory
10  * dynamically, handle string bounded segments (suggested by Rob Pike),
11  * and handle pipes.
12  */
13 
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <stdio.h>
17 #include <signal.h>
18 
19 /*
20  * This should be defined for BSD releases later than 4.2 and for Sys V.2,
21  * at least.  fwrite is faster than putc only if you have a new speedy fwrite.
22  */
23 #define FAST_FWRITE
24 
25 #ifdef DEBUG				/* dbx can't handle registers */
26 #include <ctype.h>
27 # define register
28 #endif
29 
30 /* Default target string and bound */
31 int right = 1;				/* right or left bounded segments? */
32 char *targ = "\n";
33 
34 char *tfile;
35 char *buf;
36 
37 int readsize = 4096;			/* significantly faster than 1024 */
38 int bufsize;
39 int targlen;
40 int numerr;
41 
42 int cleanup();
43 extern off_t lseek();
44 extern char *strcpy(), *malloc(), *realloc(), *mktemp();
45 
46 main(argc, argv)
47     int argc;
48     char **argv;
49 {
50 
51 #ifdef DEBUG
52     if (argc > 1 && isdigit(*argv[1])) {
53 	readsize = atoi(argv[1]);
54 	argc--, argv++;
55     }
56 #endif
57 
58     if (argc > 1 && (argv[1][0] == '+' || argv[1][0] == '-') && argv[1][1]) {
59 	targ = &argv[1][1];
60 	right = (argv[1][0] == '+');
61 	argc--; argv++;
62     }
63     targlen = strlen(targ);
64 
65     bufsize = (readsize << 1) + targlen + 2;
66     if ((buf = malloc((unsigned) bufsize)) == NULL) {
67 	perror("tac: initial malloc");
68 	exit(1);
69     }
70 
71     (void) strcpy(buf, targ);		/* stop string at beginning */
72     buf += targlen;
73 
74     if (argc == 1)
75 	tacstdin();
76     while (--argc) {
77 	if (strcmp(*++argv, "-") == 0)
78 	    tacstdin();
79 	else
80 	    tacit(*argv);
81     }
82     exit(numerr > 0 ? 1 : 0);
83 }
84 
85 tacstdin()
86 {
87 
88     void (*sigint)(), (*sighup)(), (*sigterm)();
89 
90     if ((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
91 	(void) signal(SIGINT, cleanup);
92     if ((sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
93 	(void) signal(SIGHUP, cleanup);
94     if ((sigterm = signal(SIGTERM, SIG_IGN)) != SIG_IGN)
95 	(void) signal(SIGTERM, cleanup);
96 
97     savestdin();
98     tacit(tfile);
99     (void) unlink(tfile);
100 
101     (void) signal(SIGINT, sigint);
102     (void) signal(SIGHUP, sighup);
103     (void) signal(SIGTERM, sigterm);
104 }
105 
106 char template[] = "/tmp/tacXXXXXX";
107 char workplate[sizeof template];
108 
109 savestdin()
110 {
111     int fd;
112     register int n;
113 
114     (void) strcpy(workplate, template);
115     tfile = mktemp(workplate);
116     if ((fd = creat(tfile, 0600)) < 0) {
117 	prterr(tfile);
118 	cleanup();
119     }
120     while ((n = read(0, buf, readsize)) > 0)
121 	if (write(fd, buf, n) != n) {
122 	    prterr(tfile);
123 	    cleanup();
124 	}
125     (void) close(fd);
126     if (n < 0) {
127 	prterr("stdin read");
128 	cleanup();
129     }
130 }
131 
132 tacit(name)
133     char *name;
134 {
135     register char *p, *pastend;
136     register int firstchar, targm1len;	/* target length minus 1 */
137     struct stat st;
138     off_t off;
139     int fd, i;
140 
141     firstchar = *targ;
142     targm1len = targlen - 1;
143 
144     if (stat(name, &st) < 0) {
145 	prterr(name);
146 	numerr++;
147 	return;
148     }
149     if ((off = st.st_size) == 0)
150 	return;
151     if ((fd = open(name, 0)) < 0) {
152 	prterr(name);
153 	numerr++;
154 	return;
155     }
156 
157     /*
158      * Arrange for the first read to lop off enough to
159      * leave the rest of the file a multiple of readsize.
160      * Since readsize can change, this may not always hold during
161      * the pgm run, but since it usually will, leave it here
162      * for i/o efficiency (page/sector boundaries and all that).
163      * Note: the efficiency gain has not been verified.
164      */
165     if ((i = off % readsize) == 0)
166 	i = readsize;
167     off -= i;
168 
169     (void) lseek(fd, off, 0);
170     if (read(fd, buf, i) != i) {
171 	prterr(name);
172 	(void) close(fd);
173 	numerr++;
174 	return;
175     }
176     p = pastend = buf + i;		/* pastend always points to end+1 */
177     p -= targm1len;
178 
179     for (;;) {
180 	while ( *--p != firstchar ||
181 	  (targm1len && strncmp(p+1, targ+1, targm1len)) )
182 	    continue;
183 	if (p < buf) {		/* backed off front of buffer */
184 	    if (off == 0) {
185 		/* beginning of file: dump last segment */
186 		output(p + targlen, pastend);
187 		(void) close(fd);
188 		break;
189 	    }
190 	    if ((i = pastend - buf) > readsize) {
191 		char *tbuf;
192 		int newbufsize = (readsize << 2) + targlen + 2;
193 
194 		if ((tbuf = realloc(buf-targlen, (unsigned) newbufsize)) == NULL) {
195 		    /* If realloc fails, old buf contents may be lost. */
196 		    perror("tac: segment too long; may have garbage here");
197 		    numerr++;
198 		    i = readsize;
199 		}
200 		else {
201 		    tbuf += targlen;	/* skip over the stop string */
202 		    p += tbuf - buf;
203 		    pastend += tbuf - buf;
204 		    buf = tbuf;
205 		    bufsize = newbufsize;
206 		    readsize = readsize << 1;
207 		    /* guaranteed to fit now (I think!) */
208 		}
209 	    }
210 	    if (off - readsize < 0) {
211 		readsize = off;
212 		off = 0;
213 	    }
214 	    else
215 		off -= readsize;
216 	    (void) lseek(fd, off, 0);	/* back up */
217 	    /* Shift pending old data right to make room for new */
218 	    bcopy(buf, p = buf + readsize, i);
219 	    pastend = p + i;
220 	    if (read(fd, buf, readsize) != readsize) {
221 		prterr(name);
222 		numerr++;
223 		(void) close(fd);
224 		break;
225 	    }
226 	    continue;
227 	}
228 	/* Found a real instance of the target string */
229 	output(right ? p + targlen : p, pastend);
230 	pastend = p;
231 	p -= targm1len;
232     }
233 }
234 
235 /*
236  * Dump chars from p to pastend-1.  If right-bounded by target
237  * and not the first time through, append the target string.
238  */
239 output(p, pastend)
240     register char *p;
241     char *pastend;
242 {
243     static short first = 1;
244 
245 #ifdef FAST_FWRITE
246     (void) fwrite(p, 1, pastend - p, stdout);
247 #else
248     while (p < pastend)
249 	(void) putc(*p++, stdout);
250 #endif
251     if (right && !first)
252 	(void) fwrite(targ, 1, targlen, stdout);
253     first = 0;
254     if ferror(stdout) {
255 	perror("tac: fwrite/putc");
256 	exit(++numerr > 1 ? numerr : 1);
257     }
258 }
259 
260 prterr(s)
261     char *s;
262 {
263 
264     fprintf(stderr, "tac: ");
265     perror(s);
266 }
267 
268 cleanup()
269 {
270 
271     (void) unlink(tfile);
272     exit(1);
273 }
274