src/head.c - coreutils-8.23
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#include <config.h>
#include <stdio.h>
#include <getopt.h>
#include <sys/types.h>
#include "system.h"
#include "error.h"
#include "full-read.h"
#include "quote.h"
#include "quotearg.h"
#include "safe-read.h"
#include "xfreopen.h"
#include "xstrtol.h"
#define PROGRAM_NAME "head"
#define AUTHORS \
proper_name ("David MacKenzie"), \
proper_name ("Jim Meyering")
#define DEFAULT_NUMBER 10
static bool presume_input_pipe;
static bool print_headers;
enum header_mode
{
multiple_files, always, never
};
static bool have_read_stdin;
enum Copy_fd_status
{
COPY_FD_OK = 0,
COPY_FD_READ_ERROR,
COPY_FD_UNEXPECTED_EOF
};
enum
{
PRESUME_INPUT_PIPE_OPTION = CHAR_MAX + 1
};
static struct option const long_options[] =
{
{"bytes", required_argument, NULL, 'c'},
{"lines", required_argument, NULL, 'n'},
{"-presume-input-pipe", no_argument, NULL,
PRESUME_INPUT_PIPE_OPTION}, {"quiet", no_argument, NULL, 'q'},
{"silent", no_argument, NULL, 'q'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
{NULL, 0, NULL, 0}
};
void
usage (int status)
{
if (status != EXIT_SUCCESS)
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\
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\
"), stdout);
emit_mandatory_arg_note ();
fputs (_("\
-c, --bytes=[-]K print the first K bytes of each file;\n\
with the leading '-', print all but the last\n\
K bytes of each file\n\
-n, --lines=[-]K print the first K lines instead of the first 10;\n\
with the leading '-', print all but the last\n\
K lines of each file\n\
"), stdout);
fputs (_("\
-q, --quiet, --silent never print headers giving file names\n\
-v, --verbose always print headers giving file names\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
K 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);
emit_ancillary_info ();
}
exit (status);
}
static void
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_UNEXPECTED_EOF:
error (0, errno, _("%s: file has shrunk too much"), quote (filename));
break;
default:
abort ();
}
}
static void
write_header (const char *filename)
{
static bool first_file = true;
printf ("%s==> %s <==\n", (first_file ? "" : "\n"), filename);
first_file = false;
}
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); error (EXIT_FAILURE, errno, _("error writing %s"),
quote ("standard output"));
}
}
static enum Copy_fd_status
copy_fd (int src_fd, uintmax_t n_bytes)
{
char buf[BUFSIZ];
const size_t buf_size = sizeof (buf);
while (0 < 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;
n_bytes -= n_read;
if (n_read == 0 && n_bytes != 0)
return COPY_FD_UNEXPECTED_EOF;
xwrite_stdout (buf, n_read);
}
return COPY_FD_OK;
}
static bool
elide_tail_bytes_pipe (const char *filename, int fd, uintmax_t n_elide_0)
{
size_t n_elide = n_elide_0;
#ifndef HEAD_TAIL_PIPE_READ_BUFSIZE
# define HEAD_TAIL_PIPE_READ_BUFSIZE BUFSIZ
#endif
#define READ_BUFSIZE HEAD_TAIL_PIPE_READ_BUFSIZE
FIXMEFIXME#ifndef HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD
# define HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD 1024 * 1024
#endif
#if HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD < 2 * READ_BUFSIZE
"HEAD_TAIL_PIPE_BYTECOUNT_THRESHOLD must be at least 2 * READ_BUFSIZE"
#endif
if (SIZE_MAX < n_elide_0 + READ_BUFSIZE)
{
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));
}
FIXME
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;
bool i;
char *b[2];
b[0] = xnmalloc (2, n_to_read);
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;
}
if (n_read <= n_elide)
{
if (first)
{
}
else
{
delta = n_elide - n_read;
}
}
eof = true;
}
if (! first)
xwrite_stdout (b[!i] + READ_BUFSIZE, n_elide - delta);
first = false;
if (n_elide < n_read)
xwrite_stdout (b[i], n_read - n_elide);
}
free (b[0]);
return ok;
}
else
{
bool ok = true;
bool eof = false;
size_t n_read;
bool buffered_enough;
size_t i, i_next;
char **b = NULL;
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;
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 (n_array_alloc == i)
{
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"), quote (filename));
ok = false;
goto free_mem;
}
eof = true;
}
if (i + 1 == n_bufs)
buffered_enough = true;
if (buffered_enough)
xwrite_stdout (b[i_next], 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)
{
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)
{
size_t y = READ_BUFSIZE - rem;
size_t x = n_read - y;
xwrite_stdout (b[i_next], x);
}
}
free_mem:
for (i = 0; i < n_alloc; i++)
free (b[i]);
free (b);
return ok;
}
}
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;
}
static bool
elide_tail_bytes_file (const char *filename, int fd, uintmax_t n_elide,
off_t current_pos, off_t size)
{
if (size < 0)
return elide_tail_bytes_pipe (filename, fd, n_elide);
else
{
off_t diff = size - current_pos;
off_t bytes_remaining = diff < 0 ? 0 : diff;
if (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;
diagnose_copy_fd_failure (err, filename);
return false;
}
}
static bool
elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide)
{
struct linebuffer
{
char buffer[BUFSIZ];
size_t nbytes;
size_t nlines;
struct linebuffer *next;
};
typedef struct linebuffer LBUFFER;
LBUFFER *first, *last, *tmp;
size_t total_lines = 0; bool ok = true;
size_t n_read;
first = last = xmalloc (sizeof (LBUFFER));
first->nbytes = first->nlines = 0;
first->next = NULL;
tmp = xmalloc (sizeof (LBUFFER));
while (1)
{
n_read = safe_read (fd, tmp->buffer, BUFSIZ);
if (n_read == 0 || n_read == SAFE_READ_ERROR)
break;
if (! n_elide)
{
xwrite_stdout (tmp->buffer, n_read);
continue;
}
tmp->nbytes = n_read;
tmp->nlines = 0;
tmp->next = NULL;
{
char const *buffer_end = tmp->buffer + n_read;
char const *p = tmp->buffer;
while ((p = memchr (p, '\n', buffer_end - p)))
{
++p;
++tmp->nlines;
}
}
total_lines += tmp->nlines;
if (tmp->nbytes + last->nbytes < BUFSIZ)
{
memcpy (&last->buffer[last->nbytes], tmp->buffer, tmp->nbytes);
last->nbytes += tmp->nbytes;
last->nlines += tmp->nlines;
}
else
{
last = last->next = tmp;
if (n_elide < total_lines - first->nlines)
{
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));
ok = false;
goto free_lbuffers;
}
if (last->nbytes && last->buffer[last->nbytes - 1] != '\n')
{
++last->nlines;
++total_lines;
}
for (tmp = first; n_elide < total_lines - tmp->nlines; tmp = tmp->next)
{
xwrite_stdout (tmp->buffer, tmp->nbytes);
total_lines -= tmp->nlines;
}
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;
}
xwrite_stdout (tmp->buffer, p - tmp->buffer);
}
free_lbuffers:
while (first)
{
tmp = first->next;
free (first);
first = tmp;
}
return ok;
}
static bool
elide_tail_lines_seekable (const char *pretty_filename, int fd,
uintmax_t n_lines,
off_t start_pos, off_t size)
{
char buffer[BUFSIZ];
size_t bytes_read;
off_t pos = size;
bytes_read = (pos - start_pos) % BUFSIZ;
if (bytes_read == 0)
bytes_read = BUFSIZ;
pos -= bytes_read;
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));
return false;
}
const bool all_lines = !n_lines;
if (n_lines && bytes_read && buffer[bytes_read - 1] != '\n')
--n_lines;
while (1)
{
size_t n = bytes_read;
while (n)
{
if (all_lines)
n -= 1;
else
{
char const *nl;
nl = memrchr (buffer, '\n', n);
if (nl == NULL)
break;
n = nl - buffer;
}
if (n_lines-- == 0)
{
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;
}
}
xwrite_stdout (buffer, n + 1);
return 0 <= elseek (fd, pos + n + 1, SEEK_SET, pretty_filename);
}
}
if (pos == start_pos)
{
return true;
}
pos -= BUFSIZ;
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;
}
FIXME if (bytes_read == 0)
return true;
}
}
static bool
elide_tail_lines_file (const char *filename, int fd, uintmax_t n_elide,
off_t current_pos, off_t size)
{
if (size < 0)
return elide_tail_lines_pipe (filename, fd, n_elide);
else
{
return (size <= current_pos
|| elide_tail_lines_seekable (filename, fd, n_elide,
current_pos, size));
}
}
static bool
head_bytes (const char *filename, int fd, uintmax_t bytes_to_write)
{
char buffer[BUFSIZ];
size_t bytes_to_read = BUFSIZ;
while (bytes_to_write)
{
size_t bytes_read;
if (bytes_to_write < bytes_to_read)
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;
}
if (bytes_read == 0)
break;
xwrite_stdout (buffer, bytes_read);
bytes_to_write -= bytes_read;
}
return true;
}
static bool
head_lines (const char *filename, int fd, uintmax_t lines_to_write)
{
char buffer[BUFSIZ];
while (lines_to_write)
{
size_t bytes_read = safe_read (fd, buffer, BUFSIZ);
size_t bytes_to_write = 0;
if (bytes_read == SAFE_READ_ERROR)
{
error (0, errno, _("error reading %s"), quote (filename));
return false;
}
if (bytes_read == 0)
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 (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;
}
static bool
head (const char *filename, int fd, uintmax_t n_units, bool count_lines,
bool elide_from_end)
{
if (print_headers)
write_header (filename);
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, current_pos, size);
else
return elide_tail_bytes_file (filename, fd, n_units, current_pos, size);
}
if (count_lines)
return head_lines (filename, fd, n_units);
else
return head_bytes (filename, fd, n_units);
}
static bool
head_file (const char *filename, uintmax_t n_units, bool count_lines,
bool elide_from_end)
{
int fd;
bool ok;
bool is_stdin = STREQ (filename, "-");
if (is_stdin)
{
have_read_stdin = true;
fd = STDIN_FILENO;
filename = _("standard input");
if (O_BINARY && ! isatty (STDIN_FILENO))
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;
}
}
ok = head (filename, fd, n_units, count_lines, elide_from_end);
if (!is_stdin && close (fd) != 0)
{
error (0, errno, _("failed to close %s"), quote (filename));
return false;
}
return ok;
}
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, "bkKmMGTPEZY0");
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;
}
int
main (int argc, char **argv)
{
enum header_mode header_mode = multiple_files;
bool ok = true;
int c;
size_t i;
uintmax_t n_units = DEFAULT_NUMBER;
bool count_lines = true;
bool elide_from_end = false;
static char const *const default_file_list[] = {"-", NULL};
char const *const *file_list;
initialize_main (&argc, &argv);
set_program_name (argv[0]);
setlocale (LC_ALL, "");
bindtextdomain (PACKAGE, LOCALEDIR);
textdomain (PACKAGE);
atexit (close_stdout);
have_read_stdin = false;
print_headers = false;
if (1 < argc && argv[1][0] == '-' && ISDIGIT (argv[1][1]))
{
char *a = argv[1];
char *n_string = ++a;
char *end_n_string;
char multiplier_char = 0;
do ++a;
while (ISDIGIT (*a));
end_n_string = a;
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);
}
}
*end_n_string = multiplier_char;
if (multiplier_char)
*(++end_n_string) = 0;
n_units = string_to_integer (count_lines, n_string);
argv[1] = argv[0];
argv++;
argc--;
}
while ((c = getopt_long (argc, argv, "c:n:qv0123456789", 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);
}
}
if (header_mode == always
|| (header_mode == multiple_files && optind < argc - 1))
print_headers = true;
if ( ! count_lines && elide_from_end && OFF_T_MAX < n_units)
{
char umax_buf[INT_BUFSIZE_BOUND (n_units)];
error (EXIT_FAILURE, 0, _("%s: number of bytes is too large"),
umaxtostr (n_units, umax_buf));
}
file_list = (optind < argc
? (char const *const *) &argv[optind]
: default_file_list);
if (O_BINARY && ! isatty (STDOUT_FILENO))
xfreopen (NULL, "wb", stdout);
for (i = 0; file_list[i]; ++i)
ok &= head_file (file_list[i], n_units, count_lines, elide_from_end);
if (have_read_stdin && close (STDIN_FILENO) < 0)
error (EXIT_FAILURE, errno, "-");
exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
}