📄 tall.c
字号:
last byte of the file).
Return 0 if successful, 1 if an error occurred. */
static int
file_lines (const char *pretty_filename, int fd, long int n_lines,
off_t start_pos, off_t file_length)
{
char buffer[BUFSIZ];
size_t bytes_read;
off_t pos = file_length;
if (n_lines == 0)
return 0;
/* Set `bytes_read' to the size of the last, probably partial, buffer;
0 < `bytes_read' <= `BUFSIZ'. */
bytes_read = (pos - start_pos) % BUFSIZ;
if (bytes_read == 0)
bytes_read = BUFSIZ;
/* Make `pos' a multiple of `BUFSIZ' (0 if the file is short), so that all
reads will be on block boundaries, which might increase efficiency. */
pos -= bytes_read;
xlseek (fd, pos, SEEK_SET, pretty_filename);
bytes_read = safe_read (fd, buffer, bytes_read);
if (bytes_read == SAFE_READ_ERROR)
{
error (0, errno, "%s", pretty_filename);
return 1;
}
/* Count the incomplete line on files that don't end with a newline. */
if (bytes_read && buffer[bytes_read - 1] != '\n')
--n_lines;
do
{
/* Scan backward, counting the newlines in this bufferfull. */
size_t n = bytes_read;
while (n)
{
char const *nl;
nl = memrchr (buffer, '\n', n);
if (nl == NULL)
break;
n = nl - buffer;
if (n_lines-- == 0)
{
/* If this newline isn't the last character in the buffer,
output the part that is after it. */
if (n != bytes_read - 1)
xwrite (STDOUT_FILENO, nl + 1, bytes_read - (n + 1));
dump_remainder (pretty_filename, fd,
file_length - (pos + bytes_read));
return 0;
}
}
/* Not enough newlines in that bufferfull. */
if (pos == start_pos)
{
/* Not enough lines in the file; print the entire file. */
xlseek (fd, start_pos, SEEK_SET, pretty_filename);
dump_remainder (pretty_filename, fd, file_length);
return 0;
}
pos -= BUFSIZ;
xlseek (fd, pos, SEEK_SET, pretty_filename);
bytes_read = safe_read (fd, buffer, BUFSIZ);
if (bytes_read == SAFE_READ_ERROR)
{
error (0, errno, "%s", pretty_filename);
return 1;
}
}
while (bytes_read > 0);
return 0;
}
/* Print the last N_LINES lines from the end of the standard input,
open for reading as pipe FD.
Buffer the text as a linked list of LBUFFERs, adding them as needed.
Return 0 if successful, 1 if an error occured. */
static int
pipe_lines (const char *pretty_filename, int fd, long int n_lines)
{
struct linebuffer
{
int nbytes, nlines;
char buffer[BUFSIZ];
struct linebuffer *next;
};
typedef struct linebuffer LBUFFER;
LBUFFER *first, *last, *tmp;
int i; /* Index into buffers. */
int total_lines = 0; /* Total number of newlines in all buffers. */
int errors = 0;
int nbytes; /* Size of most recent read */
first = last = (LBUFFER *) xmalloc (sizeof (LBUFFER));
first->nbytes = first->nlines = 0;
first->next = NULL;
tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));
/* Input is always read into a fresh buffer. */
while ((nbytes = tmp->nbytes = safe_read (fd, tmp->buffer, BUFSIZ)) > 0)
{
tmp->nlines = 0;
tmp->next = NULL;
/* Count the number of newlines just read. */
for (i = 0; i < tmp->nbytes; i++)
if (tmp->buffer[i] == '\n')
++tmp->nlines;
total_lines += tmp->nlines;
/* If there is enough room in the last buffer read, just append the new
one to it. This is because when reading from a pipe, `nbytes' can
often be very small. */
if (tmp->nbytes + last->nbytes < BUFSIZ)
{
memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
last->nbytes += tmp->nbytes;
last->nlines += tmp->nlines;
}
else
{
/* If there's not enough room, link the new buffer onto the end of
the list, then either free up the oldest buffer for the next
read if that would leave enough lines, or else malloc a new one.
Some compaction mechanism is possible but probably not
worthwhile. */
last = last->next = tmp;
if (total_lines - first->nlines > n_lines)
{
tmp = first;
total_lines -= first->nlines;
first = first->next;
}
else
tmp = (LBUFFER *) xmalloc (sizeof (LBUFFER));
}
}
free ((char *) tmp);
if (nbytes < 0)
{
error (0, errno, "%s", pretty_filename);
errors = 1;
goto free_lbuffers;
}
/* If the file is empty, then bail out. */
if (last->nbytes == 0)
goto free_lbuffers;
/* This prevents a core dump when the pipe contains no newlines. */
if (n_lines == 0)
goto free_lbuffers;
/* Count the incomplete line on files that don't end with a newline. */
if (last->buffer[last->nbytes - 1] != '\n')
{
++last->nlines;
++total_lines;
}
/* Run through the list, printing lines. First, skip over unneeded
buffers. */
for (tmp = first; total_lines - tmp->nlines > n_lines; tmp = tmp->next)
total_lines -= tmp->nlines;
/* Find the correct beginning, then print the rest of the file. */
if (total_lines > n_lines)
{
const char *cp;
/* Skip `total_lines' - `n_lines' newlines. We made sure that
`total_lines' - `n_lines' <= `tmp->nlines'. */
cp = tmp->buffer;
for (i = total_lines - n_lines; i; --i)
while (*cp++ != '\n')
/* Do nothing. */ ;
i = cp - tmp->buffer;
}
else
i = 0;
xwrite (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
for (tmp = tmp->next; tmp; tmp = tmp->next)
xwrite (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
free_lbuffers:
while (first)
{
tmp = first->next;
free ((char *) first);
first = tmp;
}
return errors;
}
/* Print the last N_BYTES characters from the end of pipe FD.
This is a stripped down version of pipe_lines.
Return 0 if successful, 1 if an error occurred. */
static int
pipe_bytes (const char *pretty_filename, int fd, off_t n_bytes)
{
struct charbuffer
{
int nbytes;
char buffer[BUFSIZ];
struct charbuffer *next;
};
typedef struct charbuffer CBUFFER;
CBUFFER *first, *last, *tmp;
int i; /* Index into buffers. */
int total_bytes = 0; /* Total characters in all buffers. */
int errors = 0;
first = last = (CBUFFER *) xmalloc (sizeof (CBUFFER));
first->nbytes = 0;
first->next = NULL;
tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));
/* Input is always read into a fresh buffer. */
while ((tmp->nbytes = safe_read (fd, tmp->buffer, BUFSIZ)) > 0)
{
tmp->next = NULL;
total_bytes += tmp->nbytes;
/* If there is enough room in the last buffer read, just append the new
one to it. This is because when reading from a pipe, `nbytes' can
often be very small. */
if (tmp->nbytes + last->nbytes < BUFSIZ)
{
memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
last->nbytes += tmp->nbytes;
}
else
{
/* If there's not enough room, link the new buffer onto the end of
the list, then either free up the oldest buffer for the next
read if that would leave enough characters, or else malloc a new
one. Some compaction mechanism is possible but probably not
worthwhile. */
last = last->next = tmp;
if (total_bytes - first->nbytes > n_bytes)
{
tmp = first;
total_bytes -= first->nbytes;
first = first->next;
}
else
{
tmp = (CBUFFER *) xmalloc (sizeof (CBUFFER));
}
}
}
if (tmp->nbytes == -1)
{
error (0, errno, "%s", pretty_filename);
errors = 1;
free ((char *) tmp);
goto free_cbuffers;
}
free ((char *) tmp);
/* Run through the list, printing characters. First, skip over unneeded
buffers. */
for (tmp = first; total_bytes - tmp->nbytes > n_bytes; tmp = tmp->next)
total_bytes -= tmp->nbytes;
/* Find the correct beginning, then print the rest of the file.
We made sure that `total_bytes' - `n_bytes' <= `tmp->nbytes'. */
if (total_bytes > n_bytes)
i = total_bytes - n_bytes;
else
i = 0;
xwrite (STDOUT_FILENO, &tmp->buffer[i], tmp->nbytes - i);
for (tmp = tmp->next; tmp; tmp = tmp->next)
xwrite (STDOUT_FILENO, tmp->buffer, tmp->nbytes);
free_cbuffers:
while (first)
{
tmp = first->next;
free ((char *) first);
first = tmp;
}
return errors;
}
/* Skip N_BYTES characters from the start of pipe FD, and print
any extra characters that were read beyond that.
Return 1 on error, 0 if ok, -1 if EOF. */
static int
start_bytes (const char *pretty_filename, int fd, off_t n_bytes)
{
char buffer[BUFSIZ];
size_t bytes_read = 0;
while (0 < n_bytes)
{
bytes_read = safe_read (fd, buffer, BUFSIZ);
if (bytes_read == 0)
return -1;
if (bytes_read == SAFE_READ_ERROR)
{
error (0, errno, "%s", pretty_filename);
return 1;
}
n_bytes -= bytes_read;
}
if (n_bytes < 0)
xwrite (STDOUT_FILENO, &buffer[bytes_read + n_bytes], -n_bytes);
return 0;
}
/* Skip N_LINES lines at the start of file or pipe FD, and print
any extra characters that were read beyond that.
Return 1 on error, 0 if ok, -1 if EOF. */
static int
start_lines (const char *pretty_filename, int fd, long int n_lines)
{
char buffer[BUFSIZ];
size_t bytes_read = 0;
size_t bytes_to_skip = 0;
if (n_lines == 0)
return 0;
while (n_lines)
{
bytes_to_skip = 0;
bytes_read = safe_read (fd, buffer, BUFSIZ);
if (bytes_read == 0) /* EOF */
return -1;
if (bytes_read == SAFE_READ_ERROR) /* error */
{
error (0, errno, "%s", pretty_filename);
return 1;
}
while (bytes_to_skip < bytes_read)
if (buffer[bytes_to_skip++] == '\n' && --n_lines == 0)
break;
}
if (bytes_to_skip < bytes_read)
{
xwrite (STDOUT_FILENO, &buffer[bytes_to_skip],
bytes_read - bytes_to_skip);
}
return 0;
}
/* FIXME: describe */
static void
recheck (struct File_spec *f)
{
/* open/fstat the file and announce if dev/ino have changed */
struct stat new_stats;
int fd;
int fail = 0;
int is_stdin = (STREQ (f->name, "-"));
int was_tailable = f->tailable;
int prev_errnum = f->errnum;
int new_file;
assert (valid_file_spec (f));
fd = (is_stdin ? STDIN_FILENO : open (f->name, O_RDONLY));
/* If the open fails because the file doesn't exist,
then mark the file as not tailable. */
f->tailable = !(reopen_inaccessible_files && fd == -1);
if (fd == -1 || fstat (fd, &new_stats) < 0)
{
fail = 1;
f->errnum = errno;
if (!f->tailable)
{
if (was_tailable)
{
/* FIXME-maybe: detect the case in which the file first becomes
unreadable (perms), and later becomes readable again and can
be seen to be the same file (dev/ino). Otherwise, tail prints
the entire contents of the file when it becomes readable. */
error (0, f->errnum, _("`%s' has become inaccessible"),
pretty_name (f));
}
else
{
/* say nothing... it's still not tailable */
}
}
else if (prev_errnum != errno)
{
error (0, errno, "%s", pretty_name (f));
}
}
else if (!IS_TAILABLE_FILE_TYPE (new_stats.st_mode))
{
fail = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -