14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1992-2010 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*12068SRoger.Faulkner@Oracle.COM "+[-?\n@(#)$Id: tail (AT&T Research) 2010-03-23 $\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.]"
4710898Sroland.mainz@nrubsig.org "[+k?1 KiB.]"
4810898Sroland.mainz@nrubsig.org "[+m?1 MiB.]"
4910898Sroland.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 "
5310898Sroland.mainz@nrubsig.org "\b-n -\b\anumber\a. \anumber\a may also have these option "
5410898Sroland.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 "
5710898Sroland.mainz@nrubsig.org "for \alines\a indicates an offset from the end of the file.]"
5810898Sroland.mainz@nrubsig.org "[b:blocks?Copy units of 512 bytes.]"
594887Schin "[c:bytes]:?[chars?Copy \achars\a bytes from each file. A negative value "
6010898Sroland.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.]"
6510898Sroland.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.]"
9010898Sroland.mainz@nrubsig.org
914887Schin "\n"
924887Schin "\n[file ...]\n"
934887Schin "\n"
9410898Sroland.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)
11210898Sroland.mainz@nrubsig.org #define LINES (1<<4)
11310898Sroland.mainz@nrubsig.org #define LOG (1<<5)
11410898Sroland.mainz@nrubsig.org #define NEGATIVE (1<<6)
11510898Sroland.mainz@nrubsig.org #define POSITIVE (1<<7)
11610898Sroland.mainz@nrubsig.org #define REVERSE (1<<8)
11710898Sroland.mainz@nrubsig.org #define SILENT (1<<9)
11810898Sroland.mainz@nrubsig.org #define TIMEOUT (1<<10)
11910898Sroland.mainz@nrubsig.org #define VERBOSE (1<<11)
1204887Schin
1214887Schin #define NOW (unsigned long)time(NiL)
1224887Schin
12310898Sroland.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;
138*12068SRoger.Faulkner@Oracle.COM Sfoff_t cur;
139*12068SRoger.Faulkner@Oracle.COM Sfoff_t end;
1404887Schin unsigned long expire;
1414887Schin long dev;
1424887Schin long ino;
14310898Sroland.mainz@nrubsig.org int fifo;
1444887Schin };
1454887Schin
14610898Sroland.mainz@nrubsig.org static const char header_fmt[] = "\n==> %s <==\n";
14710898Sroland.mainz@nrubsig.org
1484887Schin /*
1494887Schin * if file is seekable, position file to tail location and return offset
1504887Schin * otherwise, return -1
1514887Schin */
1524887Schin
1534887Schin static Sfoff_t
tailpos(register Sfio_t * fp,register Sfoff_t number,int delim)1544887Schin tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
1554887Schin {
1564887Schin register size_t n;
1574887Schin register Sfoff_t offset;
1584887Schin register Sfoff_t first;
1594887Schin register Sfoff_t last;
1604887Schin register char* s;
1614887Schin register char* t;
1624887Schin struct stat st;
1634887Schin
1644887Schin last = sfsize(fp);
1654887Schin if ((first = sfseek(fp, (Sfoff_t)0, SEEK_CUR)) < 0)
1664887Schin return last || fstat(sffileno(fp), &st) || st.st_size || FIFO(st.st_mode) ? -1 : 0;
1674887Schin if (delim < 0)
1684887Schin {
1694887Schin if ((offset = last - number) < first)
1704887Schin return first;
1714887Schin return offset;
1724887Schin }
1734887Schin for (;;)
1744887Schin {
17510898Sroland.mainz@nrubsig.org if ((offset = last - SF_BUFSIZE) < first)
17610898Sroland.mainz@nrubsig.org offset = first;
1774887Schin sfseek(fp, offset, SEEK_SET);
1784887Schin n = last - offset;
1794887Schin if (!(s = sfreserve(fp, n, SF_LOCKR)))
1804887Schin return -1;
1814887Schin t = s + n;
1824887Schin while (t > s)
1834887Schin if (*--t == delim && number-- <= 0)
1844887Schin {
1854887Schin sfread(fp, s, 0);
1864887Schin return offset + (t - s) + 1;
1874887Schin }
1884887Schin sfread(fp, s, 0);
1894887Schin if (offset == first)
1904887Schin break;
1914887Schin last = offset;
1924887Schin }
1934887Schin return first;
1944887Schin }
1954887Schin
1964887Schin /*
1974887Schin * this code handles tail from a pipe without any size limits
1984887Schin */
1994887Schin
2004887Schin static void
pipetail(Sfio_t * infile,Sfio_t * outfile,Sfoff_t number,int delim)2014887Schin pipetail(Sfio_t* infile, Sfio_t* outfile, Sfoff_t number, int delim)
2024887Schin {
2034887Schin register Sfio_t* out;
2044887Schin register Sfoff_t n;
2054887Schin register Sfoff_t nleft = number;
2064887Schin register size_t a = 2 * SF_BUFSIZE;
2074887Schin register int fno = 0;
2084887Schin Sfoff_t offset[2];
2094887Schin Sfio_t* tmp[2];
2104887Schin
2114887Schin if (delim < 0 && a > number)
2124887Schin a = number;
2134887Schin out = tmp[0] = sftmp(a);
2144887Schin tmp[1] = sftmp(a);
2154887Schin offset[0] = offset[1] = 0;
2164887Schin while ((n = sfmove(infile, out, number, delim)) > 0)
2174887Schin {
2184887Schin offset[fno] = sftell(out);
2194887Schin if ((nleft -= n) <= 0)
2204887Schin {
2214887Schin out = tmp[fno= !fno];
2224887Schin sfseek(out, (Sfoff_t)0, SEEK_SET);
2234887Schin nleft = number;
2244887Schin }
2254887Schin }
2264887Schin if (nleft == number)
2274887Schin {
2284887Schin offset[fno] = 0;
2294887Schin fno= !fno;
2304887Schin }
2314887Schin sfseek(tmp[0], (Sfoff_t)0, SEEK_SET);
2324887Schin
2334887Schin /*
2344887Schin * see whether both files are needed
2354887Schin */
2364887Schin
2374887Schin if (offset[fno])
2384887Schin {
2394887Schin sfseek(tmp[1], (Sfoff_t)0, SEEK_SET);
2404887Schin if ((n = number - nleft) > 0)
2414887Schin sfmove(tmp[!fno], NiL, n, delim);
2424887Schin if ((n = offset[!fno] - sftell(tmp[!fno])) > 0)
2434887Schin sfmove(tmp[!fno], outfile, n, -1);
2444887Schin }
2454887Schin else
2464887Schin fno = !fno;
2474887Schin sfmove(tmp[fno], outfile, offset[fno], -1);
2484887Schin sfclose(tmp[0]);
2494887Schin sfclose(tmp[1]);
2504887Schin }
2514887Schin
2524887Schin /*
2534887Schin * (re)initialize a tail stream
2544887Schin */
2554887Schin
2564887Schin static int
init(Tail_t * tp,Sfoff_t number,int delim,int flags,const char ** format)25710898Sroland.mainz@nrubsig.org init(Tail_t* tp, Sfoff_t number, int delim, int flags, const char** format)
2584887Schin {
2594887Schin Sfoff_t offset;
26010898Sroland.mainz@nrubsig.org Sfio_t* op;
2614887Schin struct stat st;
2624887Schin
26310898Sroland.mainz@nrubsig.org tp->fifo = 0;
2644887Schin if (tp->sp)
2654887Schin {
2664887Schin offset = 0;
2674887Schin if (tp->sp == sfstdin)
2684887Schin tp->sp = 0;
2694887Schin }
2704887Schin else if (!number)
2714887Schin offset = 0;
2724887Schin else
2734887Schin offset = 1;
2744887Schin if (!tp->name || streq(tp->name, "-"))
2754887Schin {
2764887Schin tp->name = "/dev/stdin";
2774887Schin tp->sp = sfstdin;
2784887Schin }
2794887Schin else if (!(tp->sp = sfopen(tp->sp, tp->name, "r")))
2804887Schin {
2814887Schin error(ERROR_system(0), "%s: cannot open", tp->name);
2824887Schin return -1;
2834887Schin }
2844887Schin sfset(tp->sp, SF_SHARE, 0);
2854887Schin if (offset)
2864887Schin {
28710898Sroland.mainz@nrubsig.org if (number < 0 || !number && (flags & POSITIVE))
28810898Sroland.mainz@nrubsig.org {
28910898Sroland.mainz@nrubsig.org sfset(tp->sp, SF_SHARE, !(flags & FOLLOW));
29010898Sroland.mainz@nrubsig.org if (number < -1)
29110898Sroland.mainz@nrubsig.org {
29210898Sroland.mainz@nrubsig.org sfmove(tp->sp, NiL, -number - 1, delim);
29310898Sroland.mainz@nrubsig.org offset = sfseek(tp->sp, (Sfoff_t)0, SEEK_CUR);
29410898Sroland.mainz@nrubsig.org }
29510898Sroland.mainz@nrubsig.org else
29610898Sroland.mainz@nrubsig.org offset = 0;
29710898Sroland.mainz@nrubsig.org }
29810898Sroland.mainz@nrubsig.org else if ((offset = tailpos(tp->sp, number, delim)) >= 0)
29910898Sroland.mainz@nrubsig.org sfseek(tp->sp, offset, SEEK_SET);
30010898Sroland.mainz@nrubsig.org else if (fstat(sffileno(tp->sp), &st))
30110898Sroland.mainz@nrubsig.org {
30210898Sroland.mainz@nrubsig.org error(ERROR_system(0), "%s: cannot stat", tp->name);
30310898Sroland.mainz@nrubsig.org goto bad;
30410898Sroland.mainz@nrubsig.org }
30510898Sroland.mainz@nrubsig.org else if (!FIFO(st.st_mode))
3064887Schin {
3074887Schin error(ERROR_SYSTEM|2, "%s: cannot position file to tail", tp->name);
3084887Schin goto bad;
3094887Schin }
31010898Sroland.mainz@nrubsig.org else
31110898Sroland.mainz@nrubsig.org {
31210898Sroland.mainz@nrubsig.org tp->fifo = 1;
31310898Sroland.mainz@nrubsig.org if (flags & (HEADERS|VERBOSE))
31410898Sroland.mainz@nrubsig.org {
31510898Sroland.mainz@nrubsig.org sfprintf(sfstdout, *format, tp->name);
31610898Sroland.mainz@nrubsig.org *format = header_fmt;
31710898Sroland.mainz@nrubsig.org }
31810898Sroland.mainz@nrubsig.org op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout;
31910898Sroland.mainz@nrubsig.org pipetail(tp->sp ? tp->sp : sfstdin, op, number, delim);
32010898Sroland.mainz@nrubsig.org if (flags & REVERSE)
32110898Sroland.mainz@nrubsig.org {
32210898Sroland.mainz@nrubsig.org sfseek(op, (Sfoff_t)0, SEEK_SET);
32310898Sroland.mainz@nrubsig.org rev_line(op, sfstdout, (Sfoff_t)0);
32410898Sroland.mainz@nrubsig.org sfclose(op);
32510898Sroland.mainz@nrubsig.org }
32610898Sroland.mainz@nrubsig.org }
3274887Schin }
328*12068SRoger.Faulkner@Oracle.COM tp->cur = tp->end = offset;
3294887Schin if (flags & LOG)
3304887Schin {
3314887Schin if (fstat(sffileno(tp->sp), &st))
3324887Schin {
3334887Schin error(ERROR_system(0), "%s: cannot stat", tp->name);
3344887Schin goto bad;
3354887Schin }
3364887Schin tp->dev = st.st_dev;
3374887Schin tp->ino = st.st_ino;
3384887Schin }
3394887Schin return 0;
3404887Schin bad:
3414887Schin if (tp->sp != sfstdin)
3424887Schin sfclose(tp->sp);
3434887Schin tp->sp = 0;
3444887Schin return -1;
3454887Schin }
3464887Schin
3474887Schin /*
3484887Schin * convert number with validity diagnostics
3494887Schin */
3504887Schin
3514887Schin static intmax_t
num(register const char * s,char ** e,int * f,int o)3524887Schin num(register const char* s, char** e, int* f, int o)
3534887Schin {
3544887Schin intmax_t number;
3554887Schin char* t;
3564887Schin int c;
3574887Schin
3584887Schin *f &= ~(ERROR|NEGATIVE|POSITIVE);
3594887Schin if ((c = *s) == '-')
3604887Schin {
3614887Schin *f |= NEGATIVE;
3624887Schin s++;
3634887Schin }
3644887Schin else if (c == '+')
3654887Schin {
3664887Schin *f |= POSITIVE;
3674887Schin s++;
3684887Schin }
3694887Schin while (*s == '0' && isdigit(*(s + 1)))
3704887Schin s++;
3714887Schin errno = 0;
3724887Schin number = strtonll(s, &t, NiL, 0);
3734887Schin if (t == s)
37410898Sroland.mainz@nrubsig.org number = DEFAULT;
3754887Schin if (o && *t)
3764887Schin {
3774887Schin number = 0;
3784887Schin *f |= ERROR;
3794887Schin error(2, "-%c: %s: invalid numeric argument -- unknown suffix", o, s);
3804887Schin }
3814887Schin else if (errno)
3824887Schin {
3834887Schin *f |= ERROR;
3844887Schin if (o)
3854887Schin error(2, "-%c: %s: invalid numeric argument -- out of range", o, s);
3864887Schin else
3874887Schin error(2, "%s: invalid numeric argument -- out of range", s);
3884887Schin }
3894887Schin else
3904887Schin {
3914887Schin *f |= COUNT;
39210898Sroland.mainz@nrubsig.org if (t > s && isalpha(*(t - 1)))
39310898Sroland.mainz@nrubsig.org *f &= ~LINES;
3944887Schin if (c == '-')
3954887Schin number = -number;
3964887Schin }
3974887Schin if (e)
3984887Schin *e = t;
3994887Schin return number;
4004887Schin }
4014887Schin
4024887Schin int
b_tail(int argc,char ** argv,void * context)4034887Schin b_tail(int argc, char** argv, void* context)
4044887Schin {
4054887Schin register Sfio_t* ip;
4064887Schin register int n;
4074887Schin register int i;
40810898Sroland.mainz@nrubsig.org int delim;
40910898Sroland.mainz@nrubsig.org int flags = HEADERS|LINES;
41010898Sroland.mainz@nrubsig.org int blocks = 0;
4114887Schin char* s;
4124887Schin char* t;
4134887Schin char* r;
4144887Schin char* file;
4154887Schin Sfoff_t offset;
41610898Sroland.mainz@nrubsig.org Sfoff_t number = DEFAULT;
4174887Schin unsigned long timeout = 0;
4184887Schin struct stat st;
4194887Schin const char* format = header_fmt+1;
42010898Sroland.mainz@nrubsig.org ssize_t z;
421*12068SRoger.Faulkner@Oracle.COM ssize_t w;
4224887Schin Sfio_t* op;
4234887Schin register Tail_t* fp;
4244887Schin register Tail_t* pp;
4254887Schin register Tail_t* hp;
4264887Schin Tail_t* files;
4274887Schin
4284887Schin cmdinit(argc, argv, context, ERROR_CATALOG, 0);
4294887Schin for (;;)
4304887Schin {
4314887Schin switch (n = optget(argv, usage))
4324887Schin {
43310898Sroland.mainz@nrubsig.org case 0:
43410898Sroland.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])
43510898Sroland.mainz@nrubsig.org {
43610898Sroland.mainz@nrubsig.org number = argv[opt_info.index][0] == '-' ? 10 : -10;
43710898Sroland.mainz@nrubsig.org flags |= LINES;
43810898Sroland.mainz@nrubsig.org opt_info.index++;
43910898Sroland.mainz@nrubsig.org continue;
44010898Sroland.mainz@nrubsig.org }
44110898Sroland.mainz@nrubsig.org break;
44210898Sroland.mainz@nrubsig.org case 'b':
44310898Sroland.mainz@nrubsig.org blocks = 512;
44410898Sroland.mainz@nrubsig.org flags &= ~LINES;
44510898Sroland.mainz@nrubsig.org if (opt_info.option[0] == '+')
44610898Sroland.mainz@nrubsig.org number = -number;
44710898Sroland.mainz@nrubsig.org continue;
4484887Schin case 'c':
44910898Sroland.mainz@nrubsig.org flags &= ~LINES;
45010898Sroland.mainz@nrubsig.org if (opt_info.arg == argv[opt_info.index - 1])
4514887Schin {
45210898Sroland.mainz@nrubsig.org strtol(opt_info.arg, &s, 10);
45310898Sroland.mainz@nrubsig.org if (*s)
45410898Sroland.mainz@nrubsig.org {
45510898Sroland.mainz@nrubsig.org opt_info.index--;
45610898Sroland.mainz@nrubsig.org t = "";
45710898Sroland.mainz@nrubsig.org goto suffix;
45810898Sroland.mainz@nrubsig.org }
45910898Sroland.mainz@nrubsig.org }
46010898Sroland.mainz@nrubsig.org else if (opt_info.arg && isalpha(*opt_info.arg))
46110898Sroland.mainz@nrubsig.org {
46210898Sroland.mainz@nrubsig.org t = opt_info.arg;
46310898Sroland.mainz@nrubsig.org goto suffix;
4644887Schin }
4654887Schin /*FALLTHROUGH*/
4664887Schin case 'n':
4674887Schin flags |= COUNT;
4684887Schin if (s = opt_info.arg)
4694887Schin number = num(s, &s, &flags, n);
4704887Schin else
4714887Schin {
47210898Sroland.mainz@nrubsig.org number = DEFAULT;
4734887Schin flags &= ~(ERROR|NEGATIVE|POSITIVE);
4744887Schin s = "";
4754887Schin }
47610898Sroland.mainz@nrubsig.org if (n != 'n' && s && isalpha(*s))
4774887Schin {
47810898Sroland.mainz@nrubsig.org t = s;
47910898Sroland.mainz@nrubsig.org goto suffix;
4804887Schin }
4814887Schin if (flags & ERROR)
4824887Schin continue;
4834887Schin if (flags & (NEGATIVE|POSITIVE))
4844887Schin number = -number;
4854887Schin if (opt_info.option[0]=='+')
4864887Schin number = -number;
4874887Schin continue;
4884887Schin case 'f':
4894887Schin flags |= FOLLOW;
4904887Schin continue;
4914887Schin case 'h':
4924887Schin if (opt_info.num)
4934887Schin flags |= HEADERS;
4944887Schin else
4954887Schin flags &= ~HEADERS;
4964887Schin continue;
49710898Sroland.mainz@nrubsig.org case 'l':
49810898Sroland.mainz@nrubsig.org flags |= LINES;
49910898Sroland.mainz@nrubsig.org if (opt_info.option[0] == '+')
50010898Sroland.mainz@nrubsig.org number = -number;
50110898Sroland.mainz@nrubsig.org continue;
5024887Schin case 'L':
5034887Schin flags |= LOG;
5044887Schin continue;
5054887Schin case 'q':
5064887Schin flags &= ~HEADERS;
5074887Schin continue;
5084887Schin case 'r':
5094887Schin flags |= REVERSE;
5104887Schin continue;
5114887Schin case 's':
5124887Schin flags |= SILENT;
5134887Schin continue;
5144887Schin case 't':
5154887Schin flags |= TIMEOUT;
5164887Schin timeout = strelapsed(opt_info.arg, &s, 1);
5174887Schin if (*s)
518*12068SRoger.Faulkner@Oracle.COM error(ERROR_exit(1), "%s: invalid elapsed time [%s]", opt_info.arg, s);
5194887Schin continue;
5204887Schin case 'v':
5214887Schin flags |= VERBOSE;
5224887Schin continue;
5234887Schin case ':':
5244887Schin /* handle old style arguments */
52510898Sroland.mainz@nrubsig.org if (!(r = argv[opt_info.index]) || !opt_info.offset)
52610898Sroland.mainz@nrubsig.org {
52710898Sroland.mainz@nrubsig.org error(2, "%s", opt_info.arg);
52810898Sroland.mainz@nrubsig.org break;
52910898Sroland.mainz@nrubsig.org }
53010898Sroland.mainz@nrubsig.org s = r + opt_info.offset - 1;
53110898Sroland.mainz@nrubsig.org if (i = *(s - 1) == '-' || *(s - 1) == '+')
53210898Sroland.mainz@nrubsig.org s--;
53310898Sroland.mainz@nrubsig.org if ((number = num(s, &t, &flags, 0)) && i)
53410898Sroland.mainz@nrubsig.org number = -number;
53510898Sroland.mainz@nrubsig.org goto compatibility;
53610898Sroland.mainz@nrubsig.org suffix:
53710898Sroland.mainz@nrubsig.org r = 0;
53810898Sroland.mainz@nrubsig.org if (opt_info.option[0] == '+')
53910898Sroland.mainz@nrubsig.org number = -number;
54010898Sroland.mainz@nrubsig.org compatibility:
5414887Schin for (;;)
5424887Schin {
5434887Schin switch (*t++)
5444887Schin {
5454887Schin case 0:
54610898Sroland.mainz@nrubsig.org if (r)
54710898Sroland.mainz@nrubsig.org opt_info.offset = t - r - 1;
5484887Schin break;
5494887Schin case 'c':
55010898Sroland.mainz@nrubsig.org flags &= ~LINES;
5514887Schin continue;
5524887Schin case 'f':
5534887Schin flags |= FOLLOW;
5544887Schin continue;
5554887Schin case 'l':
55610898Sroland.mainz@nrubsig.org flags |= LINES;
5574887Schin continue;
5584887Schin case 'r':
5594887Schin flags |= REVERSE;
5604887Schin continue;
5614887Schin default:
5624887Schin error(2, "%s: invalid suffix", t - 1);
56310898Sroland.mainz@nrubsig.org if (r)
56410898Sroland.mainz@nrubsig.org opt_info.offset = strlen(r);
5654887Schin break;
5664887Schin }
5674887Schin break;
5684887Schin }
5694887Schin continue;
5704887Schin case '?':
5714887Schin error(ERROR_usage(2), "%s", opt_info.arg);
5724887Schin break;
5734887Schin }
5744887Schin break;
5754887Schin }
5764887Schin argv += opt_info.index;
5774887Schin if (!*argv)
5784887Schin {
5794887Schin flags &= ~HEADERS;
5804887Schin if (fstat(0, &st))
5814887Schin error(ERROR_system(0), "/dev/stdin: cannot stat");
5824887Schin else if (FIFO(st.st_mode))
5834887Schin flags &= ~FOLLOW;
5844887Schin }
5854887Schin else if (!*(argv + 1))
5864887Schin flags &= ~HEADERS;
58710898Sroland.mainz@nrubsig.org delim = (flags & LINES) ? '\n' : -1;
58810898Sroland.mainz@nrubsig.org if (blocks)
58910898Sroland.mainz@nrubsig.org number *= blocks;
5904887Schin if (flags & REVERSE)
5914887Schin {
5924887Schin if (delim < 0)
5934887Schin error(2, "--reverse requires line mode");
59410898Sroland.mainz@nrubsig.org if (!(flags & COUNT))
59510898Sroland.mainz@nrubsig.org number = -1;
5964887Schin flags &= ~FOLLOW;
5974887Schin }
5984887Schin if ((flags & (FOLLOW|TIMEOUT)) == TIMEOUT)
5994887Schin {
6004887Schin flags &= ~TIMEOUT;
6014887Schin timeout = 0;
6024887Schin error(ERROR_warn(0), "--timeout ignored for --noforever");
6034887Schin }
6044887Schin if ((flags & (LOG|TIMEOUT)) == LOG)
6054887Schin {
6064887Schin flags &= ~LOG;
6074887Schin error(ERROR_warn(0), "--log ignored for --notimeout");
6084887Schin }
6094887Schin if (error_info.errors)
6104887Schin error(ERROR_usage(2), "%s", optusage(NiL));
6114887Schin if (flags & FOLLOW)
6124887Schin {
6134887Schin if (!(fp = (Tail_t*)stakalloc(argc * sizeof(Tail_t))))
6144887Schin error(ERROR_system(1), "out of space");
6154887Schin files = 0;
6164887Schin s = *argv;
6174887Schin do
6184887Schin {
6194887Schin fp->name = s;
6204887Schin fp->sp = 0;
62110898Sroland.mainz@nrubsig.org if (!init(fp, number, delim, flags, &format))
6224887Schin {
6234887Schin fp->expire = timeout ? (NOW + timeout + 1) : 0;
6244887Schin if (files)
6254887Schin pp->next = fp;
6264887Schin else
6274887Schin files = fp;
6284887Schin pp = fp;
6294887Schin fp++;
6304887Schin }
6314887Schin } while (s && (s = *++argv));
6324887Schin if (!files)
6334887Schin return error_info.errors != 0;
6344887Schin pp->next = 0;
6354887Schin hp = 0;
636*12068SRoger.Faulkner@Oracle.COM n = 1;
63710898Sroland.mainz@nrubsig.org while (fp = files)
6384887Schin {
639*12068SRoger.Faulkner@Oracle.COM if (n)
640*12068SRoger.Faulkner@Oracle.COM n = 0;
641*12068SRoger.Faulkner@Oracle.COM else
642*12068SRoger.Faulkner@Oracle.COM sleep(1);
6434887Schin pp = 0;
6444887Schin while (fp)
6454887Schin {
6464887Schin if (fstat(sffileno(fp->sp), &st))
6474887Schin error(ERROR_system(0), "%s: cannot stat", fp->name);
648*12068SRoger.Faulkner@Oracle.COM else if (fp->fifo || fp->end < st.st_size)
6494887Schin {
6504887Schin n = 1;
6514887Schin if (timeout)
6524887Schin fp->expire = NOW + timeout;
653*12068SRoger.Faulkner@Oracle.COM z = fp->fifo ? SF_UNBOUND : st.st_size - fp->cur;
6544887Schin i = 0;
6554887Schin if ((s = sfreserve(fp->sp, z, SF_LOCKR)) || (z = sfvalue(fp->sp)) && (s = sfreserve(fp->sp, z, SF_LOCKR)) && (i = 1))
6564887Schin {
657*12068SRoger.Faulkner@Oracle.COM z = sfvalue(fp->sp);
658*12068SRoger.Faulkner@Oracle.COM for (r = s + z; r > s && *(r - 1) != '\n'; r--);
659*12068SRoger.Faulkner@Oracle.COM if ((w = r - s) || i && (w = z))
6604887Schin {
6614887Schin if ((flags & (HEADERS|VERBOSE)) && hp != fp)
6624887Schin {
6634887Schin hp = fp;
6644887Schin sfprintf(sfstdout, format, fp->name);
6654887Schin format = header_fmt;
6664887Schin }
667*12068SRoger.Faulkner@Oracle.COM fp->cur += w;
668*12068SRoger.Faulkner@Oracle.COM sfwrite(sfstdout, s, w);
6694887Schin }
6704887Schin else
671*12068SRoger.Faulkner@Oracle.COM w = 0;
672*12068SRoger.Faulkner@Oracle.COM sfread(fp->sp, s, w);
673*12068SRoger.Faulkner@Oracle.COM fp->end += w;
6744887Schin }
6754887Schin goto next;
6764887Schin }
6774887Schin else if (!timeout || fp->expire > NOW)
6784887Schin goto next;
6794887Schin else
6804887Schin {
6814887Schin if (flags & LOG)
6824887Schin {
6834887Schin i = 3;
6844887Schin while (--i && stat(fp->name, &st))
6854887Schin sleep(1);
68610898Sroland.mainz@nrubsig.org if (i && (fp->dev != st.st_dev || fp->ino != st.st_ino) && !init(fp, 0, 0, flags, &format))
6874887Schin {
6884887Schin if (!(flags & SILENT))
6894887Schin error(ERROR_warn(0), "%s: log file change", fp->name);
6904887Schin fp->expire = NOW + timeout;
6914887Schin goto next;
6924887Schin }
6934887Schin }
6944887Schin if (!(flags & SILENT))
6954887Schin error(ERROR_warn(0), "%s: %s timeout", fp->name, fmtelapsed(timeout, 1));
6964887Schin }
6974887Schin if (fp->sp && fp->sp != sfstdin)
6984887Schin sfclose(fp->sp);
6994887Schin if (pp)
7004887Schin pp = pp->next = fp->next;
701*12068SRoger.Faulkner@Oracle.COM else
702*12068SRoger.Faulkner@Oracle.COM files = files->next;
7034887Schin fp = fp->next;
7044887Schin continue;
7054887Schin next:
7064887Schin pp = fp;
7074887Schin fp = fp->next;
7084887Schin }
709*12068SRoger.Faulkner@Oracle.COM if (sfsync(sfstdout))
710*12068SRoger.Faulkner@Oracle.COM error(ERROR_system(1), "write error");
7114887Schin }
7124887Schin }
7134887Schin else
7144887Schin {
7154887Schin if (file = *argv)
7164887Schin argv++;
7174887Schin do
7184887Schin {
7194887Schin if (!file || streq(file, "-"))
7204887Schin {
7214887Schin file = "/dev/stdin";
7224887Schin ip = sfstdin;
7234887Schin }
7244887Schin else if (!(ip = sfopen(NiL, file, "r")))
7254887Schin {
7264887Schin error(ERROR_system(0), "%s: cannot open", file);
7274887Schin continue;
7284887Schin }
7294887Schin if (flags & (HEADERS|VERBOSE))
73010898Sroland.mainz@nrubsig.org {
7314887Schin sfprintf(sfstdout, format, file);
73210898Sroland.mainz@nrubsig.org format = header_fmt;
73310898Sroland.mainz@nrubsig.org }
7344887Schin if (number < 0 || !number && (flags & POSITIVE))
7354887Schin {
73610898Sroland.mainz@nrubsig.org sfset(ip, SF_SHARE, 1);
7374887Schin if (number < -1)
7384887Schin sfmove(ip, NiL, -number - 1, delim);
7394887Schin if (flags & REVERSE)
7404887Schin rev_line(ip, sfstdout, sfseek(ip, (Sfoff_t)0, SEEK_CUR));
7414887Schin else
7424887Schin sfmove(ip, sfstdout, SF_UNBOUND, -1);
7434887Schin }
7444887Schin else
7454887Schin {
7464887Schin sfset(ip, SF_SHARE, 0);
7474887Schin if ((offset = tailpos(ip, number, delim)) >= 0)
7484887Schin {
7494887Schin if (flags & REVERSE)
7504887Schin rev_line(ip, sfstdout, offset);
7514887Schin else
7524887Schin {
7534887Schin sfseek(ip, offset, SEEK_SET);
7544887Schin sfmove(ip, sfstdout, SF_UNBOUND, -1);
7554887Schin }
7564887Schin }
7574887Schin else
7584887Schin {
7594887Schin op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout;
7604887Schin pipetail(ip, op, number, delim);
7614887Schin if (flags & REVERSE)
7624887Schin {
7634887Schin sfseek(op, (Sfoff_t)0, SEEK_SET);
7644887Schin rev_line(op, sfstdout, (Sfoff_t)0);
7654887Schin sfclose(op);
7664887Schin }
7674887Schin flags = 0;
7684887Schin }
7694887Schin }
7704887Schin if (ip != sfstdin)
7714887Schin sfclose(ip);
7724887Schin } while (file = *argv++);
7734887Schin }
7744887Schin return error_info.errors != 0;
7754887Schin }
776