|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.