7 * tail command, posix plus v10 option -r.
8 * the simple command tail -c, legal in v10, is illegal
16 char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]";
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);
49 #define JUMP(o,p) tseek(o,p), copy()
52 main(int argc, char **argv)
56 Binit(&bout, 1, OWRITE);
57 for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
58 if(getnumber(argv[1])) {
67 if(getnumber(argv[1]+2))
70 if(argc > 2 && getnumber(argv[2])) {
86 if(dir==REV && (units==CHARS || follow || origin==BEG))
87 fatal("incompatible options");
89 count = dir==REV? ~0UL>>1: 10;
90 if(origin==BEG && units==LINES && count>0)
94 if(argc > 1 && (file=open(argv[1],0)) < 0)
96 seekable = isseekable(file);
98 if(!seekable && origin==END)
101 if(!seekable && origin==BEG)
104 if(units==CHARS && origin==END)
107 if(units==CHARS && origin==BEG)
110 if(units==LINES && origin==END)
113 if(units==LINES && origin==BEG)
115 if(follow && (seekable || fend == 0))
117 static Dir *sb0, *sb1;
127 trunc(Dir *old, Dir **new)
137 olength = old->length;
138 if(d->length < olength)
139 d->length = tseek(0LL, 0);
147 while(*s && strchr("0123456789+-", *s))
151 if((count *= 1024) < 0)
172 * read past head of the file to find tail
181 for( ; count>0; count -=n) {
182 n = count<Bsize? count: Bsize;
183 if(!(n = tread(buf, n)))
186 } else /*units == LINES*/ {
189 if(!(n = tread(buf, Bsize)))
191 for(i=0; i<n && count>0; i++)
205 while((n=tread(buf, Bsize)) > 0) {
207 Bflush(&bout); /* for FWD on pipe; else harmless */
212 * read whole file, keeping the tail
213 * complexity is length(file)*length(tail).
225 if(len+Bsize > bufsiz) {
227 if(!(buf = realloc(buf, bufsiz+1)))
228 fatal("out of space");
230 for(; n && len<bufsiz; len+=n)
231 n = tread(buf+len, bufsiz-len);
238 j = buf[len-1]=='\n'? len-1: len;
244 memmove(buf, buf+j, len-=j);
247 if(len>0 && buf[len-1]!='\n')
249 for(j=len-1 ; j>0; j--)
250 if(buf[j-1] == '\n') {
251 twrite(buf+j, len-j);
262 * count backward and print tail of file
272 vlong pos = tseek(0LL, 2);
274 for(first=1; pos>0 && count>0; first=0) {
275 n = pos>Bsize? Bsize: (long)pos;
279 if(!(buf = realloc(buf, bufsiz+1)))
280 fatal("out of space");
282 memmove(buf+n, buf, len);
285 if(tread(buf, n) != n)
286 fatal("length error");
287 if(first && buf[len-1]!='\n')
289 for(n=len-1 ; n>0 && count>0; n--)
290 if(buf[n-1] == '\n') {
293 twrite(buf+n, len-n);
309 tseek(vlong o, int p)
311 o = seek(file, o, p);
318 tread(char *buf, long n)
320 int r = read(file, buf, n);
327 twrite(char *s, long n)
329 if(Bwrite(&bout, s, n) != n)
336 if(*s=='-' || *s=='+')
343 fatal("excess option");
346 /* check range of count */
347 if(count < 0 || (int)count != count)
357 errstr(buf, sizeof buf);
358 fprint(2, "tail: %s: %s\n", s, buf);
365 fprint(2, "%s\n", umsg);
372 fend = seek(fd, 0, 2);
373 return fend > 0 && seek(fd, 0, 0) == 0;