/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1992-2010 AT&T Intellectual Property *
* and is licensed under the *
* Common Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* David Korn <dgk@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* print the tail of one or more files
*
* David Korn
* Glenn Fowler
*/
static const char usage[] =
"+[-?\n@(#)$Id: tail (AT&T Research) 2010-03-23 $\n]"
"[+NAME?tail - output trailing portion of one or more files ]"
"[+DESCRIPTION?\btail\b copies one or more input files to standard output "
"starting at a designated point for each file. Copying starts "
"at the point indicated by the options and is unlimited in size.]"
"[+?By default a header of the form \b==> \b\afilename\a\b <==\b "
"is output before all but the first file but this can be changed "
"with the \b-q\b and \b-v\b options.]"
"[+?If no \afile\a is given, or if the \afile\a is \b-\b, \btail\b "
"copies from standard input. The start of the file is defined "
"as the current offset.]"
"[+?The option argument for \b-c\b can optionally be "
"followed by one of the following characters to specify a different "
"unit other than a single byte:]{"
"[+b?512 bytes.]"
"[+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. \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 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 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 "
"the file.]"
"[q:quiet?Don't output filename headers. For GNU compatibility.]"
"[r:reverse?Output lines in reverse order.]"
"[s:silent?Don't warn about timeout expiration and log file changes.]"
"[t:timeout?Stop checking after \atimeout\a elapses with no additional "
"\b--forever\b output. A separate elapsed time is maintained for "
"each file operand. There is no timeout by default. The default "
"\atimeout\a unit is seconds. \atimeout\a may be a catenation of 1 "
"or more integers, each followed by a 1 character suffix. The suffix "
"may be omitted from the last integer, in which case it is "
"interpreted as seconds. The supported suffixes are:]:[timeout]{"
"[+s?seconds]"
"[+m?minutes]"
"[+h?hours]"
"[+d?days]"
"[+w?weeks]"
"[+M?months]"
"[+y?years]"
"[+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.]"
"}"
"[+SEE ALSO?\bcat\b(1), \bhead\b(1), \brev\b(1)]"
;
#include <cmd.h>
#include <ctype.h>
#include <ls.h>
#include <tm.h>
#include <rev.h>
#ifdef S_ISSOCK
#else
#endif
struct Tail_s
{
char* name;
unsigned long expire;
long dev;
long ino;
int fifo;
};
/*
* if file is seekable, position file to tail location and return offset
* otherwise, return -1
*/
static Sfoff_t
{
register size_t n;
register char* s;
register char* t;
if (delim < 0)
{
return first;
return offset;
}
for (;;)
{
return -1;
t = s + n;
while (t > s)
{
return offset + (t - s) + 1;
}
break;
}
return first;
}
/*
* this code handles tail from a pipe without any size limits
*/
static void
{
register Sfoff_t n;
register int fno = 0;
a = number;
{
if ((nleft -= n) <= 0)
{
}
}
{
}
/*
* see whether both files are needed
*/
{
}
else
}
/*
* (re)initialize a tail stream
*/
static int
{
{
offset = 0;
}
else if (!number)
offset = 0;
else
offset = 1;
{
}
{
return -1;
}
if (offset)
{
{
if (number < -1)
{
}
else
offset = 0;
}
{
goto bad;
}
{
goto bad;
}
else
{
{
*format = header_fmt;
}
{
}
}
}
{
{
goto bad;
}
}
return 0;
bad:
return -1;
}
/*
* convert number with validity diagnostics
*/
static intmax_t
num(register const char* s, char** e, int* f, int o)
{
char* t;
int c;
if ((c = *s) == '-')
{
*f |= NEGATIVE;
s++;
}
else if (c == '+')
{
*f |= POSITIVE;
s++;
}
s++;
errno = 0;
if (t == s)
if (o && *t)
{
number = 0;
*f |= ERROR;
}
else if (errno)
{
*f |= ERROR;
if (o)
else
}
else
{
*f |= COUNT;
if (t > s && isalpha(*(t - 1)))
*f &= ~LINES;
if (c == '-')
}
if (e)
*e = t;
return number;
}
int
{
register int n;
register int i;
int delim;
int blocks = 0;
char* s;
char* t;
char* r;
char* file;
unsigned long timeout = 0;
ssize_t z;
ssize_t w;
for (;;)
{
{
case 0:
if (!(flags & FOLLOW) && argv[opt_info.index] && (argv[opt_info.index][0] == '-' || argv[opt_info.index][0] == '+') && !argv[opt_info.index][1])
{
continue;
}
break;
case 'b':
blocks = 512;
continue;
case 'c':
{
if (*s)
{
t = "";
goto suffix;
}
}
{
goto suffix;
}
/*FALLTHROUGH*/
case 'n':
else
{
s = "";
}
if (n != 'n' && s && isalpha(*s))
{
t = s;
goto suffix;
}
continue;
continue;
case 'f':
continue;
case 'h':
else
continue;
case 'l':
continue;
case 'L':
continue;
case 'q':
continue;
case 'r':
continue;
case 's':
continue;
case 't':
if (*s)
continue;
case 'v':
continue;
case ':':
/* handle old style arguments */
{
break;
}
if (i = *(s - 1) == '-' || *(s - 1) == '+')
s--;
goto compatibility;
r = 0;
for (;;)
{
switch (*t++)
{
case 0:
if (r)
break;
case 'c':
continue;
case 'f':
continue;
case 'l':
continue;
case 'r':
continue;
default:
if (r)
break;
}
break;
}
continue;
case '?':
break;
}
break;
}
if (!*argv)
{
}
else if (!*(argv + 1))
if (blocks)
{
if (delim < 0)
number = -1;
}
{
timeout = 0;
}
{
}
if (error_info.errors)
{
files = 0;
s = *argv;
do
{
{
if (files)
else
fp++;
}
} while (s && (s = *++argv));
if (!files)
return error_info.errors != 0;
hp = 0;
n = 1;
{
if (n)
n = 0;
else
sleep(1);
pp = 0;
while (fp)
{
{
n = 1;
if (timeout)
i = 0;
if ((s = sfreserve(fp->sp, z, SF_LOCKR)) || (z = sfvalue(fp->sp)) && (s = sfreserve(fp->sp, z, SF_LOCKR)) && (i = 1))
{
for (r = s + z; r > s && *(r - 1) != '\n'; r--);
if ((w = r - s) || i && (w = z))
{
{
format = header_fmt;
}
}
else
w = 0;
}
goto next;
}
goto next;
else
{
{
i = 3;
sleep(1);
{
goto next;
}
}
}
if (pp)
else
continue;
next:
}
}
}
else
{
argv++;
do
{
{
}
{
continue;
}
{
format = header_fmt;
}
{
if (number < -1)
else
}
else
{
{
else
{
}
}
else
{
{
}
flags = 0;
}
}
}
return error_info.errors != 0;
}