📄 tall.c
字号:
f->dev = stats.st_dev;
f->ino = stats.st_ino;
f->n_unchanged_stats = 0;
f->n_consecutive_size_changes = 0;
f->ignore = 0;
}
}
else
{
if (!is_stdin && close (fd))
{
error (0, errno, "%s", pretty_name (f));
errors = 1;
}
}
}
return errors;
}
/* If the command line arguments are of the obsolescent form and the
option string is well-formed, set *FAIL to zero, set *N_UNITS, the
globals COUNT_LINES, FOREVER, and FROM_START, and return non-zero.
Otherwise, if the command line arguments appear to be of the
obsolescent form but the option string is malformed, set *FAIL to
non-zero, don't modify any other parameter or global variable, and
return non-zero. Otherwise, return zero and don't modify any parameter
or global variable. */
static int
parse_obsolescent_option (int argc, const char *const *argv,
off_t *n_units, int *fail)
{
const char *p = argv[1];
const char *n_string = NULL;
const char *n_string_end;
bool obsolete_usage;
int t_from_start;
int t_count_lines;
int t_forever;
/* With the obsolescent form, there is one option string and
(technically) at most one file argument. But we allow two or more
by default. */
if (argc < 2)
return 0;
obsolete_usage = (posix2_version () < 200112) || !getenv ("POSIXLY_CORRECT");
/* If P starts with `+' and the POSIX version predates 1003.1-2001,
or if P starts with `-N' (where N is a digit), or `-l', then it
is obsolescent. Return zero otherwise. */
if (! ((p[0] == '+' && obsolete_usage)
|| (p[0] == '-' && (p[1] == 'l' || ISDIGIT (p[1])))))
return 0;
if (*p == '+')
t_from_start = 1;
else if (*p == '-')
t_from_start = 0;
else
return 0;
++p;
if (ISDIGIT (*p))
{
n_string = p;
do
{
++p;
}
while (ISDIGIT (*p));
}
n_string_end = p;
t_count_lines = 1;
if (*p == 'c' || *p == 'b')
{
t_count_lines = 0;
++p;
}
else if (*p == 'l')
{
++p;
}
t_forever = 0;
if (*p == 'f')
{
t_forever = 1;
++p;
}
if (*p != '\0')
{
/* If (argv[1] begins with a `+' or if it begins with `-' followed
by a digit), but has an invalid suffix character, give a diagnostic
and indicate to caller that this *is* of the obsolescent form,
but that it's an invalid option. */
if (t_from_start || n_string)
{
error (0, 0,
_("%c: invalid suffix character in obsolescent option" ), *p);
*fail = 1;
return 1;
}
/* Otherwise, it might be a valid non-obsolescent option like -n. */
return 0;
}
*fail = 0;
if (n_string == NULL)
*n_units = DEFAULT_N_LINES;
else
{
strtol_error s_err;
uintmax_t tmp;
char *end;
s_err = xstrtoumax (n_string, &end, 10, &tmp,
*n_string_end == 'b' ? "b" : NULL);
if (s_err == LONGINT_OK && tmp <= OFF_T_MAX)
*n_units = (off_t) tmp;
else
{
/* Extract a NUL-terminated string for the error message. */
size_t len = n_string_end - n_string;
char *n_string_tmp = xmalloc (len + 1);
strncpy (n_string_tmp, n_string, len);
n_string_tmp[len] = '\0';
error (0, 0,
_("%s: %s is so large that it is not representable"),
n_string_tmp, (t_count_lines
? _("number of lines")
: _("number of bytes")));
free (n_string_tmp);
*fail = 1;
}
}
if (!*fail)
{
if (argc > 3)
{
/* When POSIXLY_CORRECT is set, enforce the `at most one
file argument' requirement. */
if (getenv ("POSIXLY_CORRECT"))
{
error (0, 0, _("\
too many arguments; When using tail's obsolescent option syntax (%s)\n\
there may be no more than one file argument. Use the equivalent -n or -c\n\
option instead."), argv[1]);
*fail = 1;
return 1;
}
#if DISABLED /* FIXME: enable or remove this warning. */
error (0, 0, _("\
Warning: it is not portable to use two or more file arguments with\n\
tail's obsolescent option syntax (%s). Use the equivalent -n or -c\n\
option instead."), argv[1]);
#endif
}
if (! obsolete_usage)
{
error (0, 0, _("`%s' option is obsolete; use `%s-%c %.*s'"),
argv[1], t_forever ? " -f" : "", t_count_lines ? 'n' : 'c',
(int) (n_string_end - n_string), n_string);
usage (EXIT_FAILURE);
}
/* Set globals. */
from_start = t_from_start;
count_lines = t_count_lines;
forever = t_forever;
}
return 1;
}
static void
parse_options (int argc, char **argv,
off_t *n_units, enum header_mode *header_mode,
double *sleep_interval)
{
int c;
count_lines = 1;
forever = from_start = print_headers = 0;
while ((c = getopt_long (argc, argv, "c:n:fFqs:v", long_options, NULL))
!= -1)
{
switch (c)
{
case 0:
break;
case 'F':
forever = 1;
follow_mode = Follow_name;
reopen_inaccessible_files = 1;
break;
case 'c':
case 'n':
count_lines = (c == 'n');
if (*optarg == '+')
from_start = 1;
else if (*optarg == '-')
++optarg;
{
strtol_error s_err;
uintmax_t n;
s_err = xstrtoumax (optarg, NULL, 10, &n, "bkm");
if (s_err != LONGINT_OK)
{
error (EXIT_FAILURE, 0, "%s: %s", optarg,
(c == 'n'
? _("invalid number of lines")
: _("invalid number of bytes")));
}
if (OFF_T_MAX < n)
error (EXIT_FAILURE, 0,
_("%s is larger than the maximum file size on this system"),
optarg);
*n_units = (off_t) n;
}
break;
case 'f':
case LONG_FOLLOW_OPTION:
forever = 1;
if (optarg == NULL)
follow_mode = DEFAULT_FOLLOW_MODE;
else
follow_mode = XARGMATCH ("--follow", optarg,
follow_mode_string, follow_mode_map);
break;
case RETRY_OPTION:
reopen_inaccessible_files = 1;
break;
case MAX_UNCHANGED_STATS_OPTION:
/* --max-unchanged-stats=N */
if (xstrtoul (optarg, NULL, 10,
&max_n_unchanged_stats_between_opens, "") != LONGINT_OK)
{
error (EXIT_FAILURE, 0,
_("%s: invalid maximum number of unchanged stats between opens"),
optarg);
}
break;
case MAX_CONSECUTIVE_SIZE_CHANGES_OPTION:
/* --max-consecutive-size-changes=N */
if (xstrtoul (optarg, NULL, 10,
&max_n_consecutive_size_changes_between_opens, "")
!= LONGINT_OK)
{
error (EXIT_FAILURE, 0,
_("%s: invalid maximum number of consecutive size changes"),
optarg);
}
break;
case PID_OPTION:
{
strtol_error s_err;
unsigned long int tmp_ulong;
s_err = xstrtoul (optarg, NULL, 10, &tmp_ulong, "");
if (s_err != LONGINT_OK || tmp_ulong > PID_T_MAX)
{
error (EXIT_FAILURE, 0, _("%s: invalid PID"), optarg);
}
pid = tmp_ulong;
}
break;
case 'q':
*header_mode = never;
break;
case 's':
{
double s;
if (xstrtod (optarg, NULL, &s) || ! (0 <= s))
error (EXIT_FAILURE, 0,
_("%s: invalid number of seconds"), optarg);
*sleep_interval = s;
}
break;
case 'v':
*header_mode = always;
break;
case_GETOPT_HELP_CHAR;
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
default:
usage (EXIT_FAILURE);
}
}
if (reopen_inaccessible_files && follow_mode != Follow_name)
error (0, 0, _("warning: --retry is useful only when following by name"));
if (pid && !forever)
error (0, 0,
_("warning: PID ignored; --pid=PID is useful only when following"));
else if (pid && kill (pid, 0) != 0 && errno == ENOSYS)
{
error (0, 0, _("warning: --pid=PID is not supported on this system"));
pid = 0;
}
}
int
main (int argc, char **argv)
{
enum header_mode header_mode = multiple_files;
int exit_status = 0;
/* If from_start, the number of items to skip before printing; otherwise,
the number of items at the end of the file to print. Although the type
is signed, the value is never negative. */
off_t n_units = DEFAULT_N_LINES;
int n_files;
char **file;
struct File_spec *F;
int i;
/* The number of seconds to sleep between iterations.
During one iteration, every file name or descriptor is checked to
see if it has changed. */
double sleep_interval = 1.0;
program_name = argv[0];
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
have_read_stdin = 0;
{
int fail;
if (parse_obsolescent_option (argc,
(const char *const *) argv,
&n_units, &fail))
{
if (fail)
exit (EXIT_FAILURE);
optind = 2;
}
else
{
parse_options (argc, argv, &n_units, &header_mode, &sleep_interval);
}
}
/* To start printing with item N_UNITS from the start of the file, skip
N_UNITS - 1 items. `tail -n +0' is actually meaningless, but for Unix
compatibility it's treated the same as `tail -n +1'. */
if (from_start)
{
if (n_units)
--n_units;
}
n_files = argc - optind;
file = argv + optind;
if (n_files == 0)
{
static char *dummy_stdin = "-";
n_files = 1;
file = &dummy_stdin;
}
F = (struct File_spec *) xmalloc (n_files * sizeof (F[0]));
for (i = 0; i < n_files; i++)
F[i].name = file[i];
if (header_mode == always
|| (header_mode == multiple_files && n_files > 1))
print_headers = 1;
for (i = 0; i < n_files; i++)
exit_status |= tail_file (&F[i], n_units);
if (forever)
{
/* This fflush appears to be required only on Solaris2.7. */
if (fflush (stdout) < 0)
error (EXIT_FAILURE, errno, _("write error"));
SETVBUF (stdout, NULL, _IONBF, 0);
tail_forever (F, n_files, sleep_interval);
}
if (have_read_stdin && close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno, "-");
exit (exit_status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -