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