date and time created 85/06/09 01:42:35 by lepreau
[unix-history] / usr / src / local / tac / tac.c
CommitLineData
b5a5af67
JL
1#ifndef lint
2static char sccsid[] = "@(#)tac.c 1.1 %G%";
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 * 1985 mods by Jay Lepreau, Univ of Utah, to allocate memory dynamically
10 * and handle string bounded segments.
11 */
12
13#include <sys/types.h>
14#include <sys/stat.h>
15#include <stdio.h>
16
17/*
18 * This should be defined for BSD releases later than 4.2 and for Sys V.2,
19 * at least. fwrite is faster than putc only if you have a new speedy fwrite.
20 */
21#define FAST_FWRITE
22
23#ifdef DEBUG /* dbx can't handle registers */
24#include <ctype.h>
25# define register
26#endif
27
28/* Default target string and bound */
29int right = 1; /* right or left bounded segments? */
30char *targ = "\n";
31
32int readsize = 4096; /* significantly faster than 1024 */
33int bufsize;
34int targlen;
35int numerr;
36
37extern off_t lseek();
38extern char *malloc(), *realloc();
39
40main(argc, argv)
41 int argc;
42 char **argv;
43{
44 register char *p, *pastend;
45 register int firstchar, targm1len; /* target length minus 1 */
46 int fd, i;
47 off_t off;
48 char *buf;
49 struct stat st;
50
51 if (argc < 2) {
52 (void) fprintf(stderr, "Usage: tac [-string | +string] file ...\n");
53 exit(1);
54 }
55
56#ifdef DEBUG
57 if (isdigit(*argv[1])) {
58 readsize = atoi(argv[1]);
59 argc--, argv++;
60 }
61#endif
62
63 if (argv[1][0] == '+' || argv[1][0] == '-') {
64 targ = &argv[1][1];
65 right = (argv[1][0] == '+');
66 argc--; argv++;
67 }
68 firstchar = *targ;
69 targlen = strlen(targ);
70 targm1len = targlen - 1;
71
72 bufsize = (readsize << 1) + targlen + 2;
73 if ((buf = malloc((unsigned) bufsize)) == NULL) {
74 perror("tac: initial malloc");
75 exit(1);
76 }
77
78 (void) strcpy(buf, targ); /* stop string at beginning */
79 buf += targlen;
80
81 while (--argc) {
82 if (stat(p = *++argv, &st) < 0) {
83 prterr(p);
84 numerr++;
85 continue;
86 }
87 if ((off = st.st_size) == 0)
88 continue;
89 if ((fd = open(p, 0)) < 0) {
90 prterr(p);
91 numerr++;
92 continue;
93 }
94
95 /*
96 * Arrange for the first read to lop off enough to
97 * leave the rest of the file a multiple of readsize.
98 * Since readsize can change, this may not always hold during
99 * the pgm run, but since it usually will, leave it here
100 * for i/o efficiency (page/sector boundaries and all that).
101 * Note: Above statement has never been verified!
102 */
103 if ((i = off % readsize) == 0)
104 i = readsize;
105 off -= i;
106
107 (void) lseek(fd, off, 0);
108 if (read(fd, buf, i) != i) {
109 prterr(p);
110 (void) close(fd);
111 numerr++;
112 continue;
113 }
114 p = pastend = buf + i; /* pastend always points to end+1 */
115 p -= targm1len;
116
117 for (;;) {
118 while ( *--p != firstchar ||
119 (targm1len && strncmp(p+1, targ+1, targm1len)) )
120 continue;
121 if (p < buf) { /* backed off front of buffer */
122 if (off == 0) {
123 /* beginning of file: dump last segment */
124 output(p + targlen, pastend);
125 (void) close(fd);
126 break;
127 }
128 if ((i = pastend - buf) > readsize) {
129 char *tbuf;
130 int newbufsize = (readsize << 2) + targlen + 2;
131
132 if ((tbuf = realloc(buf-targlen, (unsigned) newbufsize)) == NULL) {
133 /* If realloc fails, old buf contents may be lost. */
134 perror("tac: segment too long; may have garbage here");
135 numerr++;
136 i = readsize;
137 }
138 else {
139 tbuf += targlen; /* skip over the stop string */
140 p += tbuf - buf;
141 pastend += tbuf - buf;
142 buf = tbuf;
143 bufsize = newbufsize;
144 readsize = readsize << 1;
145 /* guaranteed to fit now (I think!) */
146 }
147 }
148 if (off - readsize < 0) {
149 readsize = off;
150 off = 0;
151 }
152 else
153 off -= readsize;
154 (void) lseek(fd, off, 0); /* back up */
155 /* Shift pending old data right to make room for new */
156 bcopy(buf, p = buf + readsize, i);
157 pastend = p + i;
158 if (read(fd, buf, readsize) != readsize) {
159 prterr(*argv);
160 numerr++;
161 (void) close(fd);
162 break;
163 }
164 continue;
165 }
166 /* Found a real instance of the target string */
167 output(right ? p + targlen : p, pastend);
168 pastend = p;
169 p -= targm1len;
170 }
171 }
172 exit(numerr);
173}
174
175/*
176 * Dump chars from p to pastend-1. If right-bounded by target
177 * and not the first time through, append the target string.
178 */
179output(p, pastend)
180 register char *p;
181 char *pastend;
182{
183 static short first = 1;
184
185#ifdef FAST_FWRITE
186 (void) fwrite(p, 1, pastend - p, stdout);
187#else
188 while (p < pastend)
189 (void) putc(*p++, stdout);
190#endif
191 if (right && !first)
192 (void) fwrite(targ, 1, targlen, stdout);
193 first = 0;
194 if ferror(stdout) {
195 perror("tac: fwrite/putc");
196 exit(++numerr > 1 ? numerr : 1);
197 }
198}
199
200prterr(s)
201 char *s;
202{
203
204 fprintf(stderr, "tac: ");
205 perror(s);
206}