have mailertable look for partial domains as well as full domains
[unix-history] / usr / src / local / tac / tac.c
CommitLineData
b5a5af67 1#ifndef lint
537684ad 2static 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 */
31int right = 1; /* right or left bounded segments? */
32char *targ = "\n";
33
ffcf70e9
JL
34char *tfile;
35char *buf;
36
b5a5af67
JL
37int readsize = 4096; /* significantly faster than 1024 */
38int bufsize;
39int targlen;
40int numerr;
41
ffcf70e9 42int cleanup();
b5a5af67 43extern off_t lseek();
10347d58 44extern char *strcpy(), *malloc(), *realloc(), *mktemp();
b5a5af67
JL
45
46main(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
85tacstdin()
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
106char template[] = "/tmp/tacXXXXXX";
107char workplate[sizeof template];
108
109savestdin()
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
132tacit(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 */
239output(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
260prterr(s)
261 char *s;
262{
263
264 fprintf(stderr, "tac: ");
265 perror(s);
266}
ffcf70e9
JL
267
268cleanup()
269{
270
10347d58 271 (void) unlink(tfile);
ffcf70e9
JL
272 exit(1);
273}