Commit | Line | Data |
---|---|---|
b5a5af67 JL |
1 | #ifndef lint |
2 | static 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 */ | |
29 | int right = 1; /* right or left bounded segments? */ | |
30 | char *targ = "\n"; | |
31 | ||
32 | int readsize = 4096; /* significantly faster than 1024 */ | |
33 | int bufsize; | |
34 | int targlen; | |
35 | int numerr; | |
36 | ||
37 | extern off_t lseek(); | |
38 | extern char *malloc(), *realloc(); | |
39 | ||
40 | main(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 | */ | |
179 | output(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 | ||
200 | prterr(s) | |
201 | char *s; | |
202 | { | |
203 | ||
204 | fprintf(stderr, "tac: "); | |
205 | perror(s); | |
206 | } |