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