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 long 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 extern void trunc(Dir*, Dir**); 43 extern long tseek(long, int); 44 extern void twrite(char*, long); 45 extern void usage(void); 46 47 #define JUMP(o,p) tseek(o,p), copy() 48 49 void 50 main(int argc, char **argv) 51 { 52 int seekable, c; 53 54 Binit(&bout, 1, OWRITE); 55 for(; argc > 1 && ((c=*argv[1])=='-'||c=='+'); argc--,argv++ ) { 56 if(getnumber(argv[1])) { 57 suffix(argv[1]); 58 continue; 59 } else 60 if(c == '-') 61 switch(argv[1][1]) { 62 case 'c': 63 units = CHARS; 64 case 'n': 65 if(getnumber(argv[1]+2)) 66 continue; 67 else 68 if(argc > 2 && getnumber(argv[2])) { 69 argc--, argv++; 70 continue; 71 } else 72 usage(); 73 case 'r': 74 dir = REV; 75 continue; 76 case 'f': 77 follow++; 78 continue; 79 case '-': 80 argc--, argv++; 81 } 82 break; 83 } 84 if(dir==REV && (units==CHARS || follow || origin==BEG)) 85 fatal("incompatible options"); 86 if(!anycount) 87 count = dir==REV? ~0UL>>1: 10; 88 if(origin==BEG && units==LINES && count>0) 89 count--; 90 if(argc > 2) 91 usage(); 92 if(argc > 1 && (file=open(argv[1],0)) < 0) 93 fatal(argv[1]); 94 seekable = seek(file,0L,0) == 0; 95 96 if(!seekable && origin==END) 97 keep(); 98 else 99 if(!seekable && origin==BEG) 100 skip(); 101 else 102 if(units==CHARS && origin==END) 103 JUMP(-count, 2); 104 else 105 if(units==CHARS && origin==BEG) 106 JUMP(count, 0); 107 else 108 if(units==LINES && origin==END) 109 reverse(); 110 else 111 if(units==LINES && origin==BEG) 112 skip(); 113 if(follow && seekable) 114 for(;;) { 115 static Dir *sb0, *sb1; 116 trunc(sb1, &sb0); 117 copy(); 118 trunc(sb0, &sb1); 119 sleep(5000); 120 } 121 exits(0); 122 } 123 124 void 125 trunc(Dir *old, Dir **new) 126 { 127 Dir *d; 128 ulong olength; 129 130 d = dirfstat(file); 131 if(d == nil) 132 return; 133 olength = 0; 134 if(old) 135 olength = old->length; 136 if(d->length < olength) 137 d->length = tseek(0L, 0); 138 free(*new); 139 *new = d; 140 } 141 142 void 143 suffix(char *s) 144 { 145 while(*s && strchr("0123456789+-", *s)) 146 s++; 147 switch(*s) { 148 case 'b': 149 if((count *= 1024) < 0) 150 fatal("too big"); 151 case 'c': 152 units = CHARS; 153 case 'l': 154 s++; 155 } 156 switch(*s) { 157 case 'r': 158 dir = REV; 159 return; 160 case 'f': 161 follow++; 162 return; 163 case 0: 164 return; 165 } 166 usage(); 167 } 168 169 /* 170 * read past head of the file to find tail 171 */ 172 void 173 skip(void) 174 { 175 int i; 176 long n; 177 char buf[Bsize]; 178 if(units == CHARS) { 179 for( ; count>0; count -=n) { 180 n = count<Bsize? count: Bsize; 181 if(!(n = tread(buf, n))) 182 return; 183 } 184 } else /*units == LINES*/ { 185 n = i = 0; 186 while(count > 0) { 187 if(!(n = tread(buf, Bsize))) 188 return; 189 for(i=0; i<n && count>0; i++) 190 if(buf[i]=='\n') 191 count--; 192 } 193 twrite(buf+i, n-i); 194 } 195 copy(); 196 } 197 198 void 199 copy(void) 200 { 201 long n; 202 char buf[Bsize]; 203 while((n=tread(buf, Bsize)) > 0) { 204 twrite(buf, n); 205 Bflush(&bout); /* for FWD on pipe; else harmless */ 206 } 207 } 208 209 /* 210 * read whole file, keeping the tail 211 * complexity is length(file)*length(tail). 212 * could be linear. 213 */ 214 void 215 keep(void) 216 { 217 int len = 0; 218 long bufsiz = 0; 219 char *buf = 0; 220 int j, k, n; 221 222 for(n=1; n;) { 223 if(len+Bsize > bufsiz) { 224 bufsiz += 2*Bsize; 225 if(!(buf = realloc(buf, bufsiz+1))) 226 fatal("out of space"); 227 } 228 for(; n && len<bufsiz; len+=n) 229 n = tread(buf+len, bufsiz-len); 230 if(count >= len) 231 continue; 232 if(units == CHARS) 233 j = len - count; 234 else { 235 /* units == LINES */ 236 j = buf[len-1]=='\n'? len-1: len; 237 for(k=0; j>0; j--) 238 if(buf[j-1] == '\n') 239 if(++k >= count) 240 break; 241 } 242 memmove(buf, buf+j, len-=j); 243 } 244 if(dir == REV) { 245 if(len>0 && buf[len-1]!='\n') 246 buf[len++] = '\n'; 247 for(j=len-1 ; j>0; j--) 248 if(buf[j-1] == '\n') { 249 twrite(buf+j, len-j); 250 if(--count <= 0) 251 return; 252 len = j; 253 } 254 } 255 if(count > 0) 256 twrite(buf, len); 257 } 258 259 /* 260 * count backward and print tail of file 261 */ 262 void 263 reverse(void) 264 { 265 int first; 266 long len = 0; 267 long n = 0; 268 long bufsiz = 0; 269 char *buf = 0; 270 long pos = tseek(0L, 2); 271 272 for(first=1; pos>0 && count>0; first=0) { 273 n = pos>Bsize? Bsize: (int)pos; 274 pos -= n; 275 if(len+n > bufsiz) { 276 bufsiz += 2*Bsize; 277 if(!(buf = realloc(buf, bufsiz+1))) 278 fatal("out of space"); 279 } 280 memmove(buf+n, buf, len); 281 len += n; 282 tseek(pos, 0); 283 if(tread(buf, n) != n) 284 fatal("length error"); 285 if(first && buf[len-1]!='\n') 286 buf[len++] = '\n'; 287 for(n=len-1 ; n>0 && count>0; n--) 288 if(buf[n-1] == '\n') { 289 count--; 290 if(dir == REV) 291 twrite(buf+n, len-n); 292 len = n; 293 } 294 } 295 if(dir == FWD) { 296 tseek(n==0? 0 : pos+n+1, 0); 297 copy(); 298 } else 299 if(count > 0) 300 twrite(buf, len); 301 } 302 303 long 304 tseek(long o, int p) 305 { 306 o = seek(file, o, p); 307 if(o == -1) 308 fatal(""); 309 return o; 310 } 311 312 long 313 tread(char *buf, long n) 314 { 315 int r = read(file, buf, n); 316 if(r == -1) 317 fatal(""); 318 return r; 319 } 320 321 void 322 twrite(char *s, long n) 323 { 324 if(Bwrite(&bout, s, n) != n) 325 fatal(""); 326 } 327 328 int 329 getnumber(char *s) 330 { 331 if(*s=='-' || *s=='+') 332 s++; 333 if(!isdigit(*s)) 334 return 0; 335 if(s[-1] == '+') 336 origin = BEG; 337 if(anycount++) 338 fatal("excess option"); 339 count = atol(s); 340 341 /* check range of count */ 342 if(count < 0 || (int)count != count) 343 fatal("too big"); 344 return 1; 345 } 346 347 void 348 fatal(char *s) 349 { 350 char buf[ERRMAX]; 351 352 errstr(buf, sizeof buf); 353 fprint(2, "tail: %s: %s\n", s, buf); 354 exits(s); 355 } 356 357 void 358 usage(void) 359 { 360 fprint(2, "%s\n", umsg); 361 exits("usage"); 362 } 363