7 * tail command, posix plus v10 option -r.
8 * the simple command tail -c, legal in v10, is illegal
15 char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
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);
48 #define JUMP(o,p) tseek(o,p), copy()
51 main(int argc, char **argv)
55 Binit(&bout, 1, OWRITE);
56 for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
57 if(getnumber(argv[1])) {
66 if(getnumber(argv[1]+2))
69 if(argc > 2 && getnumber(argv[2])) {
85 if(dir==REV && (units==CHARS || follow || origin==BEG))
86 fatal("incompatible options");
88 count = dir==REV? ~0UL>>1: 10;
89 if(origin==BEG && units==LINES && count>0)
93 if(argc > 1 && (file=open(argv[1],0)) < 0)
95 seekable = isseekable(file);
97 if(!seekable && origin==END)
100 if(!seekable && origin==BEG)
103 if(units==CHARS && origin==END)
106 if(units==CHARS && origin==BEG)
109 if(units==LINES && origin==END)
112 if(units==LINES && origin==BEG)
114 if(follow && seekable)
116 static Dir *sb0, *sb1;
126 trunc(Dir *old, Dir **new)
136 olength = old->length;
137 if(d->length < olength)
138 d->length = tseek(0LL, 0);
146 while(*s && strchr("0123456789+-", *s))
150 if((count *= 1024) < 0)
171 * read past head of the file to find tail
180 for( ; count>0; count -=n) {
181 n = count<Bsize? count: Bsize;
182 if(!(n = tread(buf, n)))
185 } else /*units == LINES*/ {
188 if(!(n = tread(buf, Bsize)))
190 for(i=0; i<n && count>0; i++)
204 while((n=tread(buf, Bsize)) > 0) {
206 Bflush(&bout); /* for FWD on pipe; else harmless */
211 * read whole file, keeping the tail
212 * complexity is length(file)*length(tail).
224 if(len+Bsize > bufsiz) {
226 if(!(buf = realloc(buf, bufsiz+1)))
227 fatal("out of space");
229 for(; n && len<bufsiz; len+=n)
230 n = tread(buf+len, bufsiz-len);
237 j = buf[len-1]=='\n'? len-1: len;
243 memmove(buf, buf+j, len-=j);
246 if(len>0 && buf[len-1]!='\n')
248 for(j=len-1 ; j>0; j--)
249 if(buf[j-1] == '\n') {
250 twrite(buf+j, len-j);
261 * count backward and print tail of file
271 vlong pos = tseek(0LL, 2);
273 for(first=1; pos>0 && count>0; first=0) {
274 n = pos>Bsize? Bsize: (long)pos;
278 if(!(buf = realloc(buf, bufsiz+1)))
279 fatal("out of space");
281 memmove(buf+n, buf, len);
284 if(tread(buf, n) != n)
285 fatal("length error");
286 if(first && buf[len-1]!='\n')
288 for(n=len-1 ; n>0 && count>0; n--)
289 if(buf[n-1] == '\n') {
292 twrite(buf+n, len-n);
308 tseek(vlong o, int p)
310 o = seek(file, o, p);
317 tread(char *buf, long n)
319 int r = read(file, buf, n);
326 twrite(char *s, long n)
328 if(Bwrite(&bout, s, n) != n)
335 if(*s=='-' || *s=='+')
342 fatal("excess option");
345 /* check range of count */
346 if(count < 0 || (int)count != count)
356 errstr(buf, sizeof buf);
357 fprint(2, "tail: %s: %s\n", s, buf);
364 fprint(2, "%s\n", umsg);
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