URI: 
       removed tac, added tail from p9 instead (tac == tail -r) - 9base - revived minimalist port of Plan 9 userland to Unix
  HTML git clone git://git.suckless.org/9base
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 45ac8c8bd851ba5081939a63dd8a57421abec09a
   DIR parent 942791ab23de64d2580e3143ba3866ad85fa8ab3
  HTML Author: Anselm R Garbe <anselm@garbe.us>
       Date:   Sun, 11 Apr 2010 19:13:01 +0100
       
       removed tac, added tail from p9 instead (tac == tail -r)
       Diffstat:
         M Makefile                            |       2 +-
         D tac/Makefile                        |      10 ----------
         D tac/tac.1                           |      28 ----------------------------
         D tac/tac.c                           |      60 -------------------------------
         A tail/Makefile                       |      11 +++++++++++
         A tail/tail.1                         |      87 +++++++++++++++++++++++++++++++
         A tail/tail.c                         |     363 +++++++++++++++++++++++++++++++
       
       7 files changed, 462 insertions(+), 99 deletions(-)
       ---
   DIR diff --git a/Makefile b/Makefile
       @@ -4,7 +4,7 @@ include config.mk
        
        SUBDIRS  = lib9 yacc awk basename bc cal cat cleanname date dc du echo \
                   fortune freq getflags grep hoc ls mk mkdir mtime rc read \
       -           sed seq sleep sort tac tee test touch tr troff uniq
       +           sed seq sleep sort tail tee test touch tr troff uniq
        
        # factor primes
        
   DIR diff --git a/tac/Makefile b/tac/Makefile
       @@ -1,10 +0,0 @@
       -# tac - reverse line order cat
       -# Depends on ../lib9
       -
       -TARG      = tac
       -
       -include ../std.mk
       -
       -pre-uninstall:
       -
       -post-install:
   DIR diff --git a/tac/tac.1 b/tac/tac.1
       @@ -1,28 +0,0 @@
       -.TH TAC 1
       -.SH NAME
       -tac \- reverse concatenate files
       -.SH SYNOPSIS
       -.B tac
       -[
       -.I file ...
       -]
       -.SH DESCRIPTION
       -.I Tac
       -reads each
       -.I file
       -in sequence and writes it on the standard output in reverse line order.
       -.IP
       -.L
       -tac file
       -.LP
       -prints a file in reverse line order
       -.IP
       -.L
       -tac file1 file2 >file3
       -.LP
       -Concatenate reversed file1 and file2 into file3
       -.LP
       -.SH SEE ALSO
       -.IR cat (1)
       -.SH BUGS
       -Same as in cat
   DIR diff --git a/tac/tac.c b/tac/tac.c
       @@ -1,60 +0,0 @@
       -/* author: pancake<nopcode.org> */
       -#include <u.h>
       -#include <libc.h>
       -
       -static vlong bsize = 0;
       -static char *buf;
       -#define LINES 4096
       -
       -void
       -tac()
       -{
       -        int i, j;
       -        char *ptr, **nls;
       -        nls = malloc(LINES*sizeof(nls));
       -        for(i=1, ptr=buf; ptr;) {
       -                assert(nls != NULL);
       -                for(j=0; j<LINES && (ptr=strchr(ptr+1, '\n')); j++)
       -                        nls[i++] = ptr+1;
       -                nls = realloc(nls, (i+LINES)*sizeof(nls));
       -        }
       -        *nls = buf;
       -        while(i--)
       -                write(1, nls[i], nls[i+1]-nls[i]);
       -        free(nls);
       -}
       -
       -void
       -load(int f)
       -{
       -        vlong nsize, size = seek(f, 0, 2);
       -        if (size>0) {
       -                nsize = bsize + size;
       -                buf = realloc(buf, nsize);
       -                seek(f, 0, 0);
       -                read(f, buf+bsize, size);
       -                bsize = nsize;
       -        } else
       -        while ((size = read(f, buf+bsize, LINES))>0)
       -                bsize+=size;
       -}
       -
       -void
       -main(int argc, char *argv[])
       -{
       -        int i, f;
       -        buf = malloc(1);
       -        assert(buf != NULL);
       -        if (argc == 1)
       -                load(0);
       -        else for(i=1; i<argc; i++){
       -                f = open(argv[i], OREAD);
       -                if(f >= 0){
       -                        load(f);
       -                        close(f);
       -                }else sysfatal("can't open %s: %r", argv[i]);
       -        }
       -        tac();
       -        free(buf);
       -        exits(0);
       -}
   DIR diff --git a/tail/Makefile b/tail/Makefile
       @@ -0,0 +1,11 @@
       +# tail - tail unix port from plan9
       +#
       +# Depends on ../lib9
       +
       +TARG      = tail
       +
       +include ../std.mk
       +
       +pre-uninstall:
       +
       +post-install:
   DIR diff --git a/tail/tail.1 b/tail/tail.1
       @@ -0,0 +1,87 @@
       +.TH TAIL 1
       +.SH NAME
       +tail \- deliver the last part of a file
       +.SH SYNOPSIS
       +.B tail
       +[
       +.BR +- \fInumber\fP[ lbc ][ rf ]
       +]
       +[
       +.I file
       +]
       +.PP
       +.B tail
       +[
       +.B -fr
       +]
       +[
       +.B -n
       +.I nlines
       +]
       +[
       +.B -c
       +.I nbytes
       +]
       +[
       +.I file
       +]
       +.SH DESCRIPTION
       +.I Tail
       +copies the named file to the standard output beginning
       +at a designated place.
       +If no file is named, the standard input is copied.
       +.PP
       +Copying begins at position
       +.BI + number
       +measured from the beginning, or
       +.BI - number
       +from the end of the input.
       +.I Number
       +is counted in lines, 1K blocks or bytes,
       +according to the appended flag
       +.LR l ,
       +.LR b ,
       +or
       +.LR c .
       +Default is
       +.B -10l
       +(ten ell).
       +.PP
       +The further flag
       +.L r
       +causes tail to print lines from the end of the file in reverse order;
       +.L f
       +(follow) causes
       +.IR tail ,
       +after printing to the end, to keep watch and
       +print further data as it appears.
       +.PP
       +The second syntax is that promulgated by POSIX, where
       +the
       +.I numbers
       +rather than the options are signed.
       +.SH EXAMPLES
       +.TP
       +.B tail file
       +Print the last 10 lines of a file.
       +.TP
       +.B tail +0f file
       +Print a file, and continue to watch
       +data accumulate as it grows.
       +.TP
       +.B sed 10q file
       +Print the first 10 lines of a file.
       +.SH SOURCE
       +.B \*9/src/cmd/tail.c
       +.SH BUGS
       +Tails relative to the end of the file
       +are treasured up in a buffer, and thus
       +are limited in length.
       +.PP
       +According to custom, option
       +.BI + number
       +counts lines from 1, and counts
       +blocks and bytes from 0.
       +.PP
       +.I Tail
       +is ignorant of UTF.
   DIR diff --git a/tail/tail.c b/tail/tail.c
       @@ -0,0 +1,363 @@
       +#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
       + */
       +
       +vlong        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);
       +#define trunc tailtrunc
       +extern        void        trunc(Dir*, Dir**);
       +extern        vlong        tseek(vlong, 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? ~0ULL>>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;
       +        vlong 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;
       +        vlong 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);
       +}
       +
       +vlong
       +tseek(vlong 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((uchar)*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");
       +}