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 }