summaryrefslogtreecommitdiff
path: root/usr/src/lib/libcmd/common/tail.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libcmd/common/tail.c')
-rw-r--r--usr/src/lib/libcmd/common/tail.c214
1 files changed, 157 insertions, 57 deletions
diff --git a/usr/src/lib/libcmd/common/tail.c b/usr/src/lib/libcmd/common/tail.c
index 7eb012af7f..ca128f2938 100644
--- a/usr/src/lib/libcmd/common/tail.c
+++ b/usr/src/lib/libcmd/common/tail.c
@@ -1,7 +1,7 @@
/***********************************************************************
* *
* This software is part of the ast package *
-* Copyright (c) 1992-2008 AT&T Intellectual Property *
+* Copyright (c) 1992-2009 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
@@ -28,7 +28,7 @@
*/
static const char usage[] =
-"+[-?\n@(#)$Id: tail (AT&T Research) 2006-10-18 $\n]"
+"+[-?\n@(#)$Id: tail (AT&T Research) 2009-08-25 $\n]"
USAGE_LICENSE
"[+NAME?tail - output trailing portion of one or more files ]"
"[+DESCRIPTION?\btail\b copies one or more input files to standard output "
@@ -44,21 +44,25 @@ USAGE_LICENSE
"followed by one of the following characters to specify a different "
"unit other than a single byte:]{"
"[+b?512 bytes.]"
- "[+k?1-kilobyte.]"
- "[+m?1-megabyte.]"
+ "[+k?1 KiB.]"
+ "[+m?1 MiB.]"
+ "[+g?1 GiB.]"
"}"
"[+?For backwards compatibility, \b-\b\anumber\a is equivalent to "
"\b-n\b \anumber\a and \b+\b\anumber\a is equivalent to "
- "\b-n -\b\anumber\a.]"
+ "\b-n -\b\anumber\a. \anumber\a may also have these option "
+ "suffixes: \bb c f g k l m r\b.]"
"[n:lines]:[lines:=10?Copy \alines\a lines from each file. A negative value "
- "for \alines\a indicates an offset from the start of the file.]"
+ "for \alines\a indicates an offset from the end of the file.]"
+"[b:blocks?Copy units of 512 bytes.]"
"[c:bytes]:?[chars?Copy \achars\a bytes from each file. A negative value "
- "for \achars\a indicates an offset from the start of the file.]"
+ "for \achars\a indicates an offset from the end of the file.]"
"[f:forever|follow?Loop forever trying to read more characters as the "
"end of each file to copy new data. Ignored if reading from a pipe "
"or fifo.]"
"[h!:headers?Output filename headers.]"
+"[l:lines?Copy units of lines. This is the default.]"
"[L:log?When a \b--forever\b file times out via \b--timeout\b, verify that "
"the curent file has not been renamed and replaced by another file "
"of the same name (a common log file practice) before giving up on "
@@ -83,9 +87,11 @@ USAGE_LICENSE
"[+S?scores]"
"}"
"[v:verbose?Always ouput filename headers.]"
+
"\n"
"\n[file ...]\n"
"\n"
+
"[+EXIT STATUS?]{"
"[+0?All files copied successfully.]"
"[+>0?One or more files did not copy.]"
@@ -103,17 +109,18 @@ USAGE_LICENSE
#define ERROR (1<<1)
#define FOLLOW (1<<2)
#define HEADERS (1<<3)
-#define LOG (1<<4)
-#define NEGATIVE (1<<5)
-#define POSITIVE (1<<6)
-#define REVERSE (1<<7)
-#define SILENT (1<<8)
-#define TIMEOUT (1<<9)
-#define VERBOSE (1<<10)
+#define LINES (1<<4)
+#define LOG (1<<5)
+#define NEGATIVE (1<<6)
+#define POSITIVE (1<<7)
+#define REVERSE (1<<8)
+#define SILENT (1<<9)
+#define TIMEOUT (1<<10)
+#define VERBOSE (1<<11)
#define NOW (unsigned long)time(NiL)
-#define LINES 10
+#define DEFAULT 10
#ifdef S_ISSOCK
#define FIFO(m) (S_ISFIFO(m)||S_ISSOCK(m))
@@ -132,8 +139,11 @@ struct Tail_s
unsigned long expire;
long dev;
long ino;
+ int fifo;
};
+static const char header_fmt[] = "\n==> %s <==\n";
+
/*
* if file is seekable, position file to tail location and return offset
* otherwise, return -1
@@ -159,10 +169,10 @@ tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
return first;
return offset;
}
- if ((offset = last - SF_BUFSIZE) < first)
- offset = first;
for (;;)
{
+ if ((offset = last - SF_BUFSIZE) < first)
+ offset = first;
sfseek(fp, offset, SEEK_SET);
n = last - offset;
if (!(s = sfreserve(fp, n, SF_LOCKR)))
@@ -178,8 +188,6 @@ tailpos(register Sfio_t* fp, register Sfoff_t number, int delim)
if (offset == first)
break;
last = offset;
- if ((offset = last - SF_BUFSIZE) < first)
- offset = first;
}
return first;
}
@@ -245,11 +253,13 @@ pipetail(Sfio_t* infile, Sfio_t* outfile, Sfoff_t number, int delim)
*/
static int
-init(Tail_t* tp, Sfoff_t number, int delim, int flags)
+init(Tail_t* tp, Sfoff_t number, int delim, int flags, const char** format)
{
Sfoff_t offset;
+ Sfio_t* op;
struct stat st;
+ tp->fifo = 0;
if (tp->sp)
{
offset = 0;
@@ -273,12 +283,46 @@ init(Tail_t* tp, Sfoff_t number, int delim, int flags)
sfset(tp->sp, SF_SHARE, 0);
if (offset)
{
- if ((offset = tailpos(tp->sp, number, delim)) < 0)
+ if (number < 0 || !number && (flags & POSITIVE))
+ {
+ sfset(tp->sp, SF_SHARE, !(flags & FOLLOW));
+ if (number < -1)
+ {
+ sfmove(tp->sp, NiL, -number - 1, delim);
+ offset = sfseek(tp->sp, (Sfoff_t)0, SEEK_CUR);
+ }
+ else
+ offset = 0;
+ }
+ else if ((offset = tailpos(tp->sp, number, delim)) >= 0)
+ sfseek(tp->sp, offset, SEEK_SET);
+ else if (fstat(sffileno(tp->sp), &st))
+ {
+ error(ERROR_system(0), "%s: cannot stat", tp->name);
+ goto bad;
+ }
+ else if (!FIFO(st.st_mode))
{
error(ERROR_SYSTEM|2, "%s: cannot position file to tail", tp->name);
goto bad;
}
- sfseek(tp->sp, offset, SEEK_SET);
+ else
+ {
+ tp->fifo = 1;
+ if (flags & (HEADERS|VERBOSE))
+ {
+ sfprintf(sfstdout, *format, tp->name);
+ *format = header_fmt;
+ }
+ op = (flags & REVERSE) ? sftmp(4*SF_BUFSIZE) : sfstdout;
+ pipetail(tp->sp ? tp->sp : sfstdin, op, number, delim);
+ if (flags & REVERSE)
+ {
+ sfseek(op, (Sfoff_t)0, SEEK_SET);
+ rev_line(op, sfstdout, (Sfoff_t)0);
+ sfclose(op);
+ }
+ }
}
tp->last = offset;
if (flags & LOG)
@@ -325,10 +369,8 @@ num(register const char* s, char** e, int* f, int o)
s++;
errno = 0;
number = strtonll(s, &t, NiL, 0);
- if (!o && t > s && *(t - 1) == 'l')
- t--;
if (t == s)
- number = LINES;
+ number = DEFAULT;
if (o && *t)
{
number = 0;
@@ -346,6 +388,8 @@ num(register const char* s, char** e, int* f, int o)
else
{
*f |= COUNT;
+ if (t > s && isalpha(*(t - 1)))
+ *f &= ~LINES;
if (c == '-')
number = -number;
}
@@ -357,24 +401,23 @@ num(register const char* s, char** e, int* f, int o)
int
b_tail(int argc, char** argv, void* context)
{
- static const char header_fmt[] = "\n==> %s <==\n";
-
register Sfio_t* ip;
register int n;
register int i;
- register int delim = '\n';
- int flags = HEADERS;
+ int delim;
+ int flags = HEADERS|LINES;
+ int blocks = 0;
char* s;
char* t;
char* r;
char* e;
char* file;
Sfoff_t offset;
- Sfoff_t number = LINES;
+ Sfoff_t number = DEFAULT;
unsigned long timeout = 0;
struct stat st;
const char* format = header_fmt+1;
- size_t z;
+ ssize_t z;
Sfio_t* op;
register Tail_t* fp;
register Tail_t* pp;
@@ -386,13 +429,38 @@ b_tail(int argc, char** argv, void* context)
{
switch (n = optget(argv, usage))
{
- case 'c':
- delim = -1;
- if (opt_info.arg && *opt_info.arg=='f' && !*(opt_info.arg+1))
+ case 0:
+ if (!(flags & FOLLOW) && argv[opt_info.index] && (argv[opt_info.index][0] == '-' || argv[opt_info.index][0] == '+') && !argv[opt_info.index][1])
{
- flags |= FOLLOW;
+ number = argv[opt_info.index][0] == '-' ? 10 : -10;
+ flags |= LINES;
+ opt_info.index++;
continue;
}
+ break;
+ case 'b':
+ blocks = 512;
+ flags &= ~LINES;
+ if (opt_info.option[0] == '+')
+ number = -number;
+ continue;
+ case 'c':
+ flags &= ~LINES;
+ if (opt_info.arg == argv[opt_info.index - 1])
+ {
+ strtol(opt_info.arg, &s, 10);
+ if (*s)
+ {
+ opt_info.index--;
+ t = "";
+ goto suffix;
+ }
+ }
+ else if (opt_info.arg && isalpha(*opt_info.arg))
+ {
+ t = opt_info.arg;
+ goto suffix;
+ }
/*FALLTHROUGH*/
case 'n':
flags |= COUNT;
@@ -400,14 +468,14 @@ b_tail(int argc, char** argv, void* context)
number = num(s, &s, &flags, n);
else
{
- number = LINES;
+ number = DEFAULT;
flags &= ~(ERROR|NEGATIVE|POSITIVE);
s = "";
}
- if (n=='c' && *s=='f')
+ if (n != 'n' && s && isalpha(*s))
{
- s++;
- flags |= FOLLOW;
+ t = s;
+ goto suffix;
}
if (flags & ERROR)
continue;
@@ -425,6 +493,11 @@ b_tail(int argc, char** argv, void* context)
else
flags &= ~HEADERS;
continue;
+ case 'l':
+ flags |= LINES;
+ if (opt_info.option[0] == '+')
+ number = -number;
+ continue;
case 'L':
flags |= LOG;
continue;
@@ -448,32 +521,46 @@ b_tail(int argc, char** argv, void* context)
continue;
case ':':
/* handle old style arguments */
- r = s = argv[opt_info.index];
- number = num(s, &t, &flags, 0);
+ if (!(r = argv[opt_info.index]) || !opt_info.offset)
+ {
+ error(2, "%s", opt_info.arg);
+ break;
+ }
+ s = r + opt_info.offset - 1;
+ if (i = *(s - 1) == '-' || *(s - 1) == '+')
+ s--;
+ if ((number = num(s, &t, &flags, 0)) && i)
+ number = -number;
+ goto compatibility;
+ suffix:
+ r = 0;
+ if (opt_info.option[0] == '+')
+ number = -number;
+ compatibility:
for (;;)
{
switch (*t++)
{
case 0:
- opt_info.offset = t - r - 1;
- if (number)
- number = -number;
+ if (r)
+ opt_info.offset = t - r - 1;
break;
case 'c':
- delim = -1;
+ flags &= ~LINES;
continue;
case 'f':
flags |= FOLLOW;
continue;
case 'l':
- delim = '\n';
+ flags |= LINES;
continue;
case 'r':
flags |= REVERSE;
continue;
default:
error(2, "%s: invalid suffix", t - 1);
- opt_info.offset = strlen(r);
+ if (r)
+ opt_info.offset = strlen(r);
break;
}
break;
@@ -496,12 +583,15 @@ b_tail(int argc, char** argv, void* context)
}
else if (!*(argv + 1))
flags &= ~HEADERS;
+ delim = (flags & LINES) ? '\n' : -1;
+ if (blocks)
+ number *= blocks;
if (flags & REVERSE)
{
if (delim < 0)
error(2, "--reverse requires line mode");
- else if (!(flags & COUNT))
- number = 0;
+ if (!(flags & COUNT))
+ number = -1;
flags &= ~FOLLOW;
}
if ((flags & (FOLLOW|TIMEOUT)) == TIMEOUT)
@@ -527,7 +617,7 @@ b_tail(int argc, char** argv, void* context)
{
fp->name = s;
fp->sp = 0;
- if (!init(fp, number, delim, flags))
+ if (!init(fp, number, delim, flags, &format))
{
fp->expire = timeout ? (NOW + timeout + 1) : 0;
if (files)
@@ -542,27 +632,35 @@ b_tail(int argc, char** argv, void* context)
return error_info.errors != 0;
pp->next = 0;
hp = 0;
- for (;;)
+ while (fp = files)
{
if (sfsync(sfstdout))
error(ERROR_system(1), "write error");
+#if 0
sleep(1);
+#else
+ {
+ struct timespec rqt = { 0L, 1000000000L/4L };
+ (void)nanosleep(&rqt, NULL);
+ }
+#endif
n = 0;
pp = 0;
- fp = files;
while (fp)
{
if (fstat(sffileno(fp->sp), &st))
error(ERROR_system(0), "%s: cannot stat", fp->name);
- else if (st.st_size > fp->last)
+ else if (st.st_size > fp->last || fp->fifo)
{
n = 1;
if (timeout)
fp->expire = NOW + timeout;
- z = st.st_size - fp->last;
+ z = fp->fifo ? SF_UNBOUND : st.st_size - fp->last;
i = 0;
if ((s = sfreserve(fp->sp, z, SF_LOCKR)) || (z = sfvalue(fp->sp)) && (s = sfreserve(fp->sp, z, SF_LOCKR)) && (i = 1))
{
+ if (fp->fifo)
+ z = sfvalue(fp->sp);
r = 0;
for (e = (t = s) + z; t < e; t++)
if (*t == '\n')
@@ -594,7 +692,7 @@ b_tail(int argc, char** argv, void* context)
i = 3;
while (--i && stat(fp->name, &st))
sleep(1);
- if (i && (fp->dev != st.st_dev || fp->ino != st.st_ino) && !init(fp, 0, 0, flags))
+ if (i && (fp->dev != st.st_dev || fp->ino != st.st_ino) && !init(fp, 0, 0, flags, &format))
{
if (!(flags & SILENT))
error(ERROR_warn(0), "%s: log file change", fp->name);
@@ -636,11 +734,13 @@ b_tail(int argc, char** argv, void* context)
continue;
}
if (flags & (HEADERS|VERBOSE))
+ {
sfprintf(sfstdout, format, file);
- format = header_fmt;
+ format = header_fmt;
+ }
if (number < 0 || !number && (flags & POSITIVE))
{
- sfset(ip, SF_SHARE, !(flags & FOLLOW));
+ sfset(ip, SF_SHARE, 1);
if (number < -1)
sfmove(ip, NiL, -number - 1, delim);
if (flags & REVERSE)