13e12c5d1SDavid du Colombier #include <u.h> 23e12c5d1SDavid du Colombier #include <libc.h> 33e12c5d1SDavid du Colombier #include <ctype.h> 43e12c5d1SDavid du Colombier #include <bio.h> 53e12c5d1SDavid du Colombier 67dd7cddfSDavid du Colombier /* 77dd7cddfSDavid du Colombier * tail command, posix plus v10 option -r. 87dd7cddfSDavid du Colombier * the simple command tail -c, legal in v10, is illegal 97dd7cddfSDavid du Colombier */ 103e12c5d1SDavid du Colombier 113e12c5d1SDavid du Colombier long count; 123e12c5d1SDavid du Colombier int anycount; 133e12c5d1SDavid du Colombier int follow; 143e12c5d1SDavid du Colombier int file = 0; 157dd7cddfSDavid du Colombier char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]"; 163e12c5d1SDavid du Colombier 177dd7cddfSDavid du Colombier Biobuf bout; 187dd7cddfSDavid du Colombier enum 197dd7cddfSDavid du Colombier { 207dd7cddfSDavid du Colombier BEG, 217dd7cddfSDavid du Colombier END 227dd7cddfSDavid du Colombier } origin = END; 237dd7cddfSDavid du Colombier enum 247dd7cddfSDavid du Colombier { 257dd7cddfSDavid du Colombier CHARS, 267dd7cddfSDavid du Colombier LINES 277dd7cddfSDavid du Colombier } units = LINES; 287dd7cddfSDavid du Colombier enum 297dd7cddfSDavid du Colombier { 307dd7cddfSDavid du Colombier FWD, 317dd7cddfSDavid du Colombier REV 327dd7cddfSDavid du Colombier } dir = FWD; 337dd7cddfSDavid du Colombier 347dd7cddfSDavid du Colombier extern void copy(void); 357dd7cddfSDavid du Colombier extern void fatal(char*); 363e12c5d1SDavid du Colombier extern int getnumber(char*); 377dd7cddfSDavid du Colombier extern void keep(void); 387dd7cddfSDavid du Colombier extern void reverse(void); 397dd7cddfSDavid du Colombier extern void skip(void); 407dd7cddfSDavid du Colombier extern void suffix(char*); 417dd7cddfSDavid du Colombier extern long tread(char*, long); 42*9a747e4fSDavid du Colombier extern void trunc(Dir*, Dir**); 437dd7cddfSDavid du Colombier extern long tseek(long, int); 447dd7cddfSDavid du Colombier extern void twrite(char*, long); 457dd7cddfSDavid du Colombier extern void usage(void); 467dd7cddfSDavid du Colombier 477dd7cddfSDavid du Colombier #define JUMP(o,p) tseek(o,p), copy() 483e12c5d1SDavid du Colombier 493e12c5d1SDavid du Colombier void 503e12c5d1SDavid du Colombier main(int argc, char **argv) 513e12c5d1SDavid du Colombier { 523e12c5d1SDavid du Colombier int seekable, c; 537dd7cddfSDavid du Colombier 543e12c5d1SDavid du Colombier Binit(&bout, 1, OWRITE); 553e12c5d1SDavid du Colombier for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) { 563e12c5d1SDavid du Colombier if(getnumber(argv[1])) { 573e12c5d1SDavid du Colombier suffix(argv[1]); 583e12c5d1SDavid du Colombier continue; 597dd7cddfSDavid du Colombier } else 607dd7cddfSDavid du Colombier if(c == '-') 613e12c5d1SDavid du Colombier switch(argv[1][1]) { 623e12c5d1SDavid du Colombier case 'c': 633e12c5d1SDavid du Colombier units = CHARS; 643e12c5d1SDavid du Colombier case 'n': 653e12c5d1SDavid du Colombier if(getnumber(argv[1]+2)) 663e12c5d1SDavid du Colombier continue; 677dd7cddfSDavid du Colombier else 687dd7cddfSDavid du Colombier if(argc > 2 && getnumber(argv[2])) { 693e12c5d1SDavid du Colombier argc--, argv++; 703e12c5d1SDavid du Colombier continue; 713e12c5d1SDavid du Colombier } else 723e12c5d1SDavid du Colombier usage(); 733e12c5d1SDavid du Colombier case 'r': 743e12c5d1SDavid du Colombier dir = REV; 753e12c5d1SDavid du Colombier continue; 763e12c5d1SDavid du Colombier case 'f': 773e12c5d1SDavid du Colombier follow++; 783e12c5d1SDavid du Colombier continue; 793e12c5d1SDavid du Colombier case '-': 803e12c5d1SDavid du Colombier argc--, argv++; 813e12c5d1SDavid du Colombier } 823e12c5d1SDavid du Colombier break; 833e12c5d1SDavid du Colombier } 843e12c5d1SDavid du Colombier if(dir==REV && (units==CHARS || follow || origin==BEG)) 853e12c5d1SDavid du Colombier fatal("incompatible options"); 863e12c5d1SDavid du Colombier if(!anycount) 873e12c5d1SDavid du Colombier count = dir==REV? ~0UL>>1: 10; 883e12c5d1SDavid du Colombier if(origin==BEG && units==LINES && count>0) 893e12c5d1SDavid du Colombier count--; 903e12c5d1SDavid du Colombier if(argc > 2) 913e12c5d1SDavid du Colombier usage(); 923e12c5d1SDavid du Colombier if(argc > 1 && (file=open(argv[1],0)) < 0) 933e12c5d1SDavid du Colombier fatal(argv[1]); 943e12c5d1SDavid du Colombier seekable = seek(file,0L,0) == 0; 953e12c5d1SDavid du Colombier 963e12c5d1SDavid du Colombier if(!seekable && origin==END) 973e12c5d1SDavid du Colombier keep(); 987dd7cddfSDavid du Colombier else 997dd7cddfSDavid du Colombier if(!seekable && origin==BEG) 1003e12c5d1SDavid du Colombier skip(); 1017dd7cddfSDavid du Colombier else 1027dd7cddfSDavid du Colombier if(units==CHARS && origin==END) 1037dd7cddfSDavid du Colombier JUMP(-count, 2); 1047dd7cddfSDavid du Colombier else 1057dd7cddfSDavid du Colombier if(units==CHARS && origin==BEG) 1067dd7cddfSDavid du Colombier JUMP(count, 0); 1077dd7cddfSDavid du Colombier else 1087dd7cddfSDavid du Colombier if(units==LINES && origin==END) 1093e12c5d1SDavid du Colombier reverse(); 1107dd7cddfSDavid du Colombier else 1117dd7cddfSDavid du Colombier if(units==LINES && origin==BEG) 1123e12c5d1SDavid du Colombier skip(); 1133e12c5d1SDavid du Colombier if(follow && seekable) 1143e12c5d1SDavid du Colombier for(;;) { 115*9a747e4fSDavid du Colombier static Dir *sb0, *sb1; 116*9a747e4fSDavid du Colombier trunc(sb1, &sb0); 1173e12c5d1SDavid du Colombier copy(); 118*9a747e4fSDavid du Colombier trunc(sb0, &sb1); 1193e12c5d1SDavid du Colombier sleep(5000); 1203e12c5d1SDavid du Colombier } 1213e12c5d1SDavid du Colombier exits(0); 1223e12c5d1SDavid du Colombier } 1233e12c5d1SDavid du Colombier 1243e12c5d1SDavid du Colombier void 125*9a747e4fSDavid du Colombier trunc(Dir *old, Dir **new) 1263e12c5d1SDavid du Colombier { 127*9a747e4fSDavid du Colombier Dir *d; 128*9a747e4fSDavid du Colombier ulong olength; 129*9a747e4fSDavid du Colombier 130*9a747e4fSDavid du Colombier d = dirfstat(file); 131*9a747e4fSDavid du Colombier if(d == nil) 132*9a747e4fSDavid du Colombier return; 133*9a747e4fSDavid du Colombier olength = 0; 134*9a747e4fSDavid du Colombier if(old) 135*9a747e4fSDavid du Colombier olength = old->length; 136*9a747e4fSDavid du Colombier if(d->length < olength) 137*9a747e4fSDavid du Colombier d->length = tseek(0L, 0); 138*9a747e4fSDavid du Colombier free(*new); 139*9a747e4fSDavid du Colombier *new = d; 1403e12c5d1SDavid du Colombier } 1413e12c5d1SDavid du Colombier 1423e12c5d1SDavid du Colombier void 1433e12c5d1SDavid du Colombier suffix(char *s) 1443e12c5d1SDavid du Colombier { 1453e12c5d1SDavid du Colombier while(*s && strchr("0123456789+-", *s)) 1463e12c5d1SDavid du Colombier s++; 1473e12c5d1SDavid du Colombier switch(*s) { 1483e12c5d1SDavid du Colombier case 'b': 1493e12c5d1SDavid du Colombier if((count *= 1024) < 0) 1503e12c5d1SDavid du Colombier fatal("too big"); 1513e12c5d1SDavid du Colombier case 'c': 1523e12c5d1SDavid du Colombier units = CHARS; 1533e12c5d1SDavid du Colombier case 'l': 1543e12c5d1SDavid du Colombier s++; 1553e12c5d1SDavid du Colombier } 1563e12c5d1SDavid du Colombier switch(*s) { 1573e12c5d1SDavid du Colombier case 'r': 1583e12c5d1SDavid du Colombier dir = REV; 1593e12c5d1SDavid du Colombier return; 1603e12c5d1SDavid du Colombier case 'f': 1613e12c5d1SDavid du Colombier follow++; 1623e12c5d1SDavid du Colombier return; 1633e12c5d1SDavid du Colombier case 0: 1643e12c5d1SDavid du Colombier return; 1653e12c5d1SDavid du Colombier } 1663e12c5d1SDavid du Colombier usage(); 1673e12c5d1SDavid du Colombier } 1683e12c5d1SDavid du Colombier 1697dd7cddfSDavid du Colombier /* 1707dd7cddfSDavid du Colombier * read past head of the file to find tail 1717dd7cddfSDavid du Colombier */ 1723e12c5d1SDavid du Colombier void 1737dd7cddfSDavid du Colombier skip(void) 1743e12c5d1SDavid du Colombier { 1753e12c5d1SDavid du Colombier int i; 1763e12c5d1SDavid du Colombier long n; 1773e12c5d1SDavid du Colombier char buf[Bsize]; 1783e12c5d1SDavid du Colombier if(units == CHARS) { 1793e12c5d1SDavid du Colombier for( ; count>0; count -=n) { 1803e12c5d1SDavid du Colombier n = count<Bsize? count: Bsize; 1813e12c5d1SDavid du Colombier if(!(n = tread(buf, n))) 1823e12c5d1SDavid du Colombier return; 1833e12c5d1SDavid du Colombier } 1843e12c5d1SDavid du Colombier } else /*units == LINES*/ { 1853e12c5d1SDavid du Colombier n = i = 0; 1863e12c5d1SDavid du Colombier while(count > 0) { 1873e12c5d1SDavid du Colombier if(!(n = tread(buf, Bsize))) 1883e12c5d1SDavid du Colombier return; 1893e12c5d1SDavid du Colombier for(i=0; i<n && count>0; i++) 1903e12c5d1SDavid du Colombier if(buf[i]=='\n') 1913e12c5d1SDavid du Colombier count--; 1923e12c5d1SDavid du Colombier } 1933e12c5d1SDavid du Colombier twrite(buf+i, n-i); 1943e12c5d1SDavid du Colombier } 1953e12c5d1SDavid du Colombier copy(); 1963e12c5d1SDavid du Colombier } 1973e12c5d1SDavid du Colombier 1983e12c5d1SDavid du Colombier void 1993e12c5d1SDavid du Colombier copy(void) 2003e12c5d1SDavid du Colombier { 2013e12c5d1SDavid du Colombier long n; 2023e12c5d1SDavid du Colombier char buf[Bsize]; 2033e12c5d1SDavid du Colombier while((n=tread(buf, Bsize)) > 0) { 2043e12c5d1SDavid du Colombier twrite(buf, n); 2053e12c5d1SDavid du Colombier Bflush(&bout); /* for FWD on pipe; else harmless */ 2063e12c5d1SDavid du Colombier } 2073e12c5d1SDavid du Colombier } 2083e12c5d1SDavid du Colombier 2097dd7cddfSDavid du Colombier /* 2107dd7cddfSDavid du Colombier * read whole file, keeping the tail 2117dd7cddfSDavid du Colombier * complexity is length(file)*length(tail). 2127dd7cddfSDavid du Colombier * could be linear. 2137dd7cddfSDavid du Colombier */ 2143e12c5d1SDavid du Colombier void 2157dd7cddfSDavid du Colombier keep(void) 2167dd7cddfSDavid du Colombier { 2173e12c5d1SDavid du Colombier int len = 0; 2183e12c5d1SDavid du Colombier long bufsiz = 0; 2193e12c5d1SDavid du Colombier char *buf = 0; 2203e12c5d1SDavid du Colombier int j, k, n; 2217dd7cddfSDavid du Colombier 2223e12c5d1SDavid du Colombier for(n=1; n;) { 2233e12c5d1SDavid du Colombier if(len+Bsize > bufsiz) { 2243e12c5d1SDavid du Colombier bufsiz += 2*Bsize; 2253e12c5d1SDavid du Colombier if(!(buf = realloc(buf, bufsiz+1))) 2263e12c5d1SDavid du Colombier fatal("out of space"); 2273e12c5d1SDavid du Colombier } 2283e12c5d1SDavid du Colombier for(; n && len<bufsiz; len+=n) 2293e12c5d1SDavid du Colombier n = tread(buf+len, bufsiz-len); 2303e12c5d1SDavid du Colombier if(count >= len) 2313e12c5d1SDavid du Colombier continue; 2323e12c5d1SDavid du Colombier if(units == CHARS) 2333e12c5d1SDavid du Colombier j = len - count; 2347dd7cddfSDavid du Colombier else { 2357dd7cddfSDavid du Colombier /* units == LINES */ 2363e12c5d1SDavid du Colombier j = buf[len-1]=='\n'? len-1: len; 2373e12c5d1SDavid du Colombier for(k=0; j>0; j--) 2383e12c5d1SDavid du Colombier if(buf[j-1] == '\n') 2393e12c5d1SDavid du Colombier if(++k >= count) 2403e12c5d1SDavid du Colombier break; 2413e12c5d1SDavid du Colombier } 2423e12c5d1SDavid du Colombier memmove(buf, buf+j, len-=j); 2433e12c5d1SDavid du Colombier } 2443e12c5d1SDavid du Colombier if(dir == REV) { 2453e12c5d1SDavid du Colombier if(len>0 && buf[len-1]!='\n') 2463e12c5d1SDavid du Colombier buf[len++] = '\n'; 2473e12c5d1SDavid du Colombier for(j=len-1 ; j>0; j--) 2483e12c5d1SDavid du Colombier if(buf[j-1] == '\n') { 2493e12c5d1SDavid du Colombier twrite(buf+j, len-j); 2503e12c5d1SDavid du Colombier if(--count <= 0) 2513e12c5d1SDavid du Colombier return; 2523e12c5d1SDavid du Colombier len = j; 2533e12c5d1SDavid du Colombier } 2543e12c5d1SDavid du Colombier } 2553e12c5d1SDavid du Colombier if(count > 0) 2563e12c5d1SDavid du Colombier twrite(buf, len); 2573e12c5d1SDavid du Colombier } 2583e12c5d1SDavid du Colombier 2597dd7cddfSDavid du Colombier /* 2607dd7cddfSDavid du Colombier * count backward and print tail of file 2617dd7cddfSDavid du Colombier */ 2623e12c5d1SDavid du Colombier void 2637dd7cddfSDavid du Colombier reverse(void) 2643e12c5d1SDavid du Colombier { 2653e12c5d1SDavid du Colombier int first; 2663e12c5d1SDavid du Colombier long len = 0; 2673e12c5d1SDavid du Colombier long n = 0; 2683e12c5d1SDavid du Colombier long bufsiz = 0; 2693e12c5d1SDavid du Colombier char *buf = 0; 2703e12c5d1SDavid du Colombier long pos = tseek(0L, 2); 2717dd7cddfSDavid du Colombier 2723e12c5d1SDavid du Colombier for(first=1; pos>0 && count>0; first=0) { 2733e12c5d1SDavid du Colombier n = pos>Bsize? Bsize: (int)pos; 2743e12c5d1SDavid du Colombier pos -= n; 2753e12c5d1SDavid du Colombier if(len+n > bufsiz) { 2763e12c5d1SDavid du Colombier bufsiz += 2*Bsize; 2773e12c5d1SDavid du Colombier if(!(buf = realloc(buf, bufsiz+1))) 2783e12c5d1SDavid du Colombier fatal("out of space"); 2793e12c5d1SDavid du Colombier } 2803e12c5d1SDavid du Colombier memmove(buf+n, buf, len); 2813e12c5d1SDavid du Colombier len += n; 2823e12c5d1SDavid du Colombier tseek(pos, 0); 2833e12c5d1SDavid du Colombier if(tread(buf, n) != n) 2843e12c5d1SDavid du Colombier fatal("length error"); 2853e12c5d1SDavid du Colombier if(first && buf[len-1]!='\n') 2863e12c5d1SDavid du Colombier buf[len++] = '\n'; 2873e12c5d1SDavid du Colombier for(n=len-1 ; n>0 && count>0; n--) 2883e12c5d1SDavid du Colombier if(buf[n-1] == '\n') { 2893e12c5d1SDavid du Colombier count--; 2903e12c5d1SDavid du Colombier if(dir == REV) 2913e12c5d1SDavid du Colombier twrite(buf+n, len-n); 2923e12c5d1SDavid du Colombier len = n; 2933e12c5d1SDavid du Colombier } 2943e12c5d1SDavid du Colombier } 2953e12c5d1SDavid du Colombier if(dir == FWD) { 2963e12c5d1SDavid du Colombier tseek(n==0? 0 : pos+n+1, 0); 2973e12c5d1SDavid du Colombier copy(); 2987dd7cddfSDavid du Colombier } else 2997dd7cddfSDavid du Colombier if(count > 0) 3003e12c5d1SDavid du Colombier twrite(buf, len); 3013e12c5d1SDavid du Colombier } 3023e12c5d1SDavid du Colombier 3033e12c5d1SDavid du Colombier long 3043e12c5d1SDavid du Colombier tseek(long o, int p) 3053e12c5d1SDavid du Colombier { 3063e12c5d1SDavid du Colombier o = seek(file, o, p); 3073e12c5d1SDavid du Colombier if(o == -1) 3083e12c5d1SDavid du Colombier fatal(""); 3093e12c5d1SDavid du Colombier return o; 3103e12c5d1SDavid du Colombier } 3113e12c5d1SDavid du Colombier 3123e12c5d1SDavid du Colombier long 3133e12c5d1SDavid du Colombier tread(char *buf, long n) 3143e12c5d1SDavid du Colombier { 3153e12c5d1SDavid du Colombier int r = read(file, buf, n); 3163e12c5d1SDavid du Colombier if(r == -1) 3173e12c5d1SDavid du Colombier fatal(""); 3183e12c5d1SDavid du Colombier return r; 3193e12c5d1SDavid du Colombier } 3203e12c5d1SDavid du Colombier 3213e12c5d1SDavid du Colombier void 3223e12c5d1SDavid du Colombier twrite(char *s, long n) 3233e12c5d1SDavid du Colombier { 3243e12c5d1SDavid du Colombier if(Bwrite(&bout, s, n) != n) 3253e12c5d1SDavid du Colombier fatal(""); 3263e12c5d1SDavid du Colombier } 3273e12c5d1SDavid du Colombier 3283e12c5d1SDavid du Colombier int 3293e12c5d1SDavid du Colombier getnumber(char *s) 3303e12c5d1SDavid du Colombier { 3313e12c5d1SDavid du Colombier if(*s=='-' || *s=='+') 3323e12c5d1SDavid du Colombier s++; 3333e12c5d1SDavid du Colombier if(!isdigit(*s)) 3343e12c5d1SDavid du Colombier return 0; 3353e12c5d1SDavid du Colombier if(s[-1] == '+') 3363e12c5d1SDavid du Colombier origin = BEG; 3373e12c5d1SDavid du Colombier if(anycount++) 3383e12c5d1SDavid du Colombier fatal("excess option"); 3393e12c5d1SDavid du Colombier count = atol(s); 3407dd7cddfSDavid du Colombier 3417dd7cddfSDavid du Colombier /* check range of count */ 3427dd7cddfSDavid du Colombier if(count < 0 || (int)count != count) 3433e12c5d1SDavid du Colombier fatal("too big"); 3443e12c5d1SDavid du Colombier return 1; 3453e12c5d1SDavid du Colombier } 3463e12c5d1SDavid du Colombier 3473e12c5d1SDavid du Colombier void 3483e12c5d1SDavid du Colombier fatal(char *s) 3493e12c5d1SDavid du Colombier { 350*9a747e4fSDavid du Colombier char buf[ERRMAX]; 3513e12c5d1SDavid du Colombier 352*9a747e4fSDavid du Colombier errstr(buf, sizeof buf); 3533e12c5d1SDavid du Colombier fprint(2, "tail: %s: %s\n", s, buf); 3543e12c5d1SDavid du Colombier exits(s); 3553e12c5d1SDavid du Colombier } 3563e12c5d1SDavid du Colombier 3573e12c5d1SDavid du Colombier void 3583e12c5d1SDavid du Colombier usage(void) 3593e12c5d1SDavid du Colombier { 3607dd7cddfSDavid du Colombier fprint(2, "%s\n", umsg); 3613e12c5d1SDavid du Colombier exits("usage"); 3623e12c5d1SDavid du Colombier } 363