|
|
1.1 root 1: #ifndef lint
2: static char sccsid[] = "@(#)tac.c 1.4 6/5/86";
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: int (*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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.