14887Schin /*********************************************************************** 24887Schin * * 34887Schin * This software is part of the ast package * 4*10898Sroland.mainz@nrubsig.org * Copyright (c) 1992-2009 AT&T Intellectual Property * 54887Schin * and is licensed under the * 64887Schin * Common Public License, Version 1.0 * 78462SApril.Chin@Sun.COM * by AT&T Intellectual Property * 84887Schin * * 94887Schin * A copy of the License is available at * 104887Schin * http://www.opensource.org/licenses/cpl1.0.txt * 114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 124887Schin * * 134887Schin * Information and Software Systems Research * 144887Schin * AT&T Research * 154887Schin * Florham Park NJ * 164887Schin * * 174887Schin * Glenn Fowler <gsf@research.att.com> * 184887Schin * David Korn <dgk@research.att.com> * 194887Schin * * 204887Schin ***********************************************************************/ 214887Schin #pragma prototyped 224887Schin 234887Schin /* 244887Schin * print the tail of one or more files 254887Schin * 264887Schin * David Korn 274887Schin * Glenn Fowler 284887Schin */ 294887Schin 304887Schin static const char usage[] = 31*10898Sroland.mainz@nrubsig.org "+[-?\n@(#)$Id: tail (AT&T Research) 2009-08-25 $\n]" 324887Schin USAGE_LICENSE 334887Schin "[+NAME?tail - output trailing portion of one or more files ]" 344887Schin "[+DESCRIPTION?\btail\b copies one or more input files to standard output " 354887Schin "starting at a designated point for each file. Copying starts " 364887Schin "at the point indicated by the options and is unlimited in size.]" 374887Schin "[+?By default a header of the form \b==> \b\afilename\a\b <==\b " 384887Schin "is output before all but the first file but this can be changed " 394887Schin "with the \b-q\b and \b-v\b options.]" 404887Schin "[+?If no \afile\a is given, or if the \afile\a is \b-\b, \btail\b " 414887Schin "copies from standard input. The start of the file is defined " 424887Schin "as the current offset.]" 434887Schin "[+?The option argument for \b-c\b can optionally be " 444887Schin "followed by one of the following characters to specify a different " 454887Schin "unit other than a single byte:]{" 464887Schin "[+b?512 bytes.]" 47*10898Sroland.mainz@nrubsig.org "[+k?1 KiB.]" 48*10898Sroland.mainz@nrubsig.org "[+m?1 MiB.]" 49*10898Sroland.mainz@nrubsig.org "[+g?1 GiB.]" 504887Schin "}" 514887Schin "[+?For backwards compatibility, \b-\b\anumber\a is equivalent to " 524887Schin "\b-n\b \anumber\a and \b+\b\anumber\a is equivalent to " 53*10898Sroland.mainz@nrubsig.org "\b-n -\b\anumber\a. \anumber\a may also have these option " 54*10898Sroland.mainz@nrubsig.org "suffixes: \bb c f g k l m r\b.]" 554887Schin 564887Schin "[n:lines]:[lines:=10?Copy \alines\a lines from each file. A negative value " 57*10898Sroland.mainz@nrubsig.org "for \alines\a indicates an offset from the end of the file.]" 58*10898Sroland.mainz@nrubsig.org "[b:blocks?Copy units of 512 bytes.]" 594887Schin "[c:bytes]:?[chars?Copy \achars\a bytes from each file. A negative value " 60*10898Sroland.mainz@nrubsig.org "for \achars\a indicates an offset from the end of the file.]" 614887Schin "[f:forever|follow?Loop forever trying to read more characters as the " 624887Schin "end of each file to copy new data. Ignored if reading from a pipe " 634887Schin "or fifo.]" 644887Schin "[h!:headers?Output filename headers.]" 65*10898Sroland.mainz@nrubsig.org "[l:lines?Copy units of lines. This is the default.]" 664887Schin "[L:log?When a \b--forever\b file times out via \b--timeout\b, verify that " 674887Schin "the curent file has not been renamed and replaced by another file " 684887Schin "of the same name (a common log file practice) before giving up on " 694887Schin "the file.]" 704887Schin "[q:quiet?Don't output filename headers. For GNU compatibility.]" 714887Schin "[r:reverse?Output lines in reverse order.]" 724887Schin "[s:silent?Don't warn about timeout expiration and log file changes.]" 734887Schin "[t:timeout?Stop checking after \atimeout\a elapses with no additional " 744887Schin "\b--forever\b output. A separate elapsed time is maintained for " 754887Schin "each file operand. There is no timeout by default. The default " 764887Schin "\atimeout\a unit is seconds. \atimeout\a may be a catenation of 1 " 774887Schin "or more integers, each followed by a 1 character suffix. The suffix " 784887Schin "may be omitted from the last integer, in which case it is " 794887Schin "interpreted as seconds. The supported suffixes are:]:[timeout]{" 804887Schin "[+s?seconds]" 814887Schin "[+m?minutes]" 824887Schin "[+h?hours]" 834887Schin "[+d?days]" 844887Schin "[+w?weeks]" 854887Schin "[+M?months]" 864887Schin "[+y?years]" 874887Schin "[+S?scores]" 884887Schin "}" 894887Schin "[v:verbose?Always ouput filename headers.]" 90*10898Sroland.mainz@nrubsig.org 914887Schin "\n" 924887Schin "\n[file ...]\n" 934887Schin "\n" 94*10898Sroland.mainz@nrubsig.org 954887Schin "[+EXIT STATUS?]{" 964887Schin "[+0?All files copied successfully.]" 974887Schin "[+>0?One or more files did not copy.]" 984887Schin "}" 994887Schin "[+SEE ALSO?\bcat\b(1), \bhead\b(1), \brev\b(1)]" 1004887Schin ; 1014887Schin 1024887Schin #include <cmd.h> 1034887Schin #include <ctype.h> 1044887Schin #include <ls.h> 1054887Schin #include <tm.h> 1064887Schin #include <rev.h> 1074887Schin 1084887Schin #define COUNT (1<<0) 1094887Schin #define ERROR (1<<1) 1104887Schin #define FOLLOW (1<<2) 1114887Schin #define HEADERS (1<<3) 112*10898Sroland.mainz@nrubsig.org #define LINES (1<<4) 113*10898Sroland.mainz@nrubsig.org #define LOG (1<<5) 114*10898Sroland.mainz@nrubsig.org #define NEGATIVE (1<<6) 115*10898Sroland.mainz@nrubsig.org #define POSITIVE (1<<7) 116*10898Sroland.mainz@nrubsig.org #define REVERSE (1<<8) 117*10898Sroland.mainz@nrubsig.org #define SILENT (1<<9) 118*10898Sroland.mainz@nrubsig.org #define TIMEOUT (1<<10) 119*10898Sroland.mainz@nrubsig.org #define VERBOSE (1<<11) 1204887Schin 1214887Schin #define NOW (unsigned long)time(NiL) 1224887Schin 123*10898Sroland.mainz@nrubsig.org #define DEFAULT 10 1244887Schin 1254887Schin #ifdef S_ISSOCK 1264887Schin #define FIFO(m) (S_ISFIFO(m)||S_ISSOCK(m)) 1274887Schin #else 1284887Schin #define FIFO(m) S_ISFIFO(m) 1294887Schin #endif 1304887Schin 1314887Schin struct Tail_s; typedef struct Tail_s Tail_t; 1324887Schin 1334887Schin struct Tail_s 1344887Schin { 1354887Schin Tail_t* next; 1364887Schin char* name; 1374887Schin Sfio_t* sp; 1384887Schin Sfoff_t last; 1394887Schin unsigned long expire; 1404887Schin long dev; 1414887Schin long ino; 142*10898Sroland.mainz@nrubsig.org int fifo; 1434887Schin }; 1444887Schin 145*10898Sroland.mainz@nrubsig.org static const char header_fmt[] = "\n==> %s <==\n"; 146*10898Sroland.mainz@nrubsig.org 1474887Schin /* 1484887Schin * if file is seekable, position file to tail location and return offset 1494887Schin * otherwise, return -1 1504887Schin */ 1514887Schin 1524887Schin static Sfoff_t 1534887Schin tailpos(register Sfio_t* fp, register Sfoff_t number, int delim) 1544887Schin { 1554887Schin register size_t n; 1564887Schin register Sfoff_t offset; 1574887Schin register Sfoff_t first; 1584887Schin register Sfoff_t last; 1594887Schin register char* s; 1604887Schin register char* t; 1614887Schin struct stat st; 1624887Schin 1634887Schin last = sfsize(fp); 1644887Schin if ((first = sfseek(fp, (Sfoff_t)0, SEEK_CUR)) < 0) 1654887Schin return last || fstat(sffileno(fp), &st) || st.st_size || FIFO(st.st_mode) ? -1 : 0; 1664887Schin if (delim < 0) 1674887Schin { 1684887Schin if ((offset = last - number) < first) 1694887Schin return first; 1704887Schin return offset; 1714887Schin } 1724887Schin for (;;) 1734887Schin { 174*10898Sroland.mainz@nrubsig.org if ((offset = last - SF_BUFSIZE) < first) 175*10898Sroland.mainz@nrubsig.org offset = first; 1764887Schin sfseek(fp, offset, SEEK_SET); 1774887Schin n = last - offset; 1784887Schin if (!(s = sfreserve(fp, n, SF_LOCKR))) 1794887Schin return -1; 1804887Schin t = s + n; 1814887Schin while (t > s) 1824887Schin if (*--t == delim && number-- <= 0) 1834887Schin { 1844887Schin sfread(fp, s, 0); 1854887Schin return offset + (t - s) + 1; 1864887Schin } 1874887Schin sfread(fp, s, 0); 1884887Schin if (offset == first) 1894887Schin break; 1904887Schin last = offset; 1914887Schin } 1924887Schin return first; 1934887Schin } 1944887Schin 1954887Schin /* 1964887Schin * this code handles tail from a pipe without any size limits 1974887Schin */ 1984887Schin 1994887Schin static void 2004887Schin pipetail(Sfio_t* infile, Sfio_t* outfile, Sfoff_t number, int delim) 2014887Schin { 2024887Schin register Sfio_t* out; 2034887Schin register Sfoff_t n; 2044887Schin register Sfoff_t nleft = number; 2054887Schin register size_t a = 2 * SF_BUFSIZE; 2064887Schin register int fno = 0; 2074887Schin Sfoff_t offset[2]; 2084887Schin Sfio_t* tmp[2]; 2094887Schin 2104887Schin if (delim < 0 && a > number) 2114887Schin a = number; 2124887Schin out = tmp[0] = sftmp(a); 2134887Schin tmp[1] = sftmp(a); 2144887Schin offset[0] = offset[1] = 0; 2154887Schin while ((n = sfmove(infile, out, number, delim)) > 0) 2164887Schin { 2174887Schin offset[fno] = sftell(out); 2184887Schin if ((nleft -= n) <= 0) 2194887Schin { 2204887Schin out = tmp[fno= !fno]; 2214887Schin sfseek(out, (Sfoff_t)0, SEEK_SET); 2224887Schin nleft = number; 2234887Schin } 2244887Schin } 2254887Schin if (nleft == number) 2264887Schin { 2274887Schin offset[fno] = 0; 2284887Schin fno= !fno; 2294887Schin } 2304887Schin sfseek(tmp[0], (Sfoff_t)0, SEEK_SET); 2314887Schin 2324887Schin /* 2334887Schin * see whether both files are needed 2344887Schin */ 2354887Schin 2364887Schin if (offset[fno]) 2374887Schin { 2384887Schin sfseek(tmp[1], (Sfoff_t)0, SEEK_SET); 2394887Schin if ((n = number - nleft) > 0) 2404887Schin sfmove(tmp[!fno], NiL, n, delim); 2414887Schin if ((n = offset[!fno] - sftell(tmp[!fno])) > 0) 2424887Schin sfmove(tmp[!fno], outfile, n, -1); 2434887Schin } 2444887Schin else 2454887Schin fno = !fno; 2464887Schin sfmove(tmp[fno], outfile, offset[fno], -1); 2474887Schin sfclose(tmp[0]); 2484887Schin sfclose(tmp[1]); 2494887Schin } 2504887Schin 2514887Schin /* 2524887Schin * (re)initialize a tail stream 2534887Schin */ 2544887Schin 2554887Schin static int 256*10898Sroland.mainz@nrubsig.org init(Tail_t* tp, Sfoff_t number, int delim, int flags, const char** format) 2574887Schin { 2584887Schin Sfoff_t offset; 259*10898Sroland.mainz@nrubsig.org Sfio_t* op; 2604887Schin struct stat st; 2614887Schin 262*10898Sroland.mainz@nrubsig.org tp->fifo = 0; 2634887Schin if (tp->sp) 2644887Schin { 2654887Schin offset = 0; 2664887Schin if (tp->sp == sfstdin) 2674887Schin tp->sp = 0; 2684887Schin } 2694887Schin else if (!number) 2704887Schin offset = 0; 2714887Schin else 2724887Schin offset = 1; 2734887Schin if (!tp->name || streq(tp->name, "-")) 2744887Schin { 2754887Schin tp->name = "/dev/stdin"; 2764887Schin tp->sp = sfstdin; 2774887Schin } 2784887Schin else if (!(tp->sp = sfopen(tp->sp, tp->name, "r"))) 2794887Schin { 2804887Schin error(ERROR_system(0), "%s: cannot open", tp->name); 2814887Schin return -1; 2824887Schin } 2834887Schin sfset(tp->sp, SF_SHARE, 0); 2844887Schin if (offset) 2854887Schin { 286*10898Sroland.mainz@nrubsig.org if (number < 0 || !number && (flags & POSITIVE)) 287*10898Sroland.mainz@nrubsig.org { 288*10898Sroland.mainz@nrubsig.org sfset(tp->sp, SF_SHARE, !(flags & FOLLOW)); 289*10898Sroland.mainz@nrubsig.org if (number < -1) 290*10898Sroland.mainz@nrubsig.org { 291*10898Sroland.mainz@nrubsig.org sfmove(tp->sp, NiL, -number - 1, delim); 292*10898Sroland.mainz@nrubsig.org offset = sfseek(tp->sp, (Sfoff_t)0, SEEK_CUR); 293*10898Sroland.mainz@nrubsig.org } 294*10898Sroland.mainz@nrubsig.org else 295*10898Sroland.mainz@nrubsig.org offset = 0; 296*10898Sroland.mainz@nrubsig.org } 297*10898Sroland.mainz@nrubsig.org else if ((offset = tailpos(tp->sp, number, delim)) >= 0) 298*10898Sroland.mainz@nrubsig.org sfseek(tp->sp, offset, SEEK_SET); 299*10898Sroland.mainz@nrubsig.org else if (fstat(sffileno(tp->sp), &st)) 300*10898Sroland.mainz@nrubsig.org { 301*10898Sroland.mainz@nrubsig.org error(ERROR_system(0), "%s: cannot stat", tp->name); 302*10898Sroland.mainz@nrubsig.org goto bad; 303*10898Sroland.mainz@nrubsig.org } 304*10898Sroland.mainz@nrubsig.org else if (!FIFO(st.st_mode)) 3054887Schin { 3064887Schin error(ERROR_SYSTEM|2, "%s: cannot position file to tail", tp->name); 3074887Schin goto bad; 3084887Schin } 309*10898Sroland.mainz@nrubsig.org else 310*10898Sroland.mainz@nrubsig.org { 311*10898Sroland.mainz@nrubsig.org tp->fifo = 1; 312*10898Sroland.mainz@nrubsig.org if (flags & (HEADERS|VERBOSE)) 313*10898Sroland.mainz@nrubsig.org { 314*10898Sroland.mainz@nrubsig.org sfprintf(sfstdout, *format, tp->name); 315*10898Sroland.mainz@nrubsig.org *format = header_fmt; 316*10898Sroland.mainz@nrubsig.org } 317*10898Sroland.mainz@nrubsig.org op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout; 318*10898Sroland.mainz@nrubsig.org pipetail(tp->sp ? tp->sp : sfstdin, op, number, delim); 319*10898Sroland.mainz@nrubsig.org if (flags & REVERSE) 320*10898Sroland.mainz@nrubsig.org { 321*10898Sroland.mainz@nrubsig.org sfseek(op, (Sfoff_t)0, SEEK_SET); 322*10898Sroland.mainz@nrubsig.org rev_line(op, sfstdout, (Sfoff_t)0); 323*10898Sroland.mainz@nrubsig.org sfclose(op); 324*10898Sroland.mainz@nrubsig.org } 325*10898Sroland.mainz@nrubsig.org } 3264887Schin } 3274887Schin tp->last = offset; 3284887Schin if (flags & LOG) 3294887Schin { 3304887Schin if (fstat(sffileno(tp->sp), &st)) 3314887Schin { 3324887Schin error(ERROR_system(0), "%s: cannot stat", tp->name); 3334887Schin goto bad; 3344887Schin } 3354887Schin tp->dev = st.st_dev; 3364887Schin tp->ino = st.st_ino; 3374887Schin } 3384887Schin return 0; 3394887Schin bad: 3404887Schin if (tp->sp != sfstdin) 3414887Schin sfclose(tp->sp); 3424887Schin tp->sp = 0; 3434887Schin return -1; 3444887Schin } 3454887Schin 3464887Schin /* 3474887Schin * convert number with validity diagnostics 3484887Schin */ 3494887Schin 3504887Schin static intmax_t 3514887Schin num(register const char* s, char** e, int* f, int o) 3524887Schin { 3534887Schin intmax_t number; 3544887Schin char* t; 3554887Schin int c; 3564887Schin 3574887Schin *f &= ~(ERROR|NEGATIVE|POSITIVE); 3584887Schin if ((c = *s) == '-') 3594887Schin { 3604887Schin *f |= NEGATIVE; 3614887Schin s++; 3624887Schin } 3634887Schin else if (c == '+') 3644887Schin { 3654887Schin *f |= POSITIVE; 3664887Schin s++; 3674887Schin } 3684887Schin while (*s == '0' && isdigit(*(s + 1))) 3694887Schin s++; 3704887Schin errno = 0; 3714887Schin number = strtonll(s, &t, NiL, 0); 3724887Schin if (t == s) 373*10898Sroland.mainz@nrubsig.org number = DEFAULT; 3744887Schin if (o && *t) 3754887Schin { 3764887Schin number = 0; 3774887Schin *f |= ERROR; 3784887Schin error(2, "-%c: %s: invalid numeric argument -- unknown suffix", o, s); 3794887Schin } 3804887Schin else if (errno) 3814887Schin { 3824887Schin *f |= ERROR; 3834887Schin if (o) 3844887Schin error(2, "-%c: %s: invalid numeric argument -- out of range", o, s); 3854887Schin else 3864887Schin error(2, "%s: invalid numeric argument -- out of range", s); 3874887Schin } 3884887Schin else 3894887Schin { 3904887Schin *f |= COUNT; 391*10898Sroland.mainz@nrubsig.org if (t > s && isalpha(*(t - 1))) 392*10898Sroland.mainz@nrubsig.org *f &= ~LINES; 3934887Schin if (c == '-') 3944887Schin number = -number; 3954887Schin } 3964887Schin if (e) 3974887Schin *e = t; 3984887Schin return number; 3994887Schin } 4004887Schin 4014887Schin int 4024887Schin b_tail(int argc, char** argv, void* context) 4034887Schin { 4044887Schin register Sfio_t* ip; 4054887Schin register int n; 4064887Schin register int i; 407*10898Sroland.mainz@nrubsig.org int delim; 408*10898Sroland.mainz@nrubsig.org int flags = HEADERS|LINES; 409*10898Sroland.mainz@nrubsig.org int blocks = 0; 4104887Schin char* s; 4114887Schin char* t; 4124887Schin char* r; 4134887Schin char* e; 4144887Schin char* file; 4154887Schin Sfoff_t offset; 416*10898Sroland.mainz@nrubsig.org Sfoff_t number = DEFAULT; 4174887Schin unsigned long timeout = 0; 4184887Schin struct stat st; 4194887Schin const char* format = header_fmt+1; 420*10898Sroland.mainz@nrubsig.org ssize_t z; 4214887Schin Sfio_t* op; 4224887Schin register Tail_t* fp; 4234887Schin register Tail_t* pp; 4244887Schin register Tail_t* hp; 4254887Schin Tail_t* files; 4264887Schin 4274887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0); 4284887Schin for (;;) 4294887Schin { 4304887Schin switch (n = optget(argv, usage)) 4314887Schin { 432*10898Sroland.mainz@nrubsig.org case 0: 433*10898Sroland.mainz@nrubsig.org if (!(flags & FOLLOW) && argv[opt_info.index] && (argv[opt_info.index][0] == '-' || argv[opt_info.index][0] == '+') && !argv[opt_info.index][1]) 434*10898Sroland.mainz@nrubsig.org { 435*10898Sroland.mainz@nrubsig.org number = argv[opt_info.index][0] == '-' ? 10 : -10; 436*10898Sroland.mainz@nrubsig.org flags |= LINES; 437*10898Sroland.mainz@nrubsig.org opt_info.index++; 438*10898Sroland.mainz@nrubsig.org continue; 439*10898Sroland.mainz@nrubsig.org } 440*10898Sroland.mainz@nrubsig.org break; 441*10898Sroland.mainz@nrubsig.org case 'b': 442*10898Sroland.mainz@nrubsig.org blocks = 512; 443*10898Sroland.mainz@nrubsig.org flags &= ~LINES; 444*10898Sroland.mainz@nrubsig.org if (opt_info.option[0] == '+') 445*10898Sroland.mainz@nrubsig.org number = -number; 446*10898Sroland.mainz@nrubsig.org continue; 4474887Schin case 'c': 448*10898Sroland.mainz@nrubsig.org flags &= ~LINES; 449*10898Sroland.mainz@nrubsig.org if (opt_info.arg == argv[opt_info.index - 1]) 4504887Schin { 451*10898Sroland.mainz@nrubsig.org strtol(opt_info.arg, &s, 10); 452*10898Sroland.mainz@nrubsig.org if (*s) 453*10898Sroland.mainz@nrubsig.org { 454*10898Sroland.mainz@nrubsig.org opt_info.index--; 455*10898Sroland.mainz@nrubsig.org t = ""; 456*10898Sroland.mainz@nrubsig.org goto suffix; 457*10898Sroland.mainz@nrubsig.org } 458*10898Sroland.mainz@nrubsig.org } 459*10898Sroland.mainz@nrubsig.org else if (opt_info.arg && isalpha(*opt_info.arg)) 460*10898Sroland.mainz@nrubsig.org { 461*10898Sroland.mainz@nrubsig.org t = opt_info.arg; 462*10898Sroland.mainz@nrubsig.org goto suffix; 4634887Schin } 4644887Schin /*FALLTHROUGH*/ 4654887Schin case 'n': 4664887Schin flags |= COUNT; 4674887Schin if (s = opt_info.arg) 4684887Schin number = num(s, &s, &flags, n); 4694887Schin else 4704887Schin { 471*10898Sroland.mainz@nrubsig.org number = DEFAULT; 4724887Schin flags &= ~(ERROR|NEGATIVE|POSITIVE); 4734887Schin s = ""; 4744887Schin } 475*10898Sroland.mainz@nrubsig.org if (n != 'n' && s && isalpha(*s)) 4764887Schin { 477*10898Sroland.mainz@nrubsig.org t = s; 478*10898Sroland.mainz@nrubsig.org goto suffix; 4794887Schin } 4804887Schin if (flags & ERROR) 4814887Schin continue; 4824887Schin if (flags & (NEGATIVE|POSITIVE)) 4834887Schin number = -number; 4844887Schin if (opt_info.option[0]=='+') 4854887Schin number = -number; 4864887Schin continue; 4874887Schin case 'f': 4884887Schin flags |= FOLLOW; 4894887Schin continue; 4904887Schin case 'h': 4914887Schin if (opt_info.num) 4924887Schin flags |= HEADERS; 4934887Schin else 4944887Schin flags &= ~HEADERS; 4954887Schin continue; 496*10898Sroland.mainz@nrubsig.org case 'l': 497*10898Sroland.mainz@nrubsig.org flags |= LINES; 498*10898Sroland.mainz@nrubsig.org if (opt_info.option[0] == '+') 499*10898Sroland.mainz@nrubsig.org number = -number; 500*10898Sroland.mainz@nrubsig.org continue; 5014887Schin case 'L': 5024887Schin flags |= LOG; 5034887Schin continue; 5044887Schin case 'q': 5054887Schin flags &= ~HEADERS; 5064887Schin continue; 5074887Schin case 'r': 5084887Schin flags |= REVERSE; 5094887Schin continue; 5104887Schin case 's': 5114887Schin flags |= SILENT; 5124887Schin continue; 5134887Schin case 't': 5144887Schin flags |= TIMEOUT; 5154887Schin timeout = strelapsed(opt_info.arg, &s, 1); 5164887Schin if (*s) 5174887Schin error(ERROR_exit(1), "%s: invalid elapsed time", opt_info.arg); 5184887Schin continue; 5194887Schin case 'v': 5204887Schin flags |= VERBOSE; 5214887Schin continue; 5224887Schin case ':': 5234887Schin /* handle old style arguments */ 524*10898Sroland.mainz@nrubsig.org if (!(r = argv[opt_info.index]) || !opt_info.offset) 525*10898Sroland.mainz@nrubsig.org { 526*10898Sroland.mainz@nrubsig.org error(2, "%s", opt_info.arg); 527*10898Sroland.mainz@nrubsig.org break; 528*10898Sroland.mainz@nrubsig.org } 529*10898Sroland.mainz@nrubsig.org s = r + opt_info.offset - 1; 530*10898Sroland.mainz@nrubsig.org if (i = *(s - 1) == '-' || *(s - 1) == '+') 531*10898Sroland.mainz@nrubsig.org s--; 532*10898Sroland.mainz@nrubsig.org if ((number = num(s, &t, &flags, 0)) && i) 533*10898Sroland.mainz@nrubsig.org number = -number; 534*10898Sroland.mainz@nrubsig.org goto compatibility; 535*10898Sroland.mainz@nrubsig.org suffix: 536*10898Sroland.mainz@nrubsig.org r = 0; 537*10898Sroland.mainz@nrubsig.org if (opt_info.option[0] == '+') 538*10898Sroland.mainz@nrubsig.org number = -number; 539*10898Sroland.mainz@nrubsig.org compatibility: 5404887Schin for (;;) 5414887Schin { 5424887Schin switch (*t++) 5434887Schin { 5444887Schin case 0: 545*10898Sroland.mainz@nrubsig.org if (r) 546*10898Sroland.mainz@nrubsig.org opt_info.offset = t - r - 1; 5474887Schin break; 5484887Schin case 'c': 549*10898Sroland.mainz@nrubsig.org flags &= ~LINES; 5504887Schin continue; 5514887Schin case 'f': 5524887Schin flags |= FOLLOW; 5534887Schin continue; 5544887Schin case 'l': 555*10898Sroland.mainz@nrubsig.org flags |= LINES; 5564887Schin continue; 5574887Schin case 'r': 5584887Schin flags |= REVERSE; 5594887Schin continue; 5604887Schin default: 5614887Schin error(2, "%s: invalid suffix", t - 1); 562*10898Sroland.mainz@nrubsig.org if (r) 563*10898Sroland.mainz@nrubsig.org opt_info.offset = strlen(r); 5644887Schin break; 5654887Schin } 5664887Schin break; 5674887Schin } 5684887Schin continue; 5694887Schin case '?': 5704887Schin error(ERROR_usage(2), "%s", opt_info.arg); 5714887Schin break; 5724887Schin } 5734887Schin break; 5744887Schin } 5754887Schin argv += opt_info.index; 5764887Schin if (!*argv) 5774887Schin { 5784887Schin flags &= ~HEADERS; 5794887Schin if (fstat(0, &st)) 5804887Schin error(ERROR_system(0), "/dev/stdin: cannot stat"); 5814887Schin else if (FIFO(st.st_mode)) 5824887Schin flags &= ~FOLLOW; 5834887Schin } 5844887Schin else if (!*(argv + 1)) 5854887Schin flags &= ~HEADERS; 586*10898Sroland.mainz@nrubsig.org delim = (flags & LINES) ? '\n' : -1; 587*10898Sroland.mainz@nrubsig.org if (blocks) 588*10898Sroland.mainz@nrubsig.org number *= blocks; 5894887Schin if (flags & REVERSE) 5904887Schin { 5914887Schin if (delim < 0) 5924887Schin error(2, "--reverse requires line mode"); 593*10898Sroland.mainz@nrubsig.org if (!(flags & COUNT)) 594*10898Sroland.mainz@nrubsig.org number = -1; 5954887Schin flags &= ~FOLLOW; 5964887Schin } 5974887Schin if ((flags & (FOLLOW|TIMEOUT)) == TIMEOUT) 5984887Schin { 5994887Schin flags &= ~TIMEOUT; 6004887Schin timeout = 0; 6014887Schin error(ERROR_warn(0), "--timeout ignored for --noforever"); 6024887Schin } 6034887Schin if ((flags & (LOG|TIMEOUT)) == LOG) 6044887Schin { 6054887Schin flags &= ~LOG; 6064887Schin error(ERROR_warn(0), "--log ignored for --notimeout"); 6074887Schin } 6084887Schin if (error_info.errors) 6094887Schin error(ERROR_usage(2), "%s", optusage(NiL)); 6104887Schin if (flags & FOLLOW) 6114887Schin { 6124887Schin if (!(fp = (Tail_t*)stakalloc(argc * sizeof(Tail_t)))) 6134887Schin error(ERROR_system(1), "out of space"); 6144887Schin files = 0; 6154887Schin s = *argv; 6164887Schin do 6174887Schin { 6184887Schin fp->name = s; 6194887Schin fp->sp = 0; 620*10898Sroland.mainz@nrubsig.org if (!init(fp, number, delim, flags, &format)) 6214887Schin { 6224887Schin fp->expire = timeout ? (NOW + timeout + 1) : 0; 6234887Schin if (files) 6244887Schin pp->next = fp; 6254887Schin else 6264887Schin files = fp; 6274887Schin pp = fp; 6284887Schin fp++; 6294887Schin } 6304887Schin } while (s && (s = *++argv)); 6314887Schin if (!files) 6324887Schin return error_info.errors != 0; 6334887Schin pp->next = 0; 6344887Schin hp = 0; 635*10898Sroland.mainz@nrubsig.org while (fp = files) 6364887Schin { 6374887Schin if (sfsync(sfstdout)) 6384887Schin error(ERROR_system(1), "write error"); 639*10898Sroland.mainz@nrubsig.org #if 0 6404887Schin sleep(1); 641*10898Sroland.mainz@nrubsig.org #else 642*10898Sroland.mainz@nrubsig.org { 643*10898Sroland.mainz@nrubsig.org struct timespec rqt = { 0L, 1000000000L/4L }; 644*10898Sroland.mainz@nrubsig.org (void)nanosleep(&rqt, NULL); 645*10898Sroland.mainz@nrubsig.org } 646*10898Sroland.mainz@nrubsig.org #endif 6474887Schin n = 0; 6484887Schin pp = 0; 6494887Schin while (fp) 6504887Schin { 6514887Schin if (fstat(sffileno(fp->sp), &st)) 6524887Schin error(ERROR_system(0), "%s: cannot stat", fp->name); 653*10898Sroland.mainz@nrubsig.org else if (st.st_size > fp->last || fp->fifo) 6544887Schin { 6554887Schin n = 1; 6564887Schin if (timeout) 6574887Schin fp->expire = NOW + timeout; 658*10898Sroland.mainz@nrubsig.org z = fp->fifo ? SF_UNBOUND : st.st_size - fp->last; 6594887Schin i = 0; 6604887Schin if ((s = sfreserve(fp->sp, z, SF_LOCKR)) || (z = sfvalue(fp->sp)) && (s = sfreserve(fp->sp, z, SF_LOCKR)) && (i = 1)) 6614887Schin { 662*10898Sroland.mainz@nrubsig.org if (fp->fifo) 663*10898Sroland.mainz@nrubsig.org z = sfvalue(fp->sp); 6644887Schin r = 0; 6654887Schin for (e = (t = s) + z; t < e; t++) 6664887Schin if (*t == '\n') 6674887Schin r = t; 6684887Schin if (r || i && (r = e)) 6694887Schin { 6704887Schin if ((flags & (HEADERS|VERBOSE)) && hp != fp) 6714887Schin { 6724887Schin hp = fp; 6734887Schin sfprintf(sfstdout, format, fp->name); 6744887Schin format = header_fmt; 6754887Schin } 6764887Schin z = r - s + 1; 6774887Schin fp->last += z; 6784887Schin sfwrite(sfstdout, s, z); 6794887Schin } 6804887Schin else 6814887Schin z = 0; 6824887Schin sfread(fp->sp, s, z); 6834887Schin } 6844887Schin goto next; 6854887Schin } 6864887Schin else if (!timeout || fp->expire > NOW) 6874887Schin goto next; 6884887Schin else 6894887Schin { 6904887Schin if (flags & LOG) 6914887Schin { 6924887Schin i = 3; 6934887Schin while (--i && stat(fp->name, &st)) 6944887Schin sleep(1); 695*10898Sroland.mainz@nrubsig.org if (i && (fp->dev != st.st_dev || fp->ino != st.st_ino) && !init(fp, 0, 0, flags, &format)) 6964887Schin { 6974887Schin if (!(flags & SILENT)) 6984887Schin error(ERROR_warn(0), "%s: log file change", fp->name); 6994887Schin fp->expire = NOW + timeout; 7004887Schin goto next; 7014887Schin } 7024887Schin } 7034887Schin if (!(flags & SILENT)) 7044887Schin error(ERROR_warn(0), "%s: %s timeout", fp->name, fmtelapsed(timeout, 1)); 7054887Schin } 7064887Schin if (fp->sp && fp->sp != sfstdin) 7074887Schin sfclose(fp->sp); 7084887Schin if (pp) 7094887Schin pp = pp->next = fp->next; 7104887Schin else if (!(files = files->next)) 7114887Schin return error_info.errors != 0; 7124887Schin fp = fp->next; 7134887Schin continue; 7144887Schin next: 7154887Schin pp = fp; 7164887Schin fp = fp->next; 7174887Schin } 7184887Schin } 7194887Schin } 7204887Schin else 7214887Schin { 7224887Schin if (file = *argv) 7234887Schin argv++; 7244887Schin do 7254887Schin { 7264887Schin if (!file || streq(file, "-")) 7274887Schin { 7284887Schin file = "/dev/stdin"; 7294887Schin ip = sfstdin; 7304887Schin } 7314887Schin else if (!(ip = sfopen(NiL, file, "r"))) 7324887Schin { 7334887Schin error(ERROR_system(0), "%s: cannot open", file); 7344887Schin continue; 7354887Schin } 7364887Schin if (flags & (HEADERS|VERBOSE)) 737*10898Sroland.mainz@nrubsig.org { 7384887Schin sfprintf(sfstdout, format, file); 739*10898Sroland.mainz@nrubsig.org format = header_fmt; 740*10898Sroland.mainz@nrubsig.org } 7414887Schin if (number < 0 || !number && (flags & POSITIVE)) 7424887Schin { 743*10898Sroland.mainz@nrubsig.org sfset(ip, SF_SHARE, 1); 7444887Schin if (number < -1) 7454887Schin sfmove(ip, NiL, -number - 1, delim); 7464887Schin if (flags & REVERSE) 7474887Schin rev_line(ip, sfstdout, sfseek(ip, (Sfoff_t)0, SEEK_CUR)); 7484887Schin else 7494887Schin sfmove(ip, sfstdout, SF_UNBOUND, -1); 7504887Schin } 7514887Schin else 7524887Schin { 7534887Schin sfset(ip, SF_SHARE, 0); 7544887Schin if ((offset = tailpos(ip, number, delim)) >= 0) 7554887Schin { 7564887Schin if (flags & REVERSE) 7574887Schin rev_line(ip, sfstdout, offset); 7584887Schin else 7594887Schin { 7604887Schin sfseek(ip, offset, SEEK_SET); 7614887Schin sfmove(ip, sfstdout, SF_UNBOUND, -1); 7624887Schin } 7634887Schin } 7644887Schin else 7654887Schin { 7664887Schin op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout; 7674887Schin pipetail(ip, op, number, delim); 7684887Schin if (flags & REVERSE) 7694887Schin { 7704887Schin sfseek(op, (Sfoff_t)0, SEEK_SET); 7714887Schin rev_line(op, sfstdout, (Sfoff_t)0); 7724887Schin sfclose(op); 7734887Schin } 7744887Schin flags = 0; 7754887Schin } 7764887Schin } 7774887Schin if (ip != sfstdin) 7784887Schin sfclose(ip); 7794887Schin } while (file = *argv++); 7804887Schin } 7814887Schin return error_info.errors != 0; 7824887Schin } 783