diff options
Diffstat (limited to 'appl/cmd/tail.b')
| -rw-r--r-- | appl/cmd/tail.b | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/appl/cmd/tail.b b/appl/cmd/tail.b new file mode 100644 index 00000000..07d900d1 --- /dev/null +++ b/appl/cmd/tail.b @@ -0,0 +1,379 @@ +implement Tail; + +include "sys.m"; +sys: Sys; + +include "draw.m"; + +include "bufio.m"; +bufmod : Bufio; +Iobuf : import bufmod; + +include "string.m"; + str : String; + +count, anycount, follow : int; +file : ref sys->FD; +bout : ref Iobuf; +BSize : con 8*1024; + +BEG, END, CHARS, LINES , FWD, REV : con iota; + +origin := END; +units := LINES; +dir := FWD; + + +Tail: module +{ + init: fn(nil: ref Draw->Context, argv: list of string); +}; + + +init(nil: ref Draw->Context, argv: list of string) +{ + sys = load Sys Sys->PATH; + str = load String String->PATH; + bufmod = load Bufio Bufio->PATH; + seekable : int; + bout = bufmod->fopen(sys->fildes(1),bufmod->OWRITE); + argv=parse(tl argv); + if(dir==REV && (units==CHARS || follow || origin==BEG)) + fail("incompatible options"); + if(!anycount){ + if (dir==REV) + count= 16r7fffffff; + else + count = 10; + } + if(origin==BEG && units==LINES && count>0) + count--; + if(len argv > 1) + usage(); + if(argv == nil || hd argv == "-") { + file = sys->fildes(0); + seekable = 0; + } + else { + if((file=sys->open(hd argv,sys->OREAD)) == nil ) + fatal(hd argv); + (ok, stat) := sys->fstat(file); + seekable = sys->seek(file,big 0,sys->SEEKSTART) == big 0 && stat.length > big 0; + } + + if(!seekable && origin==END) + keep(); + else if(!seekable && origin==BEG) + skip(); + else if(units==CHARS && origin==END){ + tseek(big -count, Sys->SEEKEND); + copy(); + } + else if(units==CHARS && origin==BEG){ + tseek(big count, Sys->SEEKSTART); + copy(); + } + else if(units==LINES && origin==END) + reverse(); + else if(units==LINES && origin==BEG) + skip(); + if(follow){ + if(seekable){ + d : sys->Dir; + d.length=big -1; + for(;;){ + d=trunc(d.length); + copy(); + sys->sleep(5000); + } + }else{ + for(;;){ + copy(); + sys->sleep(5000); + } + } + } + exit; +} + + +trunc(length : big) : sys->Dir +{ + (i,d):=sys->fstat(file); + if(d.length < length) + d.length = tseek(big 0, sys->SEEKSTART); + return d; +} + + +skip() # read past head of the file to find tail +{ + n : int; + buf := array[BSize] of byte; + if(units == CHARS) { + for( ; count>0; count -=n) { + if (count<BSize) + n=count; + else + n=BSize; + n = tread(buf, n); + if(n == 0) + return; + } + } else { # units == LINES + i:=0; + n=0; + while(count > 0) { + n = tread(buf, BSize); + if(n == 0) + return; + for(i=0; i<n && count>0; i++) + if(buf[i]==byte '\n') + count--; + } + twrite(buf[i:n]); + } + copy(); +} + + +copy() +{ + buf := array[BSize] of byte; + while((n := tread(buf, BSize)) > 0){ + twrite(buf[0:n]); + } + bout.flush(); +} + + +keep() # read whole file, keeping the tail +{ # complexity=length(file)*length(tail). could be linear + j, k : int; + length:=0; + buf : array of byte; + tbuf : array of byte; + bufsize := 0; + for(n:=1; n;) { + if(length+BSize > bufsize ) { + bufsize += 2*BSize; + tbuf = array[bufsize+1] of byte; + tbuf[0:]=buf[0:]; + buf = tbuf; + } + for( ; n && length<bufsize; length+=n) + n = tread(buf[length:], bufsize-length); + if(count >= length) + continue; + if(units == CHARS) + j = length - count; + else{ # units == LINES + if (int buf[length-1]=='\n') + j = length-1; + else + j=length; + for(k=0; j>0; j--) + if(int buf[j-1] == '\n') + if(++k >= count) + break; + } + length-=j; + buf[0:]=buf[j:j+length]; + } + if(dir == REV) { + if(length>0 && buf[length-1]!= byte '\n') + buf[length++] = byte '\n'; + for(j=length-1 ; j>0; j--) + if(buf[j-1] == byte '\n') { + twrite(buf[j:length]); + if(--count <= 0) + return; + length = j; + } + } + if(count > 0 && length > 0) + twrite(buf[0:length]); + bout.flush(); +} + +reverse() # count backward and print tail of file +{ + length := 0; + n := 0; + buf : array of byte; + pos := tseek(big 0, sys->SEEKEND); + bufsize := 0; + for(first:=1; pos>big 0 && count>0; first=0) { + if (pos>big BSize) + n = BSize; + else + n = int pos; + pos -= big n; + if(length+2*n > bufsize) { + bufsize += BSize*((length+2*n-bufsize+BSize-1)/BSize); + tbuf := array[bufsize+1] of byte; + tbuf[0:] = buf; + buf = tbuf; + } + length += n; + abuf := array[length] of byte; + abuf[0:] = buf[0:length]; + buf[n:] = abuf; + tseek(pos, sys->SEEKSTART); + if(tread(buf, n) != n) + fatal("length error"); + if(first && buf[length-1]!= byte '\n') + buf[length++] = byte '\n'; + for(n=length-1 ; n>0 && count>0; n--) + if(buf[n-1] == byte '\n') { + count--; + if(dir == REV){ + twrite(buf[n:length]); + bout.flush(); + } + length = n; + } + } + if(dir == FWD) { + if (n==0) + tseek(big 0 , sys->SEEKSTART); + else + tseek(pos+big n+big 1, sys->SEEKSTART); + + copy(); + } else if(count > 0) + twrite(buf[0:length]); + bout.flush(); +} + + +tseek(o : big, p: int) : big +{ + o = sys->seek(file, o, p); + if(o == big -1) + fatal(""); + return o; +} + + +tread(buf: array of byte, n: int): int +{ + r := sys->read(file, buf, n); + if(r == -1) + fatal(""); + return r; +} + + +twrite(buf:array of byte) +{ + str1:= string buf; + if(bout.puts(str1)!=len str1) + fatal(""); +} + + + +fatal(s : string) +{ + sys->fprint(sys->fildes(2), "tail: %s: %r\n", s); + exit; +} + +fail(s : string) +{ + sys->fprint(sys->fildes(2), "tail: %s\n", s); + exit; +} + + +usage() +{ + sys->fprint(sys->fildes(2), "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]\n"); + exit; +} + + +getnumber(s: string) : int +{ + i:=0; + if (len s == 0) return 0; + if(s[i]=='-' || s[i]=='+') { + if (len s == 1) + return 0; + i++; + } + if(!(s[i]>='0' && s[i]<='9')) + return 0; + if(s[0] == '+') + origin = BEG; + if(anycount++) + fail("excess option"); + if (s[0]=='-') + s=s[1:]; + (count,nil) = str->toint(s,10); + if(count < 0){ # protect int args (read, fwrite) + fail("too big"); + } + return 1; +} + +parse(args : list of string) : list of string +{ + for(; args!=nil ; args = tl args ) { + hdarg := hd args; + if(getnumber(hdarg)) + suffix(hdarg); + else if(len hdarg > 1 && hdarg[0] == '-') + case (hdarg[1]) { + 'c' or 'n'=> + if (hdarg[1]=='c') + units = CHARS; + if(len hdarg>2 && getnumber(hdarg[2:])) + ; + else if(tl args != nil && getnumber(hd tl args)) { + args = tl args; + } else + usage(); + 'r' => + dir = REV; + 'f' => + follow++; + '-' => + args = tl args; + } + else + break; + } + return args; +} + + +suffix(s : string) +{ + i:=0; + while(i < len s && str->in(s[i],"0123456789+-")) + i++; + if (i==len s) + return; + if (s[i]=='b') + if((count*=1024) < 0) + fail("too big"); + if (s[i]=='c' || s[i]=='b') + units = CHARS; + if (s[i]=='l' || s[i]=='c' || s[i]=='b') + i++; + if (i<len s){ + case s[i] { + 'r'=> + dir = REV; + return; + 'f'=> + follow++; + return; + } + } + i++; + if (i<len s) + usage(); +} |
