Annotation of researchv10no/cmd/tail.c, revision 1.1.1.1

1.1       root        1: #include <ctype.h>
                      2: #include <errno.h>
                      3: #include <stdio.h>
                      4: #include <libc/libc.h>
                      5: 
                      6: /* tail command, posix plus v10 option -r.
                      7:    the simple command tail -c, legal in v10, is illegal */
                      8: 
                      9: long count;
                     10: int anycount;
                     11: int follow;
                     12: int file = 0;
                     13: enum { BEG, END } origin = END;
                     14: enum { CHARS, LINES } units = LINES;
                     15: enum { FWD, REV } dir = FWD;
                     16: 
                     17: extern void copy(void), keep(void), skip(void);
                     18: extern void usage(void), reverse(void);
                     19: extern void trunc(struct stat*, struct stat*);
                     20: extern void suffix(char*), fatal(char*);
                     21: extern long tseek(long, int);
                     22: extern long tread(char*, long);
                     23: extern void twrite(char*, long);
                     24: extern int getnumber(char*);
                     25: #define jump(o,p) tseek(o,p), copy()
                     26: 
                     27: main(int argc, char **argv)
                     28: {
                     29:        int seekable, c;
                     30:        for(; argc>1&&((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) {
                     31:                if(getnumber(argv[1])) {
                     32:                        suffix(argv[1]);
                     33:                        continue;
                     34:                } else if(c == '-')
                     35:                        switch(argv[1][1]) {
                     36:                        case 'c':
                     37:                                units = CHARS;
                     38:                        case 'n':
                     39:                                if(getnumber(argv[1]+2))
                     40:                                        continue;
                     41:                                else if(argc>2&&getnumber(argv[2])) {
                     42:                                        argc--, argv++;
                     43:                                        continue;
                     44:                                } else
                     45:                                        usage();
                     46:                        case 'r':
                     47:                                dir = REV;
                     48:                                continue;
                     49:                        case 'f':
                     50:                                follow++;
                     51:                                continue;
                     52:                        case '-':
                     53:                                argc--, argv++;
                     54:                        }
                     55:                break;
                     56:        }
                     57:        if(dir==REV && (units==CHARS || follow || origin==BEG))
                     58:                fatal("incompatible options");
                     59:        if(!anycount)
                     60:                count = dir==REV? ~0UL>>1: 10;
                     61:        if(origin==BEG && units==LINES && count>0)
                     62:                count--;
                     63:        if(argc > 2)
                     64:                usage();
                     65:        if(argc > 1 && (file=open(argv[1],0)) < 0)
                     66:                fatal(argv[1]);
                     67:        seekable = lseek(file,0L,0) == 0;
                     68:        errno = 0;
                     69: 
                     70:        if(!seekable && origin==END)
                     71:                keep();
                     72:        else if(!seekable && origin==BEG)
                     73:                skip();
                     74:        else if(units==CHARS && origin==END)
                     75:                jump(-count, 2);
                     76:        else if(units==CHARS && origin==BEG)
                     77:                jump(count, 0);
                     78:        else if(units==LINES && origin==END)
                     79:                reverse();
                     80:        else if(units==LINES && origin==BEG)
                     81:                skip();
                     82:        if(follow && seekable)
                     83:                for(;;) {
                     84:                        static struct stat sb0, sb1;
                     85:                        trunc(&sb1, &sb0);
                     86:                        copy();
                     87:                        trunc(&sb0, &sb1);
                     88:                        sleep(5);
                     89:                }
                     90:        return 0;
                     91: }
                     92: 
                     93: void
                     94: trunc(struct stat *old, struct stat *new)
                     95: {
                     96:        fstat(file, new);
                     97:        if(new->st_size < old->st_size)
                     98:                new->st_size = tseek(0L, 0);
                     99: }
                    100: 
                    101: void
                    102: suffix(char *s)
                    103: {
                    104:        while(*s && strchr("0123456789+-", *s))
                    105:                s++;
                    106:        switch(*s) {
                    107:        case 'b':
                    108:                if((count*=1024) < 0)
                    109:                        fatal("too big");
                    110:        case 'c':
                    111:                units = CHARS;
                    112:        case 'l':
                    113:                s++;
                    114:        }
                    115:        switch(*s) {
                    116:        case 'r':
                    117:                dir = REV;
                    118:                return;
                    119:        case 'f':
                    120:                follow++;
                    121:                return;
                    122:        case 0:
                    123:                return;
                    124:        }
                    125:        usage();
                    126: }
                    127: 
                    128: void
                    129: skip(void)     /* read past head of the file to find tail */
                    130: {
                    131:        int i;
                    132:        long n;
                    133:        char buf[BUFSIZ];
                    134:        if(units == CHARS) {
                    135:                for( ; count>0; count -=n) {
                    136:                        n = count<BUFSIZ? count: BUFSIZ;
                    137:                        if(!(n = tread(buf, n)))
                    138:                                return;
                    139:                }
                    140:        } else /*units == LINES*/ {
                    141:                n = i = 0;
                    142:                while(count > 0) {
                    143:                        if(!(n = tread(buf, BUFSIZ)))
                    144:                                return;
                    145:                        for(i=0; i<n && count>0; i++)
                    146:                                if(buf[i]=='\n')
                    147:                                        count--;
                    148:                }
                    149:                twrite(buf+i, n-i);
                    150:        }
                    151:        copy();
                    152: }
                    153: 
                    154: void
                    155: copy(void)
                    156: {
                    157:        long n;
                    158:        char buf[BUFSIZ];
                    159:        while((n=tread(buf, BUFSIZ)) > 0) {
                    160:                twrite(buf, n);
                    161:                fflush(stdout); /* for FWD on pipe; else harmless */
                    162:        }
                    163: }
                    164: 
                    165: void
                    166: keep(void)     /* read whole file, keeping the tail */
                    167: {      /* complexity=length(file)*length(tail).  could be linear */
                    168:        int len = 0;
                    169:        long bufsiz = 0;
                    170:        char *buf = 0;
                    171:        int j, k, n;
                    172:        for(n=1; n;) {
                    173:                if(len+BUFSIZ > bufsiz) {
                    174:                        bufsiz += 2*BUFSIZ;
                    175:                        if(!(buf = realloc(buf, bufsiz+1)))
                    176:                                fatal("out of space");
                    177:                }
                    178:                for( ; n && len<bufsiz; len+=n)
                    179:                        n = tread(buf+len, bufsiz-len);
                    180:                if(count >= len)
                    181:                        continue;
                    182:                if(units == CHARS)
                    183:                        j = len - count;
                    184:                else /*units == LINES*/ {
                    185:                        j = buf[len-1]=='\n'? len-1: len;
                    186:                        for(k=0; j>0; j--)
                    187:                                if(buf[j-1] == '\n')
                    188:                                        if(++k >= count)
                    189:                                                break;
                    190:                }
                    191:                memmove(buf, buf+j, len-=j);
                    192:        }
                    193:        if(dir == REV) {
                    194:                if(len>0 && buf[len-1]!='\n')
                    195:                        buf[len++] = '\n';
                    196:                for(j=len-1 ; j>0; j--)
                    197:                        if(buf[j-1] == '\n') {
                    198:                                twrite(buf+j, len-j);
                    199:                                if(--count <= 0)
                    200:                                        return;
                    201:                                len = j;
                    202:                        }
                    203:        }
                    204:        if(count > 0)
                    205:                twrite(buf, len);
                    206: }
                    207: 
                    208: void
                    209: reverse(void)  /* count backward and print tail of file */
                    210: {
                    211:        int first;
                    212:        long len = 0;
                    213:        long n = 0;
                    214:        long bufsiz = 0;
                    215:        char *buf = 0;
                    216:        long pos = tseek(0L, 2);
                    217:        for(first=1; pos>0 && count>0; first=0) {
                    218:                n = pos>BUFSIZ? BUFSIZ: (int)pos;
                    219:                pos -= n;
                    220:                if(len+n > bufsiz) {
                    221:                        bufsiz += 2*BUFSIZ;
                    222:                        if(!(buf = realloc(buf, bufsiz+1)))
                    223:                                fatal("out of space");
                    224:                }
                    225:                memmove(buf+n, buf, len);
                    226:                len += n;
                    227:                tseek(pos, 0);
                    228:                if(tread(buf, n) != n)
                    229:                        fatal("length error");
                    230:                if(first && buf[len-1]!='\n')
                    231:                        buf[len++] = '\n';
                    232:                for(n=len-1 ; n>0 && count>0; n--)
                    233:                        if(buf[n-1] == '\n') {
                    234:                                count--;
                    235:                                if(dir == REV)
                    236:                                        twrite(buf+n, len-n);
                    237:                                len = n;
                    238:                        }
                    239:        }
                    240:        if(dir == FWD) {
                    241:                tseek(n==0? 0: pos+n+1, 0);
                    242:                copy();
                    243:        } else if(count > 0)
                    244:                twrite(buf, len);
                    245: }
                    246: 
                    247: long
                    248: tseek(long o, int p)
                    249: {
                    250:        o = lseek(file, o, p);
                    251:        if(o == -1)
                    252:                fatal("");
                    253:        return o;
                    254: }
                    255: 
                    256: long
                    257: tread(char *buf, long n)
                    258: {
                    259:        int r = read(file, buf, n);
                    260:        if(r == -1)
                    261:                fatal("");
                    262:        return r;
                    263: }
                    264: 
                    265: void
                    266: twrite(char *s, long n)
                    267: {
                    268:        if(fwrite(s, 1 , n, stdout) != n)
                    269:                fatal("");
                    270: }
                    271: 
                    272: int
                    273: getnumber(char *s)
                    274: {
                    275:        if(*s=='-' || *s=='+')
                    276:                s++;
                    277:        if(!isdigit(*s))
                    278:                return 0;
                    279:        if(s[-1] == '+')
                    280:                origin = BEG;
                    281:        if(anycount++)
                    282:                fatal("excess option");
                    283:        count = atol(s);
                    284:        if(count < 0 ||         /* overflow */
                    285:           (int)count != count) /* protect int args (read, fwrite) */
                    286:                fatal("too big");
                    287:        return 1;
                    288: }      
                    289: 
                    290: void           
                    291: fatal(char *s)
                    292: {
                    293:        write(2, "tail: ", 6);
                    294:        if(errno)
                    295:                perror(s);
                    296:        else {
                    297:                write(2, s, strlen(s));
                    298:                write(2, "\n", 1);
                    299:        }
                    300:        exit(1);
                    301: }
                    302: 
                    303: char *u = "usage: tail [-n N] [-c N] [-f] [-r] [+-N[bc][fr]] [file]\n";
                    304: void
                    305: usage(void)
                    306: {
                    307:        write(2, u, strlen(u));
                    308:        exit(1);
                    309: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.