Commit | Line | Data |
---|---|---|
b5a5af67 | 1 | #ifndef lint |
537684ad | 2 | static char sccsid[] = "@(#)tac.c 1.5 %G%"; |
b5a5af67 JL |
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. | |
ffcf70e9 JL |
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. | |
b5a5af67 JL |
12 | */ |
13 | ||
14 | #include <sys/types.h> | |
15 | #include <sys/stat.h> | |
16 | #include <stdio.h> | |
ffcf70e9 | 17 | #include <signal.h> |
b5a5af67 JL |
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 | ||
ffcf70e9 JL |
34 | char *tfile; |
35 | char *buf; | |
36 | ||
b5a5af67 JL |
37 | int readsize = 4096; /* significantly faster than 1024 */ |
38 | int bufsize; | |
39 | int targlen; | |
40 | int numerr; | |
41 | ||
ffcf70e9 | 42 | int cleanup(); |
b5a5af67 | 43 | extern off_t lseek(); |
10347d58 | 44 | extern char *strcpy(), *malloc(), *realloc(), *mktemp(); |
b5a5af67 JL |
45 | |
46 | main(argc, argv) | |
47 | int argc; | |
48 | char **argv; | |
49 | { | |
b5a5af67 JL |
50 | |
51 | #ifdef DEBUG | |
ffcf70e9 | 52 | if (argc > 1 && isdigit(*argv[1])) { |
b5a5af67 JL |
53 | readsize = atoi(argv[1]); |
54 | argc--, argv++; | |
55 | } | |
56 | #endif | |
57 | ||
ffcf70e9 | 58 | if (argc > 1 && (argv[1][0] == '+' || argv[1][0] == '-') && argv[1][1]) { |
b5a5af67 JL |
59 | targ = &argv[1][1]; |
60 | right = (argv[1][0] == '+'); | |
61 | argc--; argv++; | |
62 | } | |
b5a5af67 | 63 | targlen = strlen(targ); |
b5a5af67 JL |
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 | ||
ffcf70e9 JL |
74 | if (argc == 1) |
75 | tacstdin(); | |
b5a5af67 | 76 | while (--argc) { |
ffcf70e9 JL |
77 | if (strcmp(*++argv, "-") == 0) |
78 | tacstdin(); | |
79 | else | |
80 | tacit(*argv); | |
81 | } | |
82 | exit(numerr > 0 ? 1 : 0); | |
83 | } | |
b5a5af67 | 84 | |
ffcf70e9 JL |
85 | tacstdin() |
86 | { | |
87 | ||
537684ad | 88 | void (*sigint)(), (*sighup)(), (*sigterm)(); |
ffcf70e9 JL |
89 | |
90 | if ((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN) | |
10347d58 | 91 | (void) signal(SIGINT, cleanup); |
ffcf70e9 | 92 | if ((sighup = signal(SIGHUP, SIG_IGN)) != SIG_IGN) |
10347d58 | 93 | (void) signal(SIGHUP, cleanup); |
ffcf70e9 | 94 | if ((sigterm = signal(SIGTERM, SIG_IGN)) != SIG_IGN) |
10347d58 | 95 | (void) signal(SIGTERM, cleanup); |
ffcf70e9 JL |
96 | |
97 | savestdin(); | |
98 | tacit(tfile); | |
10347d58 | 99 | (void) unlink(tfile); |
ffcf70e9 | 100 | |
10347d58 JL |
101 | (void) signal(SIGINT, sigint); |
102 | (void) signal(SIGHUP, sighup); | |
103 | (void) signal(SIGTERM, sigterm); | |
ffcf70e9 JL |
104 | } |
105 | ||
106 | char template[] = "/tmp/tacXXXXXX"; | |
107 | char workplate[sizeof template]; | |
108 | ||
109 | savestdin() | |
110 | { | |
111 | int fd; | |
112 | register int n; | |
113 | ||
10347d58 | 114 | (void) strcpy(workplate, template); |
ffcf70e9 JL |
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(); | |
b5a5af67 | 124 | } |
10347d58 | 125 | (void) close(fd); |
ffcf70e9 JL |
126 | if (n < 0) { |
127 | prterr("stdin read"); | |
128 | cleanup(); | |
129 | } | |
130 | } | |
b5a5af67 | 131 | |
ffcf70e9 JL |
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"); | |
b5a5af67 | 197 | numerr++; |
ffcf70e9 | 198 | i = readsize; |
b5a5af67 | 199 | } |
ffcf70e9 JL |
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; | |
b5a5af67 | 213 | } |
ffcf70e9 JL |
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; | |
b5a5af67 | 227 | } |
ffcf70e9 JL |
228 | /* Found a real instance of the target string */ |
229 | output(right ? p + targlen : p, pastend); | |
230 | pastend = p; | |
231 | p -= targm1len; | |
b5a5af67 | 232 | } |
b5a5af67 JL |
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 | } | |
ffcf70e9 JL |
267 | |
268 | cleanup() | |
269 | { | |
270 | ||
10347d58 | 271 | (void) unlink(tfile); |
ffcf70e9 JL |
272 | exit(1); |
273 | } |