summaryrefslogtreecommitdiff
path: root/src/head.c
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2016-01-20 10:55:18 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2016-01-20 10:55:18 +0000
commit70e9163c9c18e995515598085cb824e554eb7ae7 (patch)
treea42dc8b2a6c031354bf31472de888bfc8a060132 /src/head.c
parentcbf5993c43f49281173f185863577d86bfac6eae (diff)
downloadcoreutils-tarball-70e9163c9c18e995515598085cb824e554eb7ae7.tar.gz
Diffstat (limited to 'src/head.c')
-rw-r--r--src/head.c1037
1 files changed, 534 insertions, 503 deletions
diff --git a/src/head.c b/src/head.c
index 4038722..282c2ea 100644
--- a/src/head.c
+++ b/src/head.c
@@ -1,10 +1,10 @@
/* head -- output first part of file(s)
- Copyright (C) 89, 90, 91, 1995-2006 Free Software Foundation, Inc.
+ Copyright (C) 1989-2016 Free Software Foundation, Inc.
- This program is free software; you can redistribute it and/or modify
+ This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -12,12 +12,11 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
/* Options: (see usage)
Reads from standard input if no files are given or when a filename of
- ``-'' is encountered.
+ ''-'' is encountered.
By default, filename headers are printed only if more than one file
is given.
By default, prints the first 10 lines (head -n 10).
@@ -33,17 +32,19 @@
#include "system.h"
#include "error.h"
-#include "full-write.h"
#include "full-read.h"
-#include "inttostr.h"
#include "quote.h"
#include "safe-read.h"
-#include "xstrtol.h"
+#include "stat-size.h"
+#include "xfreopen.h"
+#include "xdectoint.h"
-/* The official name of this program (e.g., no `g' prefix). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "head"
-#define AUTHORS "David MacKenzie", "Jim Meyering"
+#define AUTHORS \
+ proper_name ("David MacKenzie"), \
+ proper_name ("Jim Meyering")
/* Number of lines/chars/blocks to head. */
#define DEFAULT_NUMBER 10
@@ -57,15 +58,15 @@ static bool presume_input_pipe;
/* If true, print filename headers. */
static bool print_headers;
+/* Character to split lines by. */
+static char line_end;
+
/* When to print the filename banners. */
enum header_mode
{
multiple_files, always, never
};
-/* The name this program was run with. */
-char *program_name;
-
/* Have we ever read standard input? */
static bool have_read_stdin;
@@ -73,7 +74,6 @@ enum Copy_fd_status
{
COPY_FD_OK = 0,
COPY_FD_READ_ERROR,
- COPY_FD_WRITE_ERROR,
COPY_FD_UNEXPECTED_EOF
};
@@ -93,6 +93,7 @@ static struct option const long_options[] =
{"quiet", no_argument, NULL, 'q'},
{"silent", no_argument, NULL, 'q'},
{"verbose", no_argument, NULL, 'v'},
+ {"zero-terminated", no_argument, NULL, 'z'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
@@ -102,42 +103,45 @@ void
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
Usage: %s [OPTION]... [FILE]...\n\
"),
- program_name);
- fputs (_("\
-Print the first 10 lines of each FILE to standard output.\n\
+ program_name);
+ printf (_("\
+Print the first %d lines of each FILE to standard output.\n\
With more than one FILE, precede each with a header giving the file name.\n\
-With no FILE, or when FILE is -, read standard input.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
-"), stdout);
- fputs (_("\
- -c, --bytes=[-]N print the first N bytes of each file;\n\
- with the leading `-', print all but the last\n\
- N bytes of each file\n\
- -n, --lines=[-]N print the first N lines instead of the first 10;\n\
- with the leading `-', print all but the last\n\
- N lines of each file\n\
-"), stdout);
+"), DEFAULT_NUMBER);
+
+ emit_stdin_note ();
+ emit_mandatory_arg_note ();
+
+ printf (_("\
+ -c, --bytes=[-]NUM print the first NUM bytes of each file;\n\
+ with the leading '-', print all but the last\n\
+ NUM bytes of each file\n\
+ -n, --lines=[-]NUM print the first NUM lines instead of the first %d;\n\
+ with the leading '-', print all but the last\n\
+ NUM lines of each file\n\
+"), DEFAULT_NUMBER);
fputs (_("\
-q, --quiet, --silent never print headers giving file names\n\
-v, --verbose always print headers giving file names\n\
"), stdout);
+ fputs (_("\
+ -z, --zero-terminated line delimiter is NUL, not newline\n\
+"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-N may have a multiplier suffix: b 512, k 1024, m 1024*1024.\n\
+NUM may have a multiplier suffix:\n\
+b 512, kB 1000, K 1024, MB 1000*1000, M 1024*1024,\n\
+GB 1000*1000*1000, G 1024*1024*1024, and so on for T, P, E, Z, Y.\n\
"), stdout);
- printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
+ emit_ancillary_info (PROGRAM_NAME);
}
exit (status);
}
@@ -148,13 +152,10 @@ diagnose_copy_fd_failure (enum Copy_fd_status err, char const *filename)
switch (err)
{
case COPY_FD_READ_ERROR:
- error (0, errno, _("error reading %s"), quote (filename));
- break;
- case COPY_FD_WRITE_ERROR:
- error (0, errno, _("error writing %s"), quote (filename));
+ error (0, errno, _("error reading %s"), quoteaf (filename));
break;
case COPY_FD_UNEXPECTED_EOF:
- error (0, errno, _("%s: file has shrunk too much"), quote (filename));
+ error (0, errno, _("%s: file has shrunk too much"), quotef (filename));
break;
default:
abort ();
@@ -170,11 +171,25 @@ write_header (const char *filename)
first_file = false;
}
-/* Copy no more than N_BYTES from file descriptor SRC_FD to O_STREAM.
- Return an appropriate indication of success or failure. */
+/* Write N_BYTES from BUFFER to stdout.
+ Exit immediately on error with a single diagnostic. */
+
+static void
+xwrite_stdout (char const *buffer, size_t n_bytes)
+{
+ if (n_bytes > 0 && fwrite (buffer, 1, n_bytes, stdout) < n_bytes)
+ {
+ clearerr (stdout); /* To avoid redundant close_stdout diagnostic. */
+ error (EXIT_FAILURE, errno, _("error writing %s"),
+ quoteaf ("standard output"));
+ }
+}
+
+/* Copy no more than N_BYTES from file descriptor SRC_FD to stdout.
+ Return an appropriate indication of success or read failure. */
static enum Copy_fd_status
-copy_fd (int src_fd, FILE *o_stream, uintmax_t n_bytes)
+copy_fd (int src_fd, uintmax_t n_bytes)
{
char buf[BUFSIZ];
const size_t buf_size = sizeof (buf);
@@ -185,27 +200,55 @@ copy_fd (int src_fd, FILE *o_stream, uintmax_t n_bytes)
size_t n_to_read = MIN (buf_size, n_bytes);
size_t n_read = safe_read (src_fd, buf, n_to_read);
if (n_read == SAFE_READ_ERROR)
- return COPY_FD_READ_ERROR;
+ return COPY_FD_READ_ERROR;
n_bytes -= n_read;
if (n_read == 0 && n_bytes != 0)
- return COPY_FD_UNEXPECTED_EOF;
+ return COPY_FD_UNEXPECTED_EOF;
- if (fwrite (buf, 1, n_read, o_stream) < n_read)
- return COPY_FD_WRITE_ERROR;
+ xwrite_stdout (buf, n_read);
}
return COPY_FD_OK;
}
-/* Print all but the last N_ELIDE lines from the input available via
- the non-seekable 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")),
+ quotef (filename),
+ offtostr (offset, buf));
+
+ return new_offset;
+}
+
+/* For an input file with name FILENAME and descriptor FD,
+ output all but the last N_ELIDE_0 bytes.
+ If CURRENT_POS is nonnegative, assume that the input file is
+ positioned at CURRENT_POS and that it should be repositioned to
+ just before the elided bytes before returning.
+ Return true upon success.
Give a diagnostic and return false upon error. */
static bool
-elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
+elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0,
+ off_t current_pos)
{
size_t n_elide = n_elide_0;
+ uintmax_t desired_pos = current_pos;
+ bool ok = true;
#ifndef HEAD_TAIL_PIPE_READ_BUFSIZE
# define HEAD_TAIL_PIPE_READ_BUFSIZE BUFSIZ
@@ -226,9 +269,9 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
if (SIZE_MAX < n_elide_0 + READ_BUFSIZE)
{
- char umax_buf[INT_BUFSIZE_BOUND (uintmax_t)];
+ char umax_buf[INT_BUFSIZE_BOUND (n_elide_0)];
error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"),
- umaxtostr (n_elide_0, umax_buf));
+ umaxtostr (n_elide_0, umax_buf));
}
/* Two cases to consider...
@@ -244,7 +287,6 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
if (n_elide <= HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD)
{
- bool ok = true;
bool first = true;
bool eof = false;
size_t n_to_read = READ_BUFSIZE + n_elide;
@@ -254,213 +296,205 @@ elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
b[1] = b[0] + n_to_read;
for (i = false; ! eof ; i = !i)
- {
- size_t n_read = full_read (fd, b[i], n_to_read);
- size_t delta = 0;
- if (n_read < n_to_read)
- {
- if (errno != 0)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- ok = false;
- break;
- }
-
- /* reached EOF */
- if (n_read <= n_elide)
- {
- if (first)
- {
- /* The input is no larger than the number of bytes
- to elide. So there's nothing to output, and
- we're done. */
- }
- else
- {
- delta = n_elide - n_read;
- }
- }
- eof = true;
- }
-
- /* Output any (but maybe just part of the) elided data from
- the previous round. */
- if ( ! first)
- {
- /* Don't bother checking for errors here.
- If there's a failure, the test of the following
- fwrite or in close_stdout will catch it. */
- fwrite (b[!i] + READ_BUFSIZE, 1, n_elide - delta, stdout);
- }
- first = false;
-
- if (n_elide < n_read
- && fwrite (b[i], 1, n_read - n_elide, stdout) < n_read - n_elide)
- {
- error (0, errno, _("write error"));
- ok = false;
- break;
- }
- }
+ {
+ size_t n_read = full_read (fd, b[i], n_to_read);
+ size_t delta = 0;
+ if (n_read < n_to_read)
+ {
+ if (errno != 0)
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ ok = false;
+ break;
+ }
+
+ /* reached EOF */
+ if (n_read <= n_elide)
+ {
+ if (first)
+ {
+ /* The input is no larger than the number of bytes
+ to elide. So there's nothing to output, and
+ we're done. */
+ }
+ else
+ {
+ delta = n_elide - n_read;
+ }
+ }
+ eof = true;
+ }
+
+ /* Output any (but maybe just part of the) elided data from
+ the previous round. */
+ if (! first)
+ {
+ desired_pos += n_elide - delta;
+ xwrite_stdout (b[!i] + READ_BUFSIZE, n_elide - delta);
+ }
+ first = false;
+
+ if (n_elide < n_read)
+ {
+ desired_pos += n_read - n_elide;
+ xwrite_stdout (b[i], n_read - n_elide);
+ }
+ }
free (b[0]);
- return ok;
}
else
{
/* Read blocks of size READ_BUFSIZE, until we've read at least n_elide
- bytes. Then, for each new buffer we read, also write an old one. */
+ bytes. Then, for each new buffer we read, also write an old one. */
- bool ok = true;
bool eof = false;
size_t n_read;
bool buffered_enough;
size_t i, i_next;
- char **b;
+ char **b = NULL;
/* Round n_elide up to a multiple of READ_BUFSIZE. */
size_t rem = READ_BUFSIZE - (n_elide % READ_BUFSIZE);
size_t n_elide_round = n_elide + rem;
size_t n_bufs = n_elide_round / READ_BUFSIZE + 1;
- b = xcalloc (n_bufs, sizeof *b);
+ size_t n_alloc = 0;
+ size_t n_array_alloc = 0;
buffered_enough = false;
for (i = 0, i_next = 1; !eof; i = i_next, i_next = (i_next + 1) % n_bufs)
- {
- if (b[i] == NULL)
- b[i] = xmalloc (READ_BUFSIZE);
- n_read = full_read (fd, b[i], READ_BUFSIZE);
- if (n_read < READ_BUFSIZE)
- {
- if (errno != 0)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- ok = false;
- goto free_mem;
- }
- eof = true;
- }
-
- if (i + 1 == n_bufs)
- buffered_enough = true;
-
- if (buffered_enough)
- {
- if (fwrite (b[i_next], 1, n_read, stdout) < n_read)
- {
- error (0, errno, _("write error"));
- ok = false;
- goto free_mem;
- }
- }
- }
+ {
+ if (n_array_alloc == i)
+ {
+ /* reallocate between 16 and n_bufs entries. */
+ if (n_array_alloc == 0)
+ n_array_alloc = MIN (n_bufs, 16);
+ else if (n_array_alloc <= n_bufs / 2)
+ n_array_alloc *= 2;
+ else
+ n_array_alloc = n_bufs;
+ b = xnrealloc (b, n_array_alloc, sizeof *b);
+ }
+
+ if (! buffered_enough)
+ {
+ b[i] = xmalloc (READ_BUFSIZE);
+ n_alloc = i + 1;
+ }
+ n_read = full_read (fd, b[i], READ_BUFSIZE);
+ if (n_read < READ_BUFSIZE)
+ {
+ if (errno != 0)
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ ok = false;
+ goto free_mem;
+ }
+ eof = true;
+ }
+
+ if (i + 1 == n_bufs)
+ buffered_enough = true;
+
+ if (buffered_enough)
+ {
+ desired_pos += n_read;
+ xwrite_stdout (b[i_next], n_read);
+ }
+ }
/* Output any remainder: rem bytes from b[i] + n_read. */
if (rem)
- {
- if (buffered_enough)
- {
- size_t n_bytes_left_in_b_i = READ_BUFSIZE - n_read;
- if (rem < n_bytes_left_in_b_i)
- {
- fwrite (b[i] + n_read, 1, rem, stdout);
- }
- else
- {
- fwrite (b[i] + n_read, 1, n_bytes_left_in_b_i, stdout);
- fwrite (b[i_next], 1, rem - n_bytes_left_in_b_i, stdout);
- }
- }
- else if (i + 1 == n_bufs)
- {
- /* This happens when n_elide < file_size < n_elide_round.
-
- |READ_BUF.|
- | | rem |
- |---------!---------!---------!---------|
- |---- n_elide ---------|
- | | x |
- | |y |
- |---- file size -----------|
- | |n_read|
- |---- n_elide_round ----------|
- */
- size_t y = READ_BUFSIZE - rem;
- size_t x = n_read - y;
- fwrite (b[i_next], 1, x, stdout);
- }
- }
-
- free_mem:;
- for (i = 0; i < n_bufs; i++)
- free (b[i]);
+ {
+ if (buffered_enough)
+ {
+ size_t n_bytes_left_in_b_i = READ_BUFSIZE - n_read;
+ desired_pos += rem;
+ if (rem < n_bytes_left_in_b_i)
+ {
+ xwrite_stdout (b[i] + n_read, rem);
+ }
+ else
+ {
+ xwrite_stdout (b[i] + n_read, n_bytes_left_in_b_i);
+ xwrite_stdout (b[i_next], rem - n_bytes_left_in_b_i);
+ }
+ }
+ else if (i + 1 == n_bufs)
+ {
+ /* This happens when n_elide < file_size < n_elide_round.
+
+ |READ_BUF.|
+ | | rem |
+ |---------!---------!---------!---------|
+ |---- n_elide ---------|
+ | | x |
+ | |y |
+ |---- file size -----------|
+ | |n_read|
+ |---- n_elide_round ----------|
+ */
+ size_t y = READ_BUFSIZE - rem;
+ size_t x = n_read - y;
+ desired_pos += x;
+ xwrite_stdout (b[i_next], x);
+ }
+ }
+
+ free_mem:
+ for (i = 0; i < n_alloc; i++)
+ free (b[i]);
free (b);
-
- return ok;
}
+
+ if (0 <= current_pos && elseek (fd, desired_pos, SEEK_SET, filename) < 0)
+ ok = false;
+ return ok;
}
-/* 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
+ bytes. If SIZE is nonnegative, this is a regular file positioned
+ at CURRENT_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,
+ struct stat const *st, off_t current_pos)
{
- struct stat stats;
-
- if (presume_input_pipe || fstat (fd, &stats) || ! S_ISREG (stats.st_mode))
- {
- return elide_tail_bytes_pipe (filename, fd, n_elide);
- }
+ off_t size = st->st_size;
+ if (presume_input_pipe || size <= ST_BLKSIZE (*st))
+ return elide_tail_bytes_pipe (filename, fd, n_elide, current_pos);
else
{
- off_t current_pos, end_pos;
- uintmax_t bytes_remaining;
- off_t diff;
- enum Copy_fd_status err;
-
- if ((current_pos = lseek (fd, (off_t) 0, SEEK_CUR)) == -1
- || (end_pos = lseek (fd, (off_t) 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;
+ beyond the end of the file. */
+ 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, (off_t) 0, current_pos) == -1)
- {
- error (0, errno, _("%s: cannot lseek back to original position"),
- quote (filename));
- return false;
- }
-
- err = copy_fd (fd, stdout, bytes_remaining - n_elide);
+ return true;
+
+ enum Copy_fd_status err = copy_fd (fd, bytes_remaining - n_elide);
if (err == COPY_FD_OK)
- return true;
+ return true;
diagnose_copy_fd_failure (err, filename);
return false;
}
}
-/* Print all but the last N_ELIDE lines from the input stream
- open for reading via file descriptor FD.
+/* For an input file with name FILENAME and descriptor FD,
+ output all but the last N_ELIDE_0 bytes.
+ If CURRENT_POS is nonnegative, the input file is positioned there
+ and should be repositioned to just before the elided bytes.
Buffer the specified number of lines as a linked list of LBUFFERs,
adding them as needed. Return true if successful. */
static bool
-elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
+elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide,
+ off_t current_pos)
{
struct linebuffer
{
@@ -469,6 +503,7 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
size_t nlines;
struct linebuffer *next;
};
+ uintmax_t desired_pos = current_pos;
typedef struct linebuffer LBUFFER;
LBUFFER *first, *last, *tmp;
size_t total_lines = 0; /* Total number of newlines in all buffers. */
@@ -487,64 +522,73 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
{
n_read = safe_read (fd, tmp->buffer, BUFSIZ);
if (n_read == 0 || n_read == SAFE_READ_ERROR)
- break;
+ break;
+
+ if (! n_elide)
+ {
+ desired_pos += n_read;
+ xwrite_stdout (tmp->buffer, n_read);
+ continue;
+ }
+
tmp->nbytes = n_read;
tmp->nlines = 0;
tmp->next = NULL;
/* Count the number of newlines just read. */
{
- char const *buffer_end = tmp->buffer + n_read;
- char const *p = tmp->buffer;
- while ((p = memchr (p, '\n', buffer_end - p)))
- {
- ++p;
- ++tmp->nlines;
- }
+ char const *buffer_end = tmp->buffer + n_read;
+ char const *p = tmp->buffer;
+ while ((p = memchr (p, line_end, buffer_end - p)))
+ {
+ ++p;
+ ++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, `n_read' can
+ one to it. This is because when reading from a pipe, 'n_read' 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;
- }
+ {
+ 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 (n_elide < total_lines - first->nlines)
- {
- fwrite (first->buffer, 1, first->nbytes, stdout);
- tmp = first;
- total_lines -= first->nlines;
- first = first->next;
- }
- else
- tmp = xmalloc (sizeof (LBUFFER));
- }
+ {
+ /* 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 (n_elide < total_lines - first->nlines)
+ {
+ desired_pos += first->nbytes;
+ xwrite_stdout (first->buffer, first->nbytes);
+ tmp = first;
+ total_lines -= first->nlines;
+ first = first->next;
+ }
+ else
+ tmp = xmalloc (sizeof (LBUFFER));
+ }
}
free (tmp);
if (n_read == SAFE_READ_ERROR)
{
- error (0, errno, _("error reading %s"), quote (filename));
+ error (0, errno, _("error reading %s"), quoteaf (filename));
ok = false;
goto free_lbuffers;
}
/* If we read any bytes at all, count the incomplete line
on files that don't end with a newline. */
- if (last->nbytes && last->buffer[last->nbytes - 1] != '\n')
+ if (last->nbytes && last->buffer[last->nbytes - 1] != line_end)
{
++last->nlines;
++total_lines;
@@ -552,23 +596,25 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
for (tmp = first; n_elide < total_lines - tmp->nlines; tmp = tmp->next)
{
- fwrite (tmp->buffer, 1, tmp->nbytes, stdout);
+ desired_pos += tmp->nbytes;
+ xwrite_stdout (tmp->buffer, tmp->nbytes);
total_lines -= tmp->nlines;
}
- /* Print the first `total_lines - n_elide' lines of tmp->buffer. */
+ /* Print the first 'total_lines - n_elide' lines of tmp->buffer. */
if (n_elide < total_lines)
{
size_t n = total_lines - n_elide;
char const *buffer_end = tmp->buffer + tmp->nbytes;
char const *p = tmp->buffer;
- while (n && (p = memchr (p, '\n', buffer_end - p)))
- {
- ++p;
- ++tmp->nlines;
- --n;
- }
- fwrite (tmp->buffer, 1, p - tmp->buffer, stdout);
+ while (n && (p = memchr (p, line_end, buffer_end - p)))
+ {
+ ++p;
+ ++tmp->nlines;
+ --n;
+ }
+ desired_pos += p - tmp->buffer;
+ xwrite_stdout (tmp->buffer, p - tmp->buffer);
}
free_lbuffers:
@@ -578,14 +624,17 @@ free_lbuffers:
free (first);
first = tmp;
}
+
+ if (0 <= current_pos && elseek (fd, desired_pos, SEEK_SET, filename) < 0)
+ ok = false;
return ok;
}
/* 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.
@@ -594,37 +643,35 @@ free_lbuffers:
in a less efficient implementation or a messy interface. */
static bool
elide_tail_lines_seekable (const char *pretty_filename, int fd,
- uintmax_t n_lines,
- off_t start_pos, off_t end_pos)
+ uintmax_t n_lines,
+ 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'. */
+ /* 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
+ /* 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 (off_t)];
- 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)
{
- error (0, errno, _("error reading %s"), quote (pretty_filename));
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
return false;
}
+ /* n_lines == 0 case needs special treatment. */
+ const bool all_lines = !n_lines;
+
/* Count the incomplete line on files that don't end with a newline. */
- if (bytes_read && buffer[bytes_read - 1] != '\n')
+ if (n_lines && bytes_read && buffer[bytes_read - 1] != line_end)
--n_lines;
while (1)
@@ -633,106 +680,92 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd,
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)
- {
- /* Found it. */
- /* If necessary, restore the file pointer and copy
- input to output up to position, POS. */
- 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;
- }
-
- err = copy_fd (fd, stdout, pos - start_pos);
- if (err != COPY_FD_OK)
- {
- diagnose_copy_fd_failure (err, pretty_filename);
- return false;
- }
- }
-
- /* Output the initial portion of the buffer
- in which we found the desired newline byte.
- Don't bother testing for failure for such a small amount.
- Any failure will be detected upon close. */
- fwrite (buffer, 1, n + 1, stdout);
- return true;
- }
- }
+ {
+ if (all_lines)
+ n -= 1;
+ else
+ {
+ char const *nl;
+ nl = memrchr (buffer, line_end, n);
+ if (nl == NULL)
+ break;
+ n = nl - buffer;
+ }
+ if (n_lines-- == 0)
+ {
+ /* Found it. */
+ /* If necessary, restore the file pointer and copy
+ input to output up to position, POS. */
+ if (start_pos < pos)
+ {
+ enum Copy_fd_status err;
+ if (elseek (fd, start_pos, SEEK_SET, pretty_filename) < 0)
+ return false;
+
+ err = copy_fd (fd, pos - start_pos);
+ if (err != COPY_FD_OK)
+ {
+ diagnose_copy_fd_failure (err, pretty_filename);
+ return false;
+ }
+ }
+
+ /* Output the initial portion of the buffer
+ in which we found the desired newline byte. */
+ xwrite_stdout (buffer, n + 1);
+
+ /* Set file pointer to the byte after what we've output. */
+ return 0 <= elseek (fd, pos + n + 1, SEEK_SET, pretty_filename);
+ }
+ }
/* Not enough newlines in that bufferfull. */
if (pos == start_pos)
- {
- /* Not enough lines in the file. */
- return true;
- }
+ {
+ /* Not enough lines in the file. */
+ return true;
+ }
pos -= BUFSIZ;
- if (lseek (fd, pos, SEEK_SET) < 0)
- {
- char offset_buf[INT_BUFSIZE_BOUND (off_t)];
- 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)
- {
- error (0, errno, _("error reading %s"), quote (pretty_filename));
- return false;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (pretty_filename));
+ return false;
+ }
/* FIXME: is this dead code?
- Consider the test, pos == start_pos, above. */
+ Consider the test, pos == start_pos, above. */
if (bytes_read == 0)
- return true;
+ return true;
}
}
-/* 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,
+ struct stat const *st, off_t current_pos)
{
- if (!presume_input_pipe)
+ off_t size = st->st_size;
+ if (presume_input_pipe || size <= ST_BLKSIZE (*st))
+ return elide_tail_lines_pipe (filename, fd, n_elide, current_pos);
+ 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, (off_t) 0, SEEK_CUR);
- off_t end_pos = lseek (fd, (off_t) 0, SEEK_END);
- if (0 <= start_pos && start_pos < end_pos)
- {
- /* If the file is empty, we're done. */
- if (end_pos == 0)
- return true;
-
- return elide_tail_lines_seekable (filename, fd, n_elide,
- start_pos, end_pos);
- }
-
- /* lseek failed or the end offset precedes start.
- Fall through. */
- }
+ but not counting the last byte of the file.
+ If found, write from current position to OFF, inclusive.
+ Otherwise, just return true. */
- return elide_tail_lines_pipe (filename, fd, n_elide);
+ return (size <= current_pos
+ || elide_tail_lines_seekable (filename, fd, n_elide,
+ current_pos, size));
+ }
}
static bool
@@ -745,17 +778,16 @@ head_bytes (const char *filename, int fd, uintmax_t bytes_to_write)
{
size_t bytes_read;
if (bytes_to_write < bytes_to_read)
- bytes_to_read = bytes_to_write;
+ bytes_to_read = bytes_to_write;
bytes_read = safe_read (fd, buffer, bytes_to_read);
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- return false;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ return false;
+ }
if (bytes_read == 0)
- break;
- if (fwrite (buffer, 1, bytes_read, stdout) < bytes_read)
- error (EXIT_FAILURE, errno, _("write error"));
+ break;
+ xwrite_stdout (buffer, bytes_read);
bytes_to_write -= bytes_read;
}
return true;
@@ -772,31 +804,28 @@ head_lines (const char *filename, int fd, uintmax_t lines_to_write)
size_t bytes_to_write = 0;
if (bytes_read == SAFE_READ_ERROR)
- {
- error (0, errno, _("error reading %s"), quote (filename));
- return false;
- }
+ {
+ error (0, errno, _("error reading %s"), quoteaf (filename));
+ return false;
+ }
if (bytes_read == 0)
- break;
+ break;
while (bytes_to_write < bytes_read)
- if (buffer[bytes_to_write++] == '\n' && --lines_to_write == 0)
- {
- off_t n_bytes_past_EOL = bytes_read - bytes_to_write;
- /* If we have read more data than that on the specified number
- of lines, try to seek back to the position we would have
- 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));
- }
- break;
- }
- if (fwrite (buffer, 1, bytes_to_write, stdout) < bytes_to_write)
- error (EXIT_FAILURE, errno, _("write error"));
+ if (buffer[bytes_to_write++] == line_end && --lines_to_write == 0)
+ {
+ off_t n_bytes_past_EOL = bytes_read - bytes_to_write;
+ /* If we have read more data than that on the specified number
+ of lines, try to seek back to the position we would have
+ gotten to had we been reading one byte at a time. */
+ if (lseek (fd, -n_bytes_past_EOL, SEEK_CUR) < 0)
+ {
+ struct stat st;
+ if (fstat (fd, &st) != 0 || S_ISREG (st.st_mode))
+ elseek (fd, -n_bytes_past_EOL, SEEK_CUR, filename);
+ }
+ break;
+ }
+ xwrite_stdout (buffer, bytes_to_write);
}
return true;
}
@@ -810,14 +839,24 @@ head (const char *filename, int fd, uintmax_t n_units, bool count_lines,
if (elide_from_end)
{
+ off_t current_pos = -1;
+ struct stat st;
+ if (fstat (fd, &st) != 0)
+ {
+ error (0, errno, _("cannot fstat %s"),
+ quoteaf (filename));
+ return false;
+ }
+ if (! presume_input_pipe && usable_st_size (&st))
+ {
+ 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, &st, current_pos);
else
- {
- return elide_tail_bytes_file (filename, fd, n_units);
- }
+ return elide_tail_bytes_file (filename, fd, n_units, &st, current_pos);
}
if (count_lines)
return head_lines (filename, fd, n_units);
@@ -827,7 +866,7 @@ head (const char *filename, int fd, uintmax_t n_units, bool count_lines,
static bool
head_file (const char *filename, uintmax_t n_units, bool count_lines,
- bool elide_from_end)
+ bool elide_from_end)
{
int fd;
bool ok;
@@ -839,29 +878,29 @@ head_file (const char *filename, uintmax_t n_units, bool count_lines,
fd = STDIN_FILENO;
filename = _("standard input");
if (O_BINARY && ! isatty (STDIN_FILENO))
- freopen (NULL, "rb", stdin);
+ xfreopen (NULL, "rb", stdin);
}
else
{
fd = open (filename, O_RDONLY | O_BINARY);
if (fd < 0)
- {
- error (0, errno, _("cannot open %s for reading"), quote (filename));
- return false;
- }
+ {
+ error (0, errno, _("cannot open %s for reading"), quoteaf (filename));
+ return false;
+ }
}
ok = head (filename, fd, n_units, count_lines, elide_from_end);
if (!is_stdin && close (fd) != 0)
{
- error (0, errno, _("closing %s"), quote (filename));
+ error (0, errno, _("failed to close %s"), quoteaf (filename));
return false;
}
return ok;
}
-/* Convert a string of decimal digits, N_STRING, with a single, optional suffix
- character (b, k, or m) to an integral value. Upon successful conversion,
+/* Convert a string of decimal digits, N_STRING, with an optional suffix
+ to an integral value. Upon successful conversion,
return that value. If it cannot be converted, give a diagnostic and exit.
COUNT_LINES indicates whether N_STRING is a number of bytes or a number
of lines. It is used solely to give a more specific diagnostic. */
@@ -869,27 +908,9 @@ head_file (const char *filename, uintmax_t n_units, bool count_lines,
static uintmax_t
string_to_integer (bool count_lines, const char *n_string)
{
- strtol_error s_err;
- uintmax_t n;
-
- s_err = xstrtoumax (n_string, NULL, 10, &n, "bkm");
-
- if (s_err == LONGINT_OVERFLOW)
- {
- error (EXIT_FAILURE, 0,
- _("%s: %s is so large that it is not representable"), n_string,
- count_lines ? _("number of lines") : _("number of bytes"));
- }
-
- if (s_err != LONGINT_OK)
- {
- error (EXIT_FAILURE, 0, "%s: %s", n_string,
- (count_lines
- ? _("invalid number of lines")
- : _("invalid number of bytes")));
- }
-
- return n;
+ return xdectoumax (n_string, 0, UINTMAX_MAX, "bkKmMGTPEZY0",
+ count_lines ? _("invalid number of lines")
+ : _("invalid number of bytes"), 0);
}
int
@@ -917,7 +938,7 @@ main (int argc, char **argv)
char const *const *file_list;
initialize_main (&argc, &argv);
- program_name = argv[0];
+ set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
@@ -928,6 +949,8 @@ main (int argc, char **argv)
print_headers = false;
+ line_end = '\n';
+
if (1 < argc && argv[1][0] == '-' && ISDIGIT (argv[1][1]))
{
char *a = argv[1];
@@ -936,7 +959,7 @@ main (int argc, char **argv)
char multiplier_char = 0;
/* Old option syntax; a dash, one or more digits, and one or
- more option letters. Move past the number. */
+ more option letters. Move past the number. */
do ++a;
while (ISDIGIT (*a));
@@ -945,44 +968,48 @@ main (int argc, char **argv)
/* Parse any appended option letters. */
for (; *a; a++)
- {
- switch (*a)
- {
- case 'c':
- count_lines = false;
- multiplier_char = 0;
- break;
-
- case 'b':
- case 'k':
- case 'm':
- count_lines = false;
- multiplier_char = *a;
- break;
-
- case 'l':
- count_lines = true;
- break;
-
- case 'q':
- header_mode = never;
- break;
-
- case 'v':
- header_mode = always;
- break;
-
- default:
- error (0, 0, _("invalid trailing option -- %c"), *a);
- usage (EXIT_FAILURE);
- }
- }
+ {
+ switch (*a)
+ {
+ case 'c':
+ count_lines = false;
+ multiplier_char = 0;
+ break;
+
+ case 'b':
+ case 'k':
+ case 'm':
+ count_lines = false;
+ multiplier_char = *a;
+ break;
+
+ case 'l':
+ count_lines = true;
+ break;
+
+ case 'q':
+ header_mode = never;
+ break;
+
+ case 'v':
+ header_mode = always;
+ break;
+
+ case 'z':
+ line_end = '\0';
+ break;
+
+ default:
+ error (0, 0, _("invalid trailing option -- %c"), *a);
+ usage (EXIT_FAILURE);
+ }
+ }
/* Append the multiplier character (if any) onto the end of
- the digit string. Then add NUL byte if necessary. */
+ the digit string. Then add NUL byte if necessary. */
*end_n_string = multiplier_char;
if (multiplier_char)
- *(++end_n_string) = 0;
+ *(++end_n_string) = 0;
n_units = string_to_integer (count_lines, n_string);
@@ -992,48 +1019,52 @@ main (int argc, char **argv)
argc--;
}
- while ((c = getopt_long (argc, argv, "c:n:qv0123456789", long_options, NULL))
- != -1)
+ while ((c = getopt_long (argc, argv, "c:n:qvz0123456789", long_options, NULL))
+ != -1)
{
switch (c)
- {
- case PRESUME_INPUT_PIPE_OPTION:
- presume_input_pipe = true;
- break;
-
- case 'c':
- count_lines = false;
- elide_from_end = (*optarg == '-');
- if (elide_from_end)
- ++optarg;
- n_units = string_to_integer (count_lines, optarg);
- break;
-
- case 'n':
- count_lines = true;
- elide_from_end = (*optarg == '-');
- if (elide_from_end)
- ++optarg;
- n_units = string_to_integer (count_lines, optarg);
- break;
-
- case 'q':
- header_mode = never;
- break;
-
- case 'v':
- header_mode = always;
- break;
-
- case_GETOPT_HELP_CHAR;
-
- case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
-
- default:
- if (ISDIGIT (c))
- error (0, 0, _("invalid trailing option -- %c"), c);
- usage (EXIT_FAILURE);
- }
+ {
+ case PRESUME_INPUT_PIPE_OPTION:
+ presume_input_pipe = true;
+ break;
+
+ case 'c':
+ count_lines = false;
+ elide_from_end = (*optarg == '-');
+ if (elide_from_end)
+ ++optarg;
+ n_units = string_to_integer (count_lines, optarg);
+ break;
+
+ case 'n':
+ count_lines = true;
+ elide_from_end = (*optarg == '-');
+ if (elide_from_end)
+ ++optarg;
+ n_units = string_to_integer (count_lines, optarg);
+ break;
+
+ case 'q':
+ header_mode = never;
+ break;
+
+ case 'v':
+ header_mode = always;
+ break;
+
+ case 'z':
+ line_end = '\0';
+ break;
+
+ case_GETOPT_HELP_CHAR;
+
+ case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
+
+ default:
+ if (ISDIGIT (c))
+ error (0, 0, _("invalid trailing option -- %c"), c);
+ usage (EXIT_FAILURE);
+ }
}
if (header_mode == always
@@ -1042,17 +1073,17 @@ main (int argc, char **argv)
if ( ! count_lines && elide_from_end && OFF_T_MAX < n_units)
{
- char umax_buf[INT_BUFSIZE_BOUND (uintmax_t)];
- error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"),
- umaxtostr (n_units, umax_buf));
+ char umax_buf[INT_BUFSIZE_BOUND (n_units)];
+ error (EXIT_FAILURE, EOVERFLOW, "%s: %s", _("invalid number of bytes"),
+ quote (umaxtostr (n_units, umax_buf)));
}
file_list = (optind < argc
- ? (char const *const *) &argv[optind]
- : default_file_list);
+ ? (char const *const *) &argv[optind]
+ : default_file_list);
if (O_BINARY && ! isatty (STDOUT_FILENO))
- freopen (NULL, "wb", stdout);
+ xfreopen (NULL, "wb", stdout);
for (i = 0; file_list[i]; ++i)
ok &= head_file (file_list[i], n_units, count_lines, elide_from_end);
@@ -1060,5 +1091,5 @@ main (int argc, char **argv)
if (have_read_stdin && close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno, "-");
- exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
+ return ok ? EXIT_SUCCESS : EXIT_FAILURE;
}