diff options
author | rsc <devnull@localhost> | 2003-11-23 18:04:47 +0000 |
---|---|---|
committer | rsc <devnull@localhost> | 2003-11-23 18:04:47 +0000 |
commit | bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d (patch) | |
tree | 8ca0fe4e2418e6aa18dc74a236c577a719f6c6ed /src/cmd/tail.c | |
parent | f08fdedcee12c06e3ce9ac9bec363915978e8289 (diff) | |
download | plan9port-bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d.tar.gz plan9port-bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d.tar.bz2 plan9port-bc7cb1a15a67c859c8c71c4b52bb35fe9425a63d.zip |
new utilities.
the .C files compile but are renamed to avoid building automatically.
Diffstat (limited to 'src/cmd/tail.c')
-rw-r--r-- | src/cmd/tail.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/cmd/tail.c b/src/cmd/tail.c new file mode 100644 index 00000000..448b3eaf --- /dev/null +++ b/src/cmd/tail.c @@ -0,0 +1,362 @@ +#include <u.h> +#include <libc.h> +#include <ctype.h> +#include <bio.h> + +/* + * tail command, posix plus v10 option -r. + * the simple command tail -c, legal in v10, is illegal + */ + +long count; +int anycount; +int follow; +int file = 0; +char* umsg = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]"; + +Biobuf bout; +enum +{ + BEG, + END +} origin = END; +enum +{ + CHARS, + LINES +} units = LINES; +enum +{ + FWD, + REV +} dir = FWD; + +extern void copy(void); +extern void fatal(char*); +extern int getnumber(char*); +extern void keep(void); +extern void reverse(void); +extern void skip(void); +extern void suffix(char*); +extern long tread(char*, long); +extern void trunc(Dir*, Dir**); +extern long tseek(long, int); +extern void twrite(char*, long); +extern void usage(void); + +#define JUMP(o,p) tseek(o,p), copy() + +void +main(int argc, char **argv) +{ + int seekable, c; + + Binit(&bout, 1, OWRITE); + for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) { + if(getnumber(argv[1])) { + suffix(argv[1]); + continue; + } else + if(c == '-') + switch(argv[1][1]) { + case 'c': + units = CHARS; + case 'n': + if(getnumber(argv[1]+2)) + continue; + else + if(argc > 2 && getnumber(argv[2])) { + argc--, argv++; + continue; + } else + usage(); + case 'r': + dir = REV; + continue; + case 'f': + follow++; + continue; + case '-': + argc--, argv++; + } + break; + } + if(dir==REV && (units==CHARS || follow || origin==BEG)) + fatal("incompatible options"); + if(!anycount) + count = dir==REV? ~0UL>>1: 10; + if(origin==BEG && units==LINES && count>0) + count--; + if(argc > 2) + usage(); + if(argc > 1 && (file=open(argv[1],0)) < 0) + fatal(argv[1]); + seekable = seek(file,0L,0) == 0; + + if(!seekable && origin==END) + keep(); + else + if(!seekable && origin==BEG) + skip(); + else + if(units==CHARS && origin==END) + JUMP(-count, 2); + else + if(units==CHARS && origin==BEG) + JUMP(count, 0); + else + if(units==LINES && origin==END) + reverse(); + else + if(units==LINES && origin==BEG) + skip(); + if(follow && seekable) + for(;;) { + static Dir *sb0, *sb1; + trunc(sb1, &sb0); + copy(); + trunc(sb0, &sb1); + sleep(5000); + } + exits(0); +} + +void +trunc(Dir *old, Dir **new) +{ + Dir *d; + ulong olength; + + d = dirfstat(file); + if(d == nil) + return; + olength = 0; + if(old) + olength = old->length; + if(d->length < olength) + d->length = tseek(0L, 0); + free(*new); + *new = d; +} + +void +suffix(char *s) +{ + while(*s && strchr("0123456789+-", *s)) + s++; + switch(*s) { + case 'b': + if((count *= 1024) < 0) + fatal("too big"); + case 'c': + units = CHARS; + case 'l': + s++; + } + switch(*s) { + case 'r': + dir = REV; + return; + case 'f': + follow++; + return; + case 0: + return; + } + usage(); +} + +/* + * read past head of the file to find tail + */ +void +skip(void) +{ + int i; + long n; + char buf[Bsize]; + if(units == CHARS) { + for( ; count>0; count -=n) { + n = count<Bsize? count: Bsize; + if(!(n = tread(buf, n))) + return; + } + } else /*units == LINES*/ { + n = i = 0; + while(count > 0) { + if(!(n = tread(buf, Bsize))) + return; + for(i=0; i<n && count>0; i++) + if(buf[i]=='\n') + count--; + } + twrite(buf+i, n-i); + } + copy(); +} + +void +copy(void) +{ + long n; + char buf[Bsize]; + while((n=tread(buf, Bsize)) > 0) { + twrite(buf, n); + Bflush(&bout); /* for FWD on pipe; else harmless */ + } +} + +/* + * read whole file, keeping the tail + * complexity is length(file)*length(tail). + * could be linear. + */ +void +keep(void) +{ + int len = 0; + long bufsiz = 0; + char *buf = 0; + int j, k, n; + + for(n=1; n;) { + if(len+Bsize > bufsiz) { + bufsiz += 2*Bsize; + if(!(buf = realloc(buf, bufsiz+1))) + fatal("out of space"); + } + for(; n && len<bufsiz; len+=n) + n = tread(buf+len, bufsiz-len); + if(count >= len) + continue; + if(units == CHARS) + j = len - count; + else { + /* units == LINES */ + j = buf[len-1]=='\n'? len-1: len; + for(k=0; j>0; j--) + if(buf[j-1] == '\n') + if(++k >= count) + break; + } + memmove(buf, buf+j, len-=j); + } + if(dir == REV) { + if(len>0 && buf[len-1]!='\n') + buf[len++] = '\n'; + for(j=len-1 ; j>0; j--) + if(buf[j-1] == '\n') { + twrite(buf+j, len-j); + if(--count <= 0) + return; + len = j; + } + } + if(count > 0) + twrite(buf, len); +} + +/* + * count backward and print tail of file + */ +void +reverse(void) +{ + int first; + long len = 0; + long n = 0; + long bufsiz = 0; + char *buf = 0; + long pos = tseek(0L, 2); + + for(first=1; pos>0 && count>0; first=0) { + n = pos>Bsize? Bsize: (int)pos; + pos -= n; + if(len+n > bufsiz) { + bufsiz += 2*Bsize; + if(!(buf = realloc(buf, bufsiz+1))) + fatal("out of space"); + } + memmove(buf+n, buf, len); + len += n; + tseek(pos, 0); + if(tread(buf, n) != n) + fatal("length error"); + if(first && buf[len-1]!='\n') + buf[len++] = '\n'; + for(n=len-1 ; n>0 && count>0; n--) + if(buf[n-1] == '\n') { + count--; + if(dir == REV) + twrite(buf+n, len-n); + len = n; + } + } + if(dir == FWD) { + tseek(n==0? 0 : pos+n+1, 0); + copy(); + } else + if(count > 0) + twrite(buf, len); +} + +long +tseek(long o, int p) +{ + o = seek(file, o, p); + if(o == -1) + fatal(""); + return o; +} + +long +tread(char *buf, long n) +{ + int r = read(file, buf, n); + if(r == -1) + fatal(""); + return r; +} + +void +twrite(char *s, long n) +{ + if(Bwrite(&bout, s, n) != n) + fatal(""); +} + +int +getnumber(char *s) +{ + if(*s=='-' || *s=='+') + s++; + if(!isdigit(*s)) + return 0; + if(s[-1] == '+') + origin = BEG; + if(anycount++) + fatal("excess option"); + count = atol(s); + + /* check range of count */ + if(count < 0 || (int)count != count) + fatal("too big"); + return 1; +} + +void +fatal(char *s) +{ + char buf[ERRMAX]; + + errstr(buf, sizeof buf); + fprint(2, "tail: %s: %s\n", s, buf); + exits(s); +} + +void +usage(void) +{ + fprint(2, "%s\n", umsg); + exits("usage"); +} |