blob: db29986d732cf4a9e371f12fc3500108f53ba53a [file] [log] [blame]
rscbc7cb1a2003-11-23 18:04:47 +00001#include <u.h>
2#include <libc.h>
3#include <ctype.h>
4#include <bio.h>
5
6/*
7 * tail command, posix plus v10 option -r.
8 * the simple command tail -c, legal in v10, is illegal
9 */
10
rscb707cb52004-06-13 20:08:00 +000011vlong count;
rscbc7cb1a2003-11-23 18:04:47 +000012int anycount;
13int follow;
14int file = 0;
15char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
16
17Biobuf bout;
18enum
19{
20 BEG,
21 END
22} origin = END;
23enum
24{
25 CHARS,
26 LINES
27} units = LINES;
28enum
29{
30 FWD,
31 REV
32} dir = FWD;
33
34extern void copy(void);
35extern void fatal(char*);
36extern int getnumber(char*);
37extern void keep(void);
38extern void reverse(void);
39extern void skip(void);
40extern void suffix(char*);
41extern long tread(char*, long);
rsccedbe4a2003-11-24 00:59:36 +000042#define trunc tailtrunc
rscbc7cb1a2003-11-23 18:04:47 +000043extern void trunc(Dir*, Dir**);
rscb707cb52004-06-13 20:08:00 +000044extern vlong tseek(vlong, int);
rscbc7cb1a2003-11-23 18:04:47 +000045extern void twrite(char*, long);
46extern void usage(void);
47
48#define JUMP(o,p) tseek(o,p), copy()
49
50void
51main(int argc, char **argv)
52{
53 int seekable, c;
54
55 Binit(&bout, 1, OWRITE);
56 for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
57 if(getnumber(argv[1])) {
58 suffix(argv[1]);
59 continue;
60 } else
61 if(c == '-')
62 switch(argv[1][1]) {
63 case 'c':
64 units = CHARS;
65 case 'n':
66 if(getnumber(argv[1]+2))
67 continue;
68 else
69 if(argc > 2 && getnumber(argv[2])) {
70 argc--, argv++;
71 continue;
72 } else
73 usage();
74 case 'r':
75 dir = REV;
76 continue;
77 case 'f':
78 follow++;
79 continue;
80 case '-':
81 argc--, argv++;
82 }
83 break;
84 }
85 if(dir==REV && (units==CHARS || follow || origin==BEG))
86 fatal("incompatible options");
87 if(!anycount)
rscb707cb52004-06-13 20:08:00 +000088 count = dir==REV? ~0ULL>>1: 10;
rscbc7cb1a2003-11-23 18:04:47 +000089 if(origin==BEG && units==LINES && count>0)
90 count--;
91 if(argc > 2)
92 usage();
93 if(argc > 1 && (file=open(argv[1],0)) < 0)
94 fatal(argv[1]);
95 seekable = seek(file,0L,0) == 0;
96
97 if(!seekable && origin==END)
98 keep();
99 else
100 if(!seekable && origin==BEG)
101 skip();
102 else
103 if(units==CHARS && origin==END)
104 JUMP(-count, 2);
105 else
106 if(units==CHARS && origin==BEG)
107 JUMP(count, 0);
108 else
109 if(units==LINES && origin==END)
110 reverse();
111 else
112 if(units==LINES && origin==BEG)
113 skip();
114 if(follow && seekable)
115 for(;;) {
116 static Dir *sb0, *sb1;
117 trunc(sb1, &sb0);
118 copy();
119 trunc(sb0, &sb1);
120 sleep(5000);
121 }
122 exits(0);
123}
124
125void
126trunc(Dir *old, Dir **new)
127{
128 Dir *d;
rscb707cb52004-06-13 20:08:00 +0000129 vlong olength;
rscbc7cb1a2003-11-23 18:04:47 +0000130
131 d = dirfstat(file);
132 if(d == nil)
133 return;
134 olength = 0;
135 if(old)
136 olength = old->length;
137 if(d->length < olength)
138 d->length = tseek(0L, 0);
139 free(*new);
140 *new = d;
141}
142
143void
144suffix(char *s)
145{
146 while(*s && strchr("0123456789+-", *s))
147 s++;
148 switch(*s) {
149 case 'b':
150 if((count *= 1024) < 0)
151 fatal("too big");
152 case 'c':
153 units = CHARS;
154 case 'l':
155 s++;
156 }
157 switch(*s) {
158 case 'r':
159 dir = REV;
160 return;
161 case 'f':
162 follow++;
163 return;
164 case 0:
165 return;
166 }
167 usage();
168}
169
170/*
171 * read past head of the file to find tail
172 */
173void
174skip(void)
175{
176 int i;
177 long n;
178 char buf[Bsize];
179 if(units == CHARS) {
180 for( ; count>0; count -=n) {
181 n = count<Bsize? count: Bsize;
182 if(!(n = tread(buf, n)))
183 return;
184 }
185 } else /*units == LINES*/ {
186 n = i = 0;
187 while(count > 0) {
188 if(!(n = tread(buf, Bsize)))
189 return;
190 for(i=0; i<n && count>0; i++)
191 if(buf[i]=='\n')
192 count--;
193 }
194 twrite(buf+i, n-i);
195 }
196 copy();
197}
198
199void
200copy(void)
201{
202 long n;
203 char buf[Bsize];
204 while((n=tread(buf, Bsize)) > 0) {
205 twrite(buf, n);
206 Bflush(&bout); /* for FWD on pipe; else harmless */
207 }
208}
209
210/*
211 * read whole file, keeping the tail
212 * complexity is length(file)*length(tail).
213 * could be linear.
214 */
215void
216keep(void)
217{
218 int len = 0;
219 long bufsiz = 0;
220 char *buf = 0;
221 int j, k, n;
222
223 for(n=1; n;) {
224 if(len+Bsize > bufsiz) {
225 bufsiz += 2*Bsize;
226 if(!(buf = realloc(buf, bufsiz+1)))
227 fatal("out of space");
228 }
229 for(; n && len<bufsiz; len+=n)
230 n = tread(buf+len, bufsiz-len);
231 if(count >= len)
232 continue;
233 if(units == CHARS)
234 j = len - count;
235 else {
236 /* units == LINES */
237 j = buf[len-1]=='\n'? len-1: len;
238 for(k=0; j>0; j--)
239 if(buf[j-1] == '\n')
240 if(++k >= count)
241 break;
242 }
243 memmove(buf, buf+j, len-=j);
244 }
245 if(dir == REV) {
246 if(len>0 && buf[len-1]!='\n')
247 buf[len++] = '\n';
248 for(j=len-1 ; j>0; j--)
249 if(buf[j-1] == '\n') {
250 twrite(buf+j, len-j);
251 if(--count <= 0)
252 return;
253 len = j;
254 }
255 }
256 if(count > 0)
257 twrite(buf, len);
258}
259
260/*
261 * count backward and print tail of file
262 */
263void
264reverse(void)
265{
266 int first;
267 long len = 0;
268 long n = 0;
269 long bufsiz = 0;
270 char *buf = 0;
rscb707cb52004-06-13 20:08:00 +0000271 vlong pos = tseek(0L, 2);
rscbc7cb1a2003-11-23 18:04:47 +0000272
273 for(first=1; pos>0 && count>0; first=0) {
274 n = pos>Bsize? Bsize: (int)pos;
275 pos -= n;
276 if(len+n > bufsiz) {
277 bufsiz += 2*Bsize;
278 if(!(buf = realloc(buf, bufsiz+1)))
279 fatal("out of space");
280 }
281 memmove(buf+n, buf, len);
282 len += n;
283 tseek(pos, 0);
284 if(tread(buf, n) != n)
285 fatal("length error");
286 if(first && buf[len-1]!='\n')
287 buf[len++] = '\n';
288 for(n=len-1 ; n>0 && count>0; n--)
289 if(buf[n-1] == '\n') {
290 count--;
291 if(dir == REV)
292 twrite(buf+n, len-n);
293 len = n;
294 }
295 }
296 if(dir == FWD) {
297 tseek(n==0? 0 : pos+n+1, 0);
298 copy();
299 } else
300 if(count > 0)
301 twrite(buf, len);
302}
303
rscb707cb52004-06-13 20:08:00 +0000304vlong
305tseek(vlong o, int p)
rscbc7cb1a2003-11-23 18:04:47 +0000306{
307 o = seek(file, o, p);
308 if(o == -1)
309 fatal("");
310 return o;
311}
312
313long
314tread(char *buf, long n)
315{
316 int r = read(file, buf, n);
317 if(r == -1)
318 fatal("");
319 return r;
320}
321
322void
323twrite(char *s, long n)
324{
325 if(Bwrite(&bout, s, n) != n)
326 fatal("");
327}
328
329int
330getnumber(char *s)
331{
332 if(*s=='-' || *s=='+')
333 s++;
334 if(!isdigit(*s))
335 return 0;
336 if(s[-1] == '+')
337 origin = BEG;
338 if(anycount++)
339 fatal("excess option");
340 count = atol(s);
341
342 /* check range of count */
343 if(count < 0 || (int)count != count)
344 fatal("too big");
345 return 1;
346}
347
348void
349fatal(char *s)
350{
351 char buf[ERRMAX];
352
353 errstr(buf, sizeof buf);
354 fprint(2, "tail: %s: %s\n", s, buf);
355 exits(s);
356}
357
358void
359usage(void)
360{
361 fprint(2, "%s\n", umsg);
362 exits("usage");
363}