mirror of
https://github.com/coreutils/coreutils.git
synced 2024-11-27 12:04:25 +08:00
head: port to Darwin and use simpler seeks
This removes an unportable assumption that if lseek succeeds, the file is capable of seeking. See: http://bugs.gnu.org/17145 * src/head.c (elseek): New function, for consistency in reporting lseek failures. (elide_tail_bytes_file, elide_tail_lines_seekable) (elide_tail_lines_file, head_lines, head): Use it. (elide_tail_bytes_file, elide_tail_lines_file): New args CURRENT_POS and SIZE. All uses changed. Don't bother invoking lseek, since we know the file's pos and size now. (elide_tail_bytes_file): Change a local from uintmax_t to off_t, since it fits. (head): Use lseek only on regular files, since its behavior on unseekable devices is implementation-defined. * NEWS: Document this.
This commit is contained in:
parent
d08381bc26
commit
9111dd2499
5
NEWS
5
NEWS
@ -20,6 +20,11 @@ GNU coreutils NEWS -*- outline -*-
|
||||
date could crash or go into an infinite loop when parsing a malformed TZ="".
|
||||
[bug introduced with the --date='TZ="" ..' parsing feature in coreutils-5.3.0]
|
||||
|
||||
head --bytes=-N and --lines=-N now handles devices more
|
||||
consistently, not ignoring data from virtual devices like /dev/zero,
|
||||
or on BSD systems data from tty devices.
|
||||
[bug introduced in coreutils-5.0.1]
|
||||
|
||||
head --bytes=-N - no longer fails with a bogus diagnostic when stdin's
|
||||
seek pointer is not at the beginning.
|
||||
[bug introduced with the --bytes=-N feature in coreutils-5.0.1]
|
||||
|
@ -162,7 +162,7 @@ Davide Canova kc.canova@gmail.com
|
||||
Dawson Engler engler@stanford.edu
|
||||
Dean Gaudet dean-savannah@arctic.org
|
||||
Deepak Goel deego@gnufans.org
|
||||
Denis Excoffier denis.excoffier@airbus.com
|
||||
Denis Excoffier gcc@Denis-Excoffier.org
|
||||
Denis McKeon dmckeon@swcp.com
|
||||
Dennis Henriksen opus@flamingo.osrl.dk
|
||||
Dennis Clarke dclarke@blastwave.org
|
||||
|
169
src/head.c
169
src/head.c
@ -34,6 +34,7 @@
|
||||
#include "error.h"
|
||||
#include "full-read.h"
|
||||
#include "quote.h"
|
||||
#include "quotearg.h"
|
||||
#include "safe-read.h"
|
||||
#include "xfreopen.h"
|
||||
#include "xstrtol.h"
|
||||
@ -404,53 +405,53 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
|
||||
}
|
||||
}
|
||||
|
||||
/* Print all but the last N_ELIDE lines from the input available
|
||||
via file descriptor FD. Return true upon success.
|
||||
/* Call lseek (FD, OFFSET, WHENCE), where file descriptor FD
|
||||
corresponds to the file FILENAME. WHENCE must be SEEK_SET or
|
||||
SEEK_CUR. Return the resulting offset. Give a diagnostic and
|
||||
return -1 if lseek fails. */
|
||||
|
||||
static off_t
|
||||
elseek (int fd, off_t offset, int whence, char const *filename)
|
||||
{
|
||||
off_t new_offset = lseek (fd, offset, whence);
|
||||
char buf[INT_BUFSIZE_BOUND (offset)];
|
||||
|
||||
if (new_offset < 0)
|
||||
error (0, errno,
|
||||
_(whence == SEEK_SET
|
||||
? N_("%s: cannot seek to offset %s")
|
||||
: N_("%s: cannot seek to relative offset %s")),
|
||||
quotearg_colon (filename),
|
||||
offtostr (offset, buf));
|
||||
|
||||
return new_offset;
|
||||
}
|
||||
|
||||
/* For the file FILENAME with descriptor FD, output all but the last N_ELIDE
|
||||
bytes. If SIZE is nonnegative, this is a regular file positioned
|
||||
at START_POS with SIZE bytes. Return true on success.
|
||||
Give a diagnostic and return false upon error. */
|
||||
|
||||
/* NOTE: if the input file shrinks by more than N_ELIDE bytes between
|
||||
the length determination and the actual reading, then head fails. */
|
||||
|
||||
static bool
|
||||
elide_tail_bytes_file (const char *filename, int fd, uintmax_t n_elide)
|
||||
elide_tail_bytes_file (const char *filename, int fd, uintmax_t n_elide,
|
||||
off_t current_pos, off_t size)
|
||||
{
|
||||
struct stat stats;
|
||||
|
||||
if (presume_input_pipe || fstat (fd, &stats) || ! S_ISREG (stats.st_mode))
|
||||
{
|
||||
return elide_tail_bytes_pipe (filename, fd, n_elide);
|
||||
}
|
||||
if (size < 0)
|
||||
return elide_tail_bytes_pipe (filename, fd, n_elide);
|
||||
else
|
||||
{
|
||||
off_t current_pos, end_pos;
|
||||
uintmax_t bytes_remaining;
|
||||
off_t diff;
|
||||
enum Copy_fd_status err;
|
||||
|
||||
if ((current_pos = lseek (fd, 0, SEEK_CUR)) == -1
|
||||
|| (end_pos = lseek (fd, 0, SEEK_END)) == -1)
|
||||
{
|
||||
error (0, errno, _("cannot lseek %s"), quote (filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Be careful here. The current position may actually be
|
||||
beyond the end of the file. */
|
||||
bytes_remaining = (diff = end_pos - current_pos) < 0 ? 0 : diff;
|
||||
off_t diff = size - current_pos;
|
||||
off_t bytes_remaining = diff < 0 ? 0 : diff;
|
||||
|
||||
if (bytes_remaining <= n_elide)
|
||||
return true;
|
||||
|
||||
/* Seek back to 'current' position, then copy the required
|
||||
number of bytes from fd. */
|
||||
if (lseek (fd, current_pos, SEEK_SET) < 0)
|
||||
{
|
||||
error (0, errno, _("%s: cannot lseek back to original position"),
|
||||
quote (filename));
|
||||
return false;
|
||||
}
|
||||
|
||||
err = copy_fd (fd, bytes_remaining - n_elide);
|
||||
enum Copy_fd_status err = copy_fd (fd, bytes_remaining - n_elide);
|
||||
if (err == COPY_FD_OK)
|
||||
return true;
|
||||
|
||||
@ -594,10 +595,10 @@ free_lbuffers:
|
||||
}
|
||||
|
||||
/* Output all but the last N_LINES lines of the input stream defined by
|
||||
FD, START_POS, and END_POS.
|
||||
FD, START_POS, and SIZE.
|
||||
START_POS is the starting position of the read pointer for the file
|
||||
associated with FD (may be nonzero).
|
||||
END_POS is the file offset of EOF (one larger than offset of last byte).
|
||||
SIZE is the file size in bytes.
|
||||
Return true upon success.
|
||||
Give a diagnostic and return false upon error.
|
||||
|
||||
@ -607,11 +608,11 @@ free_lbuffers:
|
||||
static bool
|
||||
elide_tail_lines_seekable (const char *pretty_filename, int fd,
|
||||
uintmax_t n_lines,
|
||||
off_t start_pos, off_t end_pos)
|
||||
off_t start_pos, off_t size)
|
||||
{
|
||||
char buffer[BUFSIZ];
|
||||
size_t bytes_read;
|
||||
off_t pos = end_pos;
|
||||
off_t pos = size;
|
||||
|
||||
/* Set 'bytes_read' to the size of the last, probably partial, buffer;
|
||||
0 < 'bytes_read' <= 'BUFSIZ'. */
|
||||
@ -621,13 +622,8 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
|
||||
/* 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;
|
||||
if (lseek (fd, pos, SEEK_SET) < 0)
|
||||
{
|
||||
char offset_buf[INT_BUFSIZE_BOUND (pos)];
|
||||
error (0, errno, _("%s: cannot seek to offset %s"),
|
||||
pretty_filename, offtostr (pos, offset_buf));
|
||||
return false;
|
||||
}
|
||||
if (elseek (fd, pos, SEEK_SET, pretty_filename) < 0)
|
||||
return false;
|
||||
bytes_read = safe_read (fd, buffer, bytes_read);
|
||||
if (bytes_read == SAFE_READ_ERROR)
|
||||
{
|
||||
@ -667,14 +663,8 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
|
||||
if (start_pos < pos)
|
||||
{
|
||||
enum Copy_fd_status err;
|
||||
if (lseek (fd, start_pos, SEEK_SET) < 0)
|
||||
{
|
||||
/* Failed to reposition file pointer. */
|
||||
error (0, errno,
|
||||
"%s: unable to restore file pointer to initial offset",
|
||||
quote (pretty_filename));
|
||||
return false;
|
||||
}
|
||||
if (elseek (fd, start_pos, SEEK_SET, pretty_filename) < 0)
|
||||
return false;
|
||||
|
||||
err = copy_fd (fd, pos - start_pos);
|
||||
if (err != COPY_FD_OK)
|
||||
@ -689,13 +679,7 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
|
||||
xwrite_stdout (buffer, n + 1);
|
||||
|
||||
/* Set file pointer to the byte after what we've output. */
|
||||
if (lseek (fd, pos + n + 1, SEEK_SET) < 0)
|
||||
{
|
||||
error (0, errno, _("%s: failed to reset file pointer"),
|
||||
quote (pretty_filename));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return 0 <= elseek (fd, pos + n + 1, SEEK_SET, pretty_filename);
|
||||
}
|
||||
}
|
||||
|
||||
@ -706,13 +690,8 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
|
||||
return true;
|
||||
}
|
||||
pos -= BUFSIZ;
|
||||
if (lseek (fd, pos, SEEK_SET) < 0)
|
||||
{
|
||||
char offset_buf[INT_BUFSIZE_BOUND (pos)];
|
||||
error (0, errno, _("%s: cannot seek to offset %s"),
|
||||
pretty_filename, offtostr (pos, offset_buf));
|
||||
return false;
|
||||
}
|
||||
if (elseek (fd, pos, SEEK_SET, pretty_filename) < 0)
|
||||
return false;
|
||||
|
||||
bytes_read = safe_read (fd, buffer, BUFSIZ);
|
||||
if (bytes_read == SAFE_READ_ERROR)
|
||||
@ -728,36 +707,28 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
|
||||
}
|
||||
}
|
||||
|
||||
/* Print all but the last N_ELIDE lines from the input available
|
||||
via file descriptor FD. Return true upon success.
|
||||
/* For the file FILENAME with descriptor FD, output all but the last N_ELIDE
|
||||
lines. If SIZE is nonnegative, this is a regular file positioned
|
||||
at START_POS with SIZE bytes. Return true on success.
|
||||
Give a diagnostic and return nonzero upon error. */
|
||||
|
||||
static bool
|
||||
elide_tail_lines_file (const char *filename, int fd, uintmax_t n_elide)
|
||||
elide_tail_lines_file (const char *filename, int fd, uintmax_t n_elide,
|
||||
off_t current_pos, off_t size)
|
||||
{
|
||||
if (!presume_input_pipe)
|
||||
if (size < 0)
|
||||
return elide_tail_lines_pipe (filename, fd, n_elide);
|
||||
else
|
||||
{
|
||||
/* Find the offset, OFF, of the Nth newline from the end,
|
||||
but not counting the last byte of the file.
|
||||
If found, write from current position to OFF, inclusive.
|
||||
Otherwise, just return true. */
|
||||
|
||||
off_t start_pos = lseek (fd, 0, SEEK_CUR);
|
||||
off_t end_pos = lseek (fd, 0, SEEK_END);
|
||||
if (0 <= start_pos && 0 <= end_pos)
|
||||
{
|
||||
/* If no data to read we're done. */
|
||||
if (start_pos >= end_pos)
|
||||
return true;
|
||||
|
||||
return elide_tail_lines_seekable (filename, fd, n_elide,
|
||||
start_pos, end_pos);
|
||||
}
|
||||
|
||||
/* lseek failed, Fall through... */
|
||||
return (size <= current_pos
|
||||
|| elide_tail_lines_seekable (filename, fd, n_elide,
|
||||
current_pos, size));
|
||||
}
|
||||
|
||||
return elide_tail_lines_pipe (filename, fd, n_elide);
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -811,11 +782,9 @@ head_lines (const char *filename, int fd, uintmax_t lines_to_write)
|
||||
gotten to had we been reading one byte at a time. */
|
||||
if (lseek (fd, -n_bytes_past_EOL, SEEK_CUR) < 0)
|
||||
{
|
||||
int e = errno;
|
||||
struct stat st;
|
||||
if (fstat (fd, &st) != 0 || S_ISREG (st.st_mode))
|
||||
error (0, e, _("cannot reposition file pointer for %s"),
|
||||
quote (filename));
|
||||
elseek (fd, -n_bytes_past_EOL, SEEK_CUR, filename);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -833,14 +802,28 @@ head (const char *filename, int fd, uintmax_t n_units, bool count_lines,
|
||||
|
||||
if (elide_from_end)
|
||||
{
|
||||
off_t current_pos = -1, size = -1;
|
||||
if (! presume_input_pipe)
|
||||
{
|
||||
struct stat st;
|
||||
if (fstat (fd, &st) != 0)
|
||||
{
|
||||
error (0, errno, _("cannot fstat %s"),
|
||||
quotearg_colon (filename));
|
||||
return false;
|
||||
}
|
||||
if (S_ISREG (st.st_mode))
|
||||
{
|
||||
size = st.st_size;
|
||||
current_pos = elseek (fd, 0, SEEK_CUR, filename);
|
||||
if (current_pos < 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (count_lines)
|
||||
{
|
||||
return elide_tail_lines_file (filename, fd, n_units);
|
||||
}
|
||||
return elide_tail_lines_file (filename, fd, n_units, current_pos, size);
|
||||
else
|
||||
{
|
||||
return elide_tail_bytes_file (filename, fd, n_units);
|
||||
}
|
||||
return elide_tail_bytes_file (filename, fd, n_units, current_pos, size);
|
||||
}
|
||||
if (count_lines)
|
||||
return head_lines (filename, fd, n_units);
|
||||
|
Loading…
Reference in New Issue
Block a user