Blob
1 #include <u.h>2 #include <libc.h>3 #include <ctype.h>4 #include <bio.h>6 /*7 * tail command, posix plus v10 option -r.8 * the simple command tail -c, legal in v10, is illegal9 */11 vlong 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]";17 Biobuf bout;18 enum19 {20 BEG,21 END22 } origin = END;23 enum24 {25 CHARS,26 LINES27 } units = LINES;28 enum29 {30 FWD,31 REV32 } dir = FWD;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 #define trunc tailtrunc43 extern void trunc(Dir*, Dir**);44 extern vlong tseek(vlong, int);45 extern void twrite(char*, long);46 extern void usage(void);48 #define JUMP(o,p) tseek(o,p), copy()50 void51 main(int argc, char **argv)52 {53 int seekable, c;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 } else61 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 else69 if(argc > 2 && getnumber(argv[2])) {70 argc--, argv++;71 continue;72 } else73 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? ~0ULL>>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 = seek(file,0L,0) == 0;97 if(!seekable && origin==END)98 keep();99 else100 if(!seekable && origin==BEG)101 skip();102 else103 if(units==CHARS && origin==END)104 JUMP(-count, 2);105 else106 if(units==CHARS && origin==BEG)107 JUMP(count, 0);108 else109 if(units==LINES && origin==END)110 reverse();111 else112 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 }125 void126 trunc(Dir *old, Dir **new)127 {128 Dir *d;129 vlong olength;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 }143 void144 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 }170 /*171 * read past head of the file to find tail172 */173 void174 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 }199 void200 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 }210 /*211 * read whole file, keeping the tail212 * complexity is length(file)*length(tail).213 * could be linear.214 */215 void216 keep(void)217 {218 int len = 0;219 long bufsiz = 0;220 char *buf = 0;221 int j, k, n;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 }260 /*261 * count backward and print tail of file262 */263 void264 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(0L, 2);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 } else300 if(count > 0)301 twrite(buf, len);302 }304 vlong305 tseek(vlong o, int p)306 {307 o = seek(file, o, p);308 if(o == -1)309 fatal("");310 return o;311 }313 long314 tread(char *buf, long n)315 {316 int r = read(file, buf, n);317 if(r == -1)318 fatal("");319 return r;320 }322 void323 twrite(char *s, long n)324 {325 if(Bwrite(&bout, s, n) != n)326 fatal("");327 }329 int330 getnumber(char *s)331 {332 if(*s=='-' || *s=='+')333 s++;334 if(!isdigit((uchar)*s))335 return 0;336 if(s[-1] == '+')337 origin = BEG;338 if(anycount++)339 fatal("excess option");340 count = atol(s);342 /* check range of count */343 if(count < 0 || (int)count != count)344 fatal("too big");345 return 1;346 }348 void349 fatal(char *s)350 {351 char buf[ERRMAX];353 errstr(buf, sizeof buf);354 fprint(2, "tail: %s: %s\n", s, buf);355 exits(s);356 }358 void359 usage(void)360 {361 fprint(2, "%s\n", umsg);362 exits("usage");363 }