]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/tail.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[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 long    count;
12 int     anycount;
13 int     follow;
14 int     file    = 0;
15 char*   umsg    = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
16
17 Biobuf  bout;
18 enum
19 {
20         BEG,
21         END
22 } origin = END;
23 enum
24 {
25         CHARS,
26         LINES
27 } units = LINES;
28 enum
29 {
30         FWD,
31         REV
32 } dir = FWD;
33
34 extern  void    copy(void);
35 extern  void    fatal(char*);
36 extern  int     getnumber(char*);
37 extern  void    keep(void);
38 extern  void    reverse(void);
39 extern  void    skip(void);
40 extern  void    suffix(char*);
41 extern  long    tread(char*, long);
42 extern  void    trunc(Dir*, Dir**);
43 extern  vlong   tseek(vlong, int);
44 extern  void    twrite(char*, long);
45 extern  void    usage(void);
46 static  int     isseekable(int fd);
47
48 #define JUMP(o,p) tseek(o,p), copy()
49
50 void
51 main(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)
88                 count = dir==REV? ~0UL>>1: 10;
89         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 = isseekable(file);
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
125 void
126 trunc(Dir *old, Dir **new)
127 {
128         Dir *d;
129         vlong olength;
130
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(0LL, 0);
139         free(*new);
140         *new = d;
141 }
142
143 void
144 suffix(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  */
173 void
174 skip(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
199 void
200 copy(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  */
215 void
216 keep(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  */
263 void
264 reverse(void)
265 {
266         int first;
267         long len = 0;
268         long n = 0;
269         long bufsiz = 0;
270         char *buf = 0;
271         vlong pos = tseek(0LL, 2);
272
273         for(first=1; pos>0 && count>0; first=0) {
274                 n = pos>Bsize? Bsize: (long)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                 if(n)
298                         tseek(pos+n+1, 0);
299                 else
300                         tseek(0, 0);
301                 copy();
302         } else
303         if(count > 0)
304                 twrite(buf, len);
305 }
306
307 vlong
308 tseek(vlong o, int p)
309 {
310         o = seek(file, o, p);
311         if(o == -1)
312                 fatal("");
313         return o;
314 }
315
316 long
317 tread(char *buf, long n)
318 {
319         int r = read(file, buf, n);
320         if(r == -1)
321                 fatal("");
322         return r;
323 }
324
325 void
326 twrite(char *s, long n)
327 {
328         if(Bwrite(&bout, s, n) != n)
329                 fatal("");
330 }
331
332 int
333 getnumber(char *s)
334 {
335         if(*s=='-' || *s=='+')
336                 s++;
337         if(!isdigit(*s))
338                 return 0;
339         if(s[-1] == '+')
340                 origin = BEG;
341         if(anycount++)
342                 fatal("excess option");
343         count = atol(s);
344
345         /* check range of count */
346         if(count < 0 || (int)count != count)
347                 fatal("too big");
348         return 1;
349 }       
350
351 void            
352 fatal(char *s)
353 {
354         char buf[ERRMAX];
355
356         errstr(buf, sizeof buf);
357         fprint(2, "tail: %s: %s\n", s, buf);
358         exits(s);
359 }
360
361 void
362 usage(void)
363 {
364         fprint(2, "%s\n", umsg);
365         exits("usage");
366 }
367
368 /* return true if seeks work and if the file is > 0 length.
369  * this will eventually bite me in the ass if seeking a file
370  * is not conservative. - presotto
371  */
372 static int
373 isseekable(int fd)
374 {       
375         vlong m;
376
377         m = seek(fd, 0, 1);
378         if(m < 0)
379                 return 0;
380         return 1;
381 }