1*4887Schin /*********************************************************************** 2*4887Schin * * 3*4887Schin * This software is part of the ast package * 4*4887Schin * Copyright (c) 1992-2007 AT&T Knowledge Ventures * 5*4887Schin * and is licensed under the * 6*4887Schin * Common Public License, Version 1.0 * 7*4887Schin * by AT&T Knowledge Ventures * 8*4887Schin * * 9*4887Schin * A copy of the License is available at * 10*4887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 11*4887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12*4887Schin * * 13*4887Schin * Information and Software Systems Research * 14*4887Schin * AT&T Research * 15*4887Schin * Florham Park NJ * 16*4887Schin * * 17*4887Schin * Glenn Fowler <gsf@research.att.com> * 18*4887Schin * David Korn <dgk@research.att.com> * 19*4887Schin * * 20*4887Schin ***********************************************************************/ 21*4887Schin #pragma prototyped 22*4887Schin 23*4887Schin /* 24*4887Schin * print the tail of one or more files 25*4887Schin * 26*4887Schin * David Korn 27*4887Schin * Glenn Fowler 28*4887Schin */ 29*4887Schin 30*4887Schin static const char usage[] = 31*4887Schin "+[-?\n@(#)$Id: tail (AT&T Research) 2006-10-18 $\n]" 32*4887Schin USAGE_LICENSE 33*4887Schin "[+NAME?tail - output trailing portion of one or more files ]" 34*4887Schin "[+DESCRIPTION?\btail\b copies one or more input files to standard output " 35*4887Schin "starting at a designated point for each file. Copying starts " 36*4887Schin "at the point indicated by the options and is unlimited in size.]" 37*4887Schin "[+?By default a header of the form \b==> \b\afilename\a\b <==\b " 38*4887Schin "is output before all but the first file but this can be changed " 39*4887Schin "with the \b-q\b and \b-v\b options.]" 40*4887Schin "[+?If no \afile\a is given, or if the \afile\a is \b-\b, \btail\b " 41*4887Schin "copies from standard input. The start of the file is defined " 42*4887Schin "as the current offset.]" 43*4887Schin "[+?The option argument for \b-c\b can optionally be " 44*4887Schin "followed by one of the following characters to specify a different " 45*4887Schin "unit other than a single byte:]{" 46*4887Schin "[+b?512 bytes.]" 47*4887Schin "[+k?1-kilobyte.]" 48*4887Schin "[+m?1-megabyte.]" 49*4887Schin "}" 50*4887Schin "[+?For backwards compatibility, \b-\b\anumber\a is equivalent to " 51*4887Schin "\b-n\b \anumber\a and \b+\b\anumber\a is equivalent to " 52*4887Schin "\b-n -\b\anumber\a.]" 53*4887Schin 54*4887Schin "[n:lines]:[lines:=10?Copy \alines\a lines from each file. A negative value " 55*4887Schin "for \alines\a indicates an offset from the start of the file.]" 56*4887Schin "[c:bytes]:?[chars?Copy \achars\a bytes from each file. A negative value " 57*4887Schin "for \achars\a indicates an offset from the start of the file.]" 58*4887Schin "[f:forever|follow?Loop forever trying to read more characters as the " 59*4887Schin "end of each file to copy new data. Ignored if reading from a pipe " 60*4887Schin "or fifo.]" 61*4887Schin "[h!:headers?Output filename headers.]" 62*4887Schin "[L:log?When a \b--forever\b file times out via \b--timeout\b, verify that " 63*4887Schin "the curent file has not been renamed and replaced by another file " 64*4887Schin "of the same name (a common log file practice) before giving up on " 65*4887Schin "the file.]" 66*4887Schin "[q:quiet?Don't output filename headers. For GNU compatibility.]" 67*4887Schin "[r:reverse?Output lines in reverse order.]" 68*4887Schin "[s:silent?Don't warn about timeout expiration and log file changes.]" 69*4887Schin "[t:timeout?Stop checking after \atimeout\a elapses with no additional " 70*4887Schin "\b--forever\b output. A separate elapsed time is maintained for " 71*4887Schin "each file operand. There is no timeout by default. The default " 72*4887Schin "\atimeout\a unit is seconds. \atimeout\a may be a catenation of 1 " 73*4887Schin "or more integers, each followed by a 1 character suffix. The suffix " 74*4887Schin "may be omitted from the last integer, in which case it is " 75*4887Schin "interpreted as seconds. The supported suffixes are:]:[timeout]{" 76*4887Schin "[+s?seconds]" 77*4887Schin "[+m?minutes]" 78*4887Schin "[+h?hours]" 79*4887Schin "[+d?days]" 80*4887Schin "[+w?weeks]" 81*4887Schin "[+M?months]" 82*4887Schin "[+y?years]" 83*4887Schin "[+S?scores]" 84*4887Schin "}" 85*4887Schin "[v:verbose?Always ouput filename headers.]" 86*4887Schin "\n" 87*4887Schin "\n[file ...]\n" 88*4887Schin "\n" 89*4887Schin "[+EXIT STATUS?]{" 90*4887Schin "[+0?All files copied successfully.]" 91*4887Schin "[+>0?One or more files did not copy.]" 92*4887Schin "}" 93*4887Schin "[+SEE ALSO?\bcat\b(1), \bhead\b(1), \brev\b(1)]" 94*4887Schin ; 95*4887Schin 96*4887Schin #include <cmd.h> 97*4887Schin #include <ctype.h> 98*4887Schin #include <ls.h> 99*4887Schin #include <tm.h> 100*4887Schin #include <rev.h> 101*4887Schin 102*4887Schin #define COUNT (1<<0) 103*4887Schin #define ERROR (1<<1) 104*4887Schin #define FOLLOW (1<<2) 105*4887Schin #define HEADERS (1<<3) 106*4887Schin #define LOG (1<<4) 107*4887Schin #define NEGATIVE (1<<5) 108*4887Schin #define POSITIVE (1<<6) 109*4887Schin #define REVERSE (1<<7) 110*4887Schin #define SILENT (1<<8) 111*4887Schin #define TIMEOUT (1<<9) 112*4887Schin #define VERBOSE (1<<10) 113*4887Schin 114*4887Schin #define NOW (unsigned long)time(NiL) 115*4887Schin 116*4887Schin #define LINES 10 117*4887Schin 118*4887Schin #ifdef S_ISSOCK 119*4887Schin #define FIFO(m) (S_ISFIFO(m)||S_ISSOCK(m)) 120*4887Schin #else 121*4887Schin #define FIFO(m) S_ISFIFO(m) 122*4887Schin #endif 123*4887Schin 124*4887Schin struct Tail_s; typedef struct Tail_s Tail_t; 125*4887Schin 126*4887Schin struct Tail_s 127*4887Schin { 128*4887Schin Tail_t* next; 129*4887Schin char* name; 130*4887Schin Sfio_t* sp; 131*4887Schin Sfoff_t last; 132*4887Schin unsigned long expire; 133*4887Schin long dev; 134*4887Schin long ino; 135*4887Schin }; 136*4887Schin 137*4887Schin /* 138*4887Schin * if file is seekable, position file to tail location and return offset 139*4887Schin * otherwise, return -1 140*4887Schin */ 141*4887Schin 142*4887Schin static Sfoff_t 143*4887Schin tailpos(register Sfio_t* fp, register Sfoff_t number, int delim) 144*4887Schin { 145*4887Schin register size_t n; 146*4887Schin register Sfoff_t offset; 147*4887Schin register Sfoff_t first; 148*4887Schin register Sfoff_t last; 149*4887Schin register char* s; 150*4887Schin register char* t; 151*4887Schin struct stat st; 152*4887Schin 153*4887Schin last = sfsize(fp); 154*4887Schin if ((first = sfseek(fp, (Sfoff_t)0, SEEK_CUR)) < 0) 155*4887Schin return last || fstat(sffileno(fp), &st) || st.st_size || FIFO(st.st_mode) ? -1 : 0; 156*4887Schin if (delim < 0) 157*4887Schin { 158*4887Schin if ((offset = last - number) < first) 159*4887Schin return first; 160*4887Schin return offset; 161*4887Schin } 162*4887Schin if ((offset = last - SF_BUFSIZE) < first) 163*4887Schin offset = first; 164*4887Schin for (;;) 165*4887Schin { 166*4887Schin sfseek(fp, offset, SEEK_SET); 167*4887Schin n = last - offset; 168*4887Schin if (!(s = sfreserve(fp, n, SF_LOCKR))) 169*4887Schin return -1; 170*4887Schin t = s + n; 171*4887Schin while (t > s) 172*4887Schin if (*--t == delim && number-- <= 0) 173*4887Schin { 174*4887Schin sfread(fp, s, 0); 175*4887Schin return offset + (t - s) + 1; 176*4887Schin } 177*4887Schin sfread(fp, s, 0); 178*4887Schin if (offset == first) 179*4887Schin break; 180*4887Schin last = offset; 181*4887Schin if ((offset = last - SF_BUFSIZE) < first) 182*4887Schin offset = first; 183*4887Schin } 184*4887Schin return first; 185*4887Schin } 186*4887Schin 187*4887Schin /* 188*4887Schin * this code handles tail from a pipe without any size limits 189*4887Schin */ 190*4887Schin 191*4887Schin static void 192*4887Schin pipetail(Sfio_t* infile, Sfio_t* outfile, Sfoff_t number, int delim) 193*4887Schin { 194*4887Schin register Sfio_t* out; 195*4887Schin register Sfoff_t n; 196*4887Schin register Sfoff_t nleft = number; 197*4887Schin register size_t a = 2 * SF_BUFSIZE; 198*4887Schin register int fno = 0; 199*4887Schin Sfoff_t offset[2]; 200*4887Schin Sfio_t* tmp[2]; 201*4887Schin 202*4887Schin if (delim < 0 && a > number) 203*4887Schin a = number; 204*4887Schin out = tmp[0] = sftmp(a); 205*4887Schin tmp[1] = sftmp(a); 206*4887Schin offset[0] = offset[1] = 0; 207*4887Schin while ((n = sfmove(infile, out, number, delim)) > 0) 208*4887Schin { 209*4887Schin offset[fno] = sftell(out); 210*4887Schin if ((nleft -= n) <= 0) 211*4887Schin { 212*4887Schin out = tmp[fno= !fno]; 213*4887Schin sfseek(out, (Sfoff_t)0, SEEK_SET); 214*4887Schin nleft = number; 215*4887Schin } 216*4887Schin } 217*4887Schin if (nleft == number) 218*4887Schin { 219*4887Schin offset[fno] = 0; 220*4887Schin fno= !fno; 221*4887Schin } 222*4887Schin sfseek(tmp[0], (Sfoff_t)0, SEEK_SET); 223*4887Schin 224*4887Schin /* 225*4887Schin * see whether both files are needed 226*4887Schin */ 227*4887Schin 228*4887Schin if (offset[fno]) 229*4887Schin { 230*4887Schin sfseek(tmp[1], (Sfoff_t)0, SEEK_SET); 231*4887Schin if ((n = number - nleft) > 0) 232*4887Schin sfmove(tmp[!fno], NiL, n, delim); 233*4887Schin if ((n = offset[!fno] - sftell(tmp[!fno])) > 0) 234*4887Schin sfmove(tmp[!fno], outfile, n, -1); 235*4887Schin } 236*4887Schin else 237*4887Schin fno = !fno; 238*4887Schin sfmove(tmp[fno], outfile, offset[fno], -1); 239*4887Schin sfclose(tmp[0]); 240*4887Schin sfclose(tmp[1]); 241*4887Schin } 242*4887Schin 243*4887Schin /* 244*4887Schin * (re)initialize a tail stream 245*4887Schin */ 246*4887Schin 247*4887Schin static int 248*4887Schin init(Tail_t* tp, Sfoff_t number, int delim, int flags) 249*4887Schin { 250*4887Schin Sfoff_t offset; 251*4887Schin struct stat st; 252*4887Schin 253*4887Schin if (tp->sp) 254*4887Schin { 255*4887Schin offset = 0; 256*4887Schin if (tp->sp == sfstdin) 257*4887Schin tp->sp = 0; 258*4887Schin } 259*4887Schin else if (!number) 260*4887Schin offset = 0; 261*4887Schin else 262*4887Schin offset = 1; 263*4887Schin if (!tp->name || streq(tp->name, "-")) 264*4887Schin { 265*4887Schin tp->name = "/dev/stdin"; 266*4887Schin tp->sp = sfstdin; 267*4887Schin } 268*4887Schin else if (!(tp->sp = sfopen(tp->sp, tp->name, "r"))) 269*4887Schin { 270*4887Schin error(ERROR_system(0), "%s: cannot open", tp->name); 271*4887Schin return -1; 272*4887Schin } 273*4887Schin sfset(tp->sp, SF_SHARE, 0); 274*4887Schin if (offset) 275*4887Schin { 276*4887Schin if ((offset = tailpos(tp->sp, number, delim)) < 0) 277*4887Schin { 278*4887Schin error(ERROR_SYSTEM|2, "%s: cannot position file to tail", tp->name); 279*4887Schin goto bad; 280*4887Schin } 281*4887Schin sfseek(tp->sp, offset, SEEK_SET); 282*4887Schin } 283*4887Schin tp->last = offset; 284*4887Schin if (flags & LOG) 285*4887Schin { 286*4887Schin if (fstat(sffileno(tp->sp), &st)) 287*4887Schin { 288*4887Schin error(ERROR_system(0), "%s: cannot stat", tp->name); 289*4887Schin goto bad; 290*4887Schin } 291*4887Schin tp->dev = st.st_dev; 292*4887Schin tp->ino = st.st_ino; 293*4887Schin } 294*4887Schin return 0; 295*4887Schin bad: 296*4887Schin if (tp->sp != sfstdin) 297*4887Schin sfclose(tp->sp); 298*4887Schin tp->sp = 0; 299*4887Schin return -1; 300*4887Schin } 301*4887Schin 302*4887Schin /* 303*4887Schin * convert number with validity diagnostics 304*4887Schin */ 305*4887Schin 306*4887Schin static intmax_t 307*4887Schin num(register const char* s, char** e, int* f, int o) 308*4887Schin { 309*4887Schin intmax_t number; 310*4887Schin char* t; 311*4887Schin int c; 312*4887Schin 313*4887Schin *f &= ~(ERROR|NEGATIVE|POSITIVE); 314*4887Schin if ((c = *s) == '-') 315*4887Schin { 316*4887Schin *f |= NEGATIVE; 317*4887Schin s++; 318*4887Schin } 319*4887Schin else if (c == '+') 320*4887Schin { 321*4887Schin *f |= POSITIVE; 322*4887Schin s++; 323*4887Schin } 324*4887Schin while (*s == '0' && isdigit(*(s + 1))) 325*4887Schin s++; 326*4887Schin errno = 0; 327*4887Schin number = strtonll(s, &t, NiL, 0); 328*4887Schin if (!o && t > s && *(t - 1) == 'l') 329*4887Schin t--; 330*4887Schin if (t == s) 331*4887Schin number = LINES; 332*4887Schin if (o && *t) 333*4887Schin { 334*4887Schin number = 0; 335*4887Schin *f |= ERROR; 336*4887Schin error(2, "-%c: %s: invalid numeric argument -- unknown suffix", o, s); 337*4887Schin } 338*4887Schin else if (errno) 339*4887Schin { 340*4887Schin *f |= ERROR; 341*4887Schin if (o) 342*4887Schin error(2, "-%c: %s: invalid numeric argument -- out of range", o, s); 343*4887Schin else 344*4887Schin error(2, "%s: invalid numeric argument -- out of range", s); 345*4887Schin } 346*4887Schin else 347*4887Schin { 348*4887Schin *f |= COUNT; 349*4887Schin if (c == '-') 350*4887Schin number = -number; 351*4887Schin } 352*4887Schin if (e) 353*4887Schin *e = t; 354*4887Schin return number; 355*4887Schin } 356*4887Schin 357*4887Schin int 358*4887Schin b_tail(int argc, char** argv, void* context) 359*4887Schin { 360*4887Schin static const char header_fmt[] = "\n==> %s <==\n"; 361*4887Schin 362*4887Schin register Sfio_t* ip; 363*4887Schin register int n; 364*4887Schin register int i; 365*4887Schin register int delim = '\n'; 366*4887Schin int flags = HEADERS; 367*4887Schin char* s; 368*4887Schin char* t; 369*4887Schin char* r; 370*4887Schin char* e; 371*4887Schin char* file; 372*4887Schin Sfoff_t offset; 373*4887Schin Sfoff_t number = LINES; 374*4887Schin unsigned long timeout = 0; 375*4887Schin struct stat st; 376*4887Schin const char* format = header_fmt+1; 377*4887Schin size_t z; 378*4887Schin Sfio_t* op; 379*4887Schin register Tail_t* fp; 380*4887Schin register Tail_t* pp; 381*4887Schin register Tail_t* hp; 382*4887Schin Tail_t* files; 383*4887Schin 384*4887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 385*4887Schin for (;;) 386*4887Schin { 387*4887Schin switch (n = optget(argv, usage)) 388*4887Schin { 389*4887Schin case 'c': 390*4887Schin delim = -1; 391*4887Schin if (opt_info.arg && *opt_info.arg=='f' && !*(opt_info.arg+1)) 392*4887Schin { 393*4887Schin flags |= FOLLOW; 394*4887Schin continue; 395*4887Schin } 396*4887Schin /*FALLTHROUGH*/ 397*4887Schin case 'n': 398*4887Schin case 'N': 399*4887Schin flags |= COUNT; 400*4887Schin if (s = opt_info.arg) 401*4887Schin number = num(s, &s, &flags, n); 402*4887Schin else 403*4887Schin { 404*4887Schin number = LINES; 405*4887Schin flags &= ~(ERROR|NEGATIVE|POSITIVE); 406*4887Schin s = ""; 407*4887Schin } 408*4887Schin if (n=='c' && *s=='f') 409*4887Schin { 410*4887Schin s++; 411*4887Schin flags |= FOLLOW; 412*4887Schin } 413*4887Schin if (flags & ERROR) 414*4887Schin continue; 415*4887Schin if (flags & (NEGATIVE|POSITIVE)) 416*4887Schin number = -number; 417*4887Schin if (opt_info.option[0]=='+') 418*4887Schin number = -number; 419*4887Schin continue; 420*4887Schin case 'f': 421*4887Schin flags |= FOLLOW; 422*4887Schin continue; 423*4887Schin case 'h': 424*4887Schin if (opt_info.num) 425*4887Schin flags |= HEADERS; 426*4887Schin else 427*4887Schin flags &= ~HEADERS; 428*4887Schin continue; 429*4887Schin case 'L': 430*4887Schin flags |= LOG; 431*4887Schin continue; 432*4887Schin case 'q': 433*4887Schin flags &= ~HEADERS; 434*4887Schin continue; 435*4887Schin case 'r': 436*4887Schin flags |= REVERSE; 437*4887Schin continue; 438*4887Schin case 's': 439*4887Schin flags |= SILENT; 440*4887Schin continue; 441*4887Schin case 't': 442*4887Schin flags |= TIMEOUT; 443*4887Schin timeout = strelapsed(opt_info.arg, &s, 1); 444*4887Schin if (*s) 445*4887Schin error(ERROR_exit(1), "%s: invalid elapsed time", opt_info.arg); 446*4887Schin continue; 447*4887Schin case 'v': 448*4887Schin flags |= VERBOSE; 449*4887Schin continue; 450*4887Schin case ':': 451*4887Schin /* handle old style arguments */ 452*4887Schin r = s = argv[opt_info.index]; 453*4887Schin number = num(s, &t, &flags, 0); 454*4887Schin for (;;) 455*4887Schin { 456*4887Schin switch (*t++) 457*4887Schin { 458*4887Schin case 0: 459*4887Schin opt_info.offset = t - r - 1; 460*4887Schin if (number) 461*4887Schin number = -number; 462*4887Schin break; 463*4887Schin case 'c': 464*4887Schin delim = -1; 465*4887Schin continue; 466*4887Schin case 'f': 467*4887Schin flags |= FOLLOW; 468*4887Schin continue; 469*4887Schin case 'l': 470*4887Schin delim = '\n'; 471*4887Schin continue; 472*4887Schin case 'r': 473*4887Schin flags |= REVERSE; 474*4887Schin continue; 475*4887Schin default: 476*4887Schin error(2, "%s: invalid suffix", t - 1); 477*4887Schin opt_info.offset = strlen(r); 478*4887Schin break; 479*4887Schin } 480*4887Schin break; 481*4887Schin } 482*4887Schin continue; 483*4887Schin case '?': 484*4887Schin error(ERROR_usage(2), "%s", opt_info.arg); 485*4887Schin break; 486*4887Schin } 487*4887Schin break; 488*4887Schin } 489*4887Schin argv += opt_info.index; 490*4887Schin if (!*argv) 491*4887Schin { 492*4887Schin flags &= ~HEADERS; 493*4887Schin if (fstat(0, &st)) 494*4887Schin error(ERROR_system(0), "/dev/stdin: cannot stat"); 495*4887Schin else if (FIFO(st.st_mode)) 496*4887Schin flags &= ~FOLLOW; 497*4887Schin } 498*4887Schin else if (!*(argv + 1)) 499*4887Schin flags &= ~HEADERS; 500*4887Schin if (flags & REVERSE) 501*4887Schin { 502*4887Schin if (delim < 0) 503*4887Schin error(2, "--reverse requires line mode"); 504*4887Schin else if (!(flags & COUNT)) 505*4887Schin number = 0; 506*4887Schin flags &= ~FOLLOW; 507*4887Schin } 508*4887Schin if ((flags & (FOLLOW|TIMEOUT)) == TIMEOUT) 509*4887Schin { 510*4887Schin flags &= ~TIMEOUT; 511*4887Schin timeout = 0; 512*4887Schin error(ERROR_warn(0), "--timeout ignored for --noforever"); 513*4887Schin } 514*4887Schin if ((flags & (LOG|TIMEOUT)) == LOG) 515*4887Schin { 516*4887Schin flags &= ~LOG; 517*4887Schin error(ERROR_warn(0), "--log ignored for --notimeout"); 518*4887Schin } 519*4887Schin if (error_info.errors) 520*4887Schin error(ERROR_usage(2), "%s", optusage(NiL)); 521*4887Schin if (flags & FOLLOW) 522*4887Schin { 523*4887Schin if (!(fp = (Tail_t*)stakalloc(argc * sizeof(Tail_t)))) 524*4887Schin error(ERROR_system(1), "out of space"); 525*4887Schin files = 0; 526*4887Schin s = *argv; 527*4887Schin do 528*4887Schin { 529*4887Schin fp->name = s; 530*4887Schin fp->sp = 0; 531*4887Schin if (!init(fp, number, delim, flags)) 532*4887Schin { 533*4887Schin fp->expire = timeout ? (NOW + timeout + 1) : 0; 534*4887Schin if (files) 535*4887Schin pp->next = fp; 536*4887Schin else 537*4887Schin files = fp; 538*4887Schin pp = fp; 539*4887Schin fp++; 540*4887Schin } 541*4887Schin } while (s && (s = *++argv)); 542*4887Schin if (!files) 543*4887Schin return error_info.errors != 0; 544*4887Schin pp->next = 0; 545*4887Schin hp = 0; 546*4887Schin for (;;) 547*4887Schin { 548*4887Schin if (sfsync(sfstdout)) 549*4887Schin error(ERROR_system(1), "write error"); 550*4887Schin sleep(1); 551*4887Schin n = 0; 552*4887Schin pp = 0; 553*4887Schin fp = files; 554*4887Schin while (fp) 555*4887Schin { 556*4887Schin if (fstat(sffileno(fp->sp), &st)) 557*4887Schin error(ERROR_system(0), "%s: cannot stat", fp->name); 558*4887Schin else if (st.st_size > fp->last) 559*4887Schin { 560*4887Schin n = 1; 561*4887Schin if (timeout) 562*4887Schin fp->expire = NOW + timeout; 563*4887Schin z = st.st_size - fp->last; 564*4887Schin i = 0; 565*4887Schin if ((s = sfreserve(fp->sp, z, SF_LOCKR)) || (z = sfvalue(fp->sp)) && (s = sfreserve(fp->sp, z, SF_LOCKR)) && (i = 1)) 566*4887Schin { 567*4887Schin r = 0; 568*4887Schin for (e = (t = s) + z; t < e; t++) 569*4887Schin if (*t == '\n') 570*4887Schin r = t; 571*4887Schin if (r || i && (r = e)) 572*4887Schin { 573*4887Schin if ((flags & (HEADERS|VERBOSE)) && hp != fp) 574*4887Schin { 575*4887Schin hp = fp; 576*4887Schin sfprintf(sfstdout, format, fp->name); 577*4887Schin format = header_fmt; 578*4887Schin } 579*4887Schin z = r - s + 1; 580*4887Schin fp->last += z; 581*4887Schin sfwrite(sfstdout, s, z); 582*4887Schin } 583*4887Schin else 584*4887Schin z = 0; 585*4887Schin sfread(fp->sp, s, z); 586*4887Schin } 587*4887Schin goto next; 588*4887Schin } 589*4887Schin else if (!timeout || fp->expire > NOW) 590*4887Schin goto next; 591*4887Schin else 592*4887Schin { 593*4887Schin if (flags & LOG) 594*4887Schin { 595*4887Schin i = 3; 596*4887Schin while (--i && stat(fp->name, &st)) 597*4887Schin sleep(1); 598*4887Schin if (i && (fp->dev != st.st_dev || fp->ino != st.st_ino) && !init(fp, 0, 0, flags)) 599*4887Schin { 600*4887Schin if (!(flags & SILENT)) 601*4887Schin error(ERROR_warn(0), "%s: log file change", fp->name); 602*4887Schin fp->expire = NOW + timeout; 603*4887Schin goto next; 604*4887Schin } 605*4887Schin } 606*4887Schin if (!(flags & SILENT)) 607*4887Schin error(ERROR_warn(0), "%s: %s timeout", fp->name, fmtelapsed(timeout, 1)); 608*4887Schin } 609*4887Schin if (fp->sp && fp->sp != sfstdin) 610*4887Schin sfclose(fp->sp); 611*4887Schin if (pp) 612*4887Schin pp = pp->next = fp->next; 613*4887Schin else if (!(files = files->next)) 614*4887Schin return error_info.errors != 0; 615*4887Schin fp = fp->next; 616*4887Schin continue; 617*4887Schin next: 618*4887Schin pp = fp; 619*4887Schin fp = fp->next; 620*4887Schin } 621*4887Schin } 622*4887Schin } 623*4887Schin else 624*4887Schin { 625*4887Schin if (file = *argv) 626*4887Schin argv++; 627*4887Schin do 628*4887Schin { 629*4887Schin if (!file || streq(file, "-")) 630*4887Schin { 631*4887Schin file = "/dev/stdin"; 632*4887Schin ip = sfstdin; 633*4887Schin } 634*4887Schin else if (!(ip = sfopen(NiL, file, "r"))) 635*4887Schin { 636*4887Schin error(ERROR_system(0), "%s: cannot open", file); 637*4887Schin continue; 638*4887Schin } 639*4887Schin if (flags & (HEADERS|VERBOSE)) 640*4887Schin sfprintf(sfstdout, format, file); 641*4887Schin format = header_fmt; 642*4887Schin if (number < 0 || !number && (flags & POSITIVE)) 643*4887Schin { 644*4887Schin sfset(ip, SF_SHARE, !(flags & FOLLOW)); 645*4887Schin if (number < -1) 646*4887Schin sfmove(ip, NiL, -number - 1, delim); 647*4887Schin if (flags & REVERSE) 648*4887Schin rev_line(ip, sfstdout, sfseek(ip, (Sfoff_t)0, SEEK_CUR)); 649*4887Schin else 650*4887Schin sfmove(ip, sfstdout, SF_UNBOUND, -1); 651*4887Schin } 652*4887Schin else 653*4887Schin { 654*4887Schin sfset(ip, SF_SHARE, 0); 655*4887Schin if ((offset = tailpos(ip, number, delim)) >= 0) 656*4887Schin { 657*4887Schin if (flags & REVERSE) 658*4887Schin rev_line(ip, sfstdout, offset); 659*4887Schin else 660*4887Schin { 661*4887Schin sfseek(ip, offset, SEEK_SET); 662*4887Schin sfmove(ip, sfstdout, SF_UNBOUND, -1); 663*4887Schin } 664*4887Schin } 665*4887Schin else 666*4887Schin { 667*4887Schin op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout; 668*4887Schin pipetail(ip, op, number, delim); 669*4887Schin if (flags & REVERSE) 670*4887Schin { 671*4887Schin sfseek(op, (Sfoff_t)0, SEEK_SET); 672*4887Schin rev_line(op, sfstdout, (Sfoff_t)0); 673*4887Schin sfclose(op); 674*4887Schin } 675*4887Schin flags = 0; 676*4887Schin } 677*4887Schin } 678*4887Schin if (ip != sfstdin) 679*4887Schin sfclose(ip); 680*4887Schin } while (file = *argv++); 681*4887Schin } 682*4887Schin return error_info.errors != 0; 683*4887Schin } 684