URI: 
       tail.c - 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
       ---
       tail.c (5753B)
       ---
            1 #include        <u.h>
            2 #include        <libc.h>
            3 #include        <ctype.h>
            4 #include        <bio.h>
            5 
            6 /*
            7  * tail command, posix plus v10 option -r.
            8  * the simple command tail -c, legal in v10, is illegal
            9  */
           10 
           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]";
           16 
           17 Biobuf        bout;
           18 enum
           19 {
           20         BEG,
           21         END
           22 } origin = END;
           23 enum
           24 {
           25         CHARS,
           26         LINES
           27 } units = LINES;
           28 enum
           29 {
           30         FWD,
           31         REV
           32 } dir = FWD;
           33 
           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 tailtrunc
           43 extern        void        trunc(Dir*, Dir**);
           44 extern        vlong        tseek(vlong, int);
           45 extern        void        twrite(char*, long);
           46 extern        void        usage(void);
           47 
           48 #define JUMP(o,p) tseek(o,p), copy()
           49 
           50 void
           51 main(int argc, char **argv)
           52 {
           53         int seekable, c;
           54 
           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                 } else
           61                 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                                 else
           69                                 if(argc > 2 && getnumber(argv[2])) {
           70                                         argc--, argv++;
           71                                         continue;
           72                                 } else
           73                                         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;
           96 
           97         if(!seekable && origin==END)
           98                 keep();
           99         else
          100         if(!seekable && origin==BEG)
          101                 skip();
          102         else
          103         if(units==CHARS && origin==END)
          104                 JUMP(-count, 2);
          105         else
          106         if(units==CHARS && origin==BEG)
          107                 JUMP(count, 0);
          108         else
          109         if(units==LINES && origin==END)
          110                 reverse();
          111         else
          112         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 }
          124 
          125 void
          126 trunc(Dir *old, Dir **new)
          127 {
          128         Dir *d;
          129         vlong olength;
          130 
          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 }
          142 
          143 void
          144 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 }
          169 
          170 /*
          171  * read past head of the file to find tail
          172  */
          173 void
          174 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 }
          198 
          199 void
          200 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 }
          209 
          210 /*
          211  * read whole file, keeping the tail
          212  *        complexity is length(file)*length(tail).
          213  *        could be linear.
          214  */
          215 void
          216 keep(void)
          217 {
          218         int len = 0;
          219         long bufsiz = 0;
          220         char *buf = 0;
          221         int j, k, n;
          222 
          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 }
          259 
          260 /*
          261  * count backward and print tail of file
          262  */
          263 void
          264 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);
          272 
          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         } else
          300         if(count > 0)
          301                 twrite(buf, len);
          302 }
          303 
          304 vlong
          305 tseek(vlong o, int p)
          306 {
          307         o = seek(file, o, p);
          308         if(o == -1)
          309                 fatal("");
          310         return o;
          311 }
          312 
          313 long
          314 tread(char *buf, long n)
          315 {
          316         int r = read(file, buf, n);
          317         if(r == -1)
          318                 fatal("");
          319         return r;
          320 }
          321 
          322 void
          323 twrite(char *s, long n)
          324 {
          325         if(Bwrite(&bout, s, n) != n)
          326                 fatal("");
          327 }
          328 
          329 int
          330 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);
          341 
          342         /* check range of count */
          343         if(count < 0 || (int)count != count)
          344                 fatal("too big");
          345         return 1;
          346 }        
          347 
          348 void                
          349 fatal(char *s)
          350 {
          351         char buf[ERRMAX];
          352 
          353         errstr(buf, sizeof buf);
          354         fprint(2, "tail: %s: %s\n", s, buf);
          355         exits(s);
          356 }
          357 
          358 void
          359 usage(void)
          360 {
          361         fprint(2, "%s\n", umsg);
          362         exits("usage");
          363 }