From bc94551f63cfc4c05a56628dfcb386707d9e98cb Mon Sep 17 00:00:00 2001 From: Richard Russon Date: Sat, 26 Sep 2015 14:22:26 +0100 Subject: head,tail: add the -z,--zero-terminated option * doc/coreutils.texi: Reference the option description. * src/head.c: Parameterize the delimiter character. * src/tail.c: Likewise. * tests/misc/head.pl: Add test case. * tests/misc/tail.pl: Likewise. * NEWS: Mention the new feature. --- src/head.c | 31 ++++++++++++++++++++++++------- src/tail.c | 26 +++++++++++++++++++------- 2 files changed, 43 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/head.c b/src/head.c index a5405aae6..282c2ea8d 100644 --- a/src/head.c +++ b/src/head.c @@ -58,6 +58,9 @@ 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 { @@ -90,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} @@ -125,6 +129,9 @@ With more than one FILE, precede each with a header giving the file name.\n\ 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); @@ -532,7 +539,7 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide, { char const *buffer_end = tmp->buffer + n_read; char const *p = tmp->buffer; - while ((p = memchr (p, '\n', buffer_end - p))) + while ((p = memchr (p, line_end, buffer_end - p))) { ++p; ++tmp->nlines; @@ -581,7 +588,7 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide, /* 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; @@ -600,7 +607,7 @@ elide_tail_lines_pipe (const char *filename, int fd, uintmax_t n_elide, 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))) + while (n && (p = memchr (p, line_end, buffer_end - p))) { ++p; ++tmp->nlines; @@ -664,7 +671,7 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd, const bool all_lines = !n_lines; /* Count the incomplete line on files that don't end with a newline. */ - if (n_lines && bytes_read && buffer[bytes_read - 1] != '\n') + if (n_lines && bytes_read && buffer[bytes_read - 1] != line_end) --n_lines; while (1) @@ -679,7 +686,7 @@ elide_tail_lines_seekable (const char *pretty_filename, int fd, else { char const *nl; - nl = memrchr (buffer, '\n', n); + nl = memrchr (buffer, line_end, n); if (nl == NULL) break; n = nl - buffer; @@ -804,7 +811,7 @@ head_lines (const char *filename, int fd, uintmax_t lines_to_write) if (bytes_read == 0) break; while (bytes_to_write < bytes_read) - if (buffer[bytes_to_write++] == '\n' && --lines_to_write == 0) + 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 @@ -942,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]; @@ -986,6 +995,10 @@ main (int argc, char **argv) header_mode = always; break; + case 'z': + line_end = '\0'; + break; + default: error (0, 0, _("invalid trailing option -- %c"), *a); usage (EXIT_FAILURE); @@ -1006,7 +1019,7 @@ main (int argc, char **argv) argc--; } - while ((c = getopt_long (argc, argv, "c:n:qv0123456789", long_options, NULL)) + while ((c = getopt_long (argc, argv, "c:n:qvz0123456789", long_options, NULL)) != -1) { switch (c) @@ -1039,6 +1052,10 @@ main (int argc, char **argv) header_mode = always; break; + case 'z': + line_end = '\0'; + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); diff --git a/src/tail.c b/src/tail.c index 9007888fc..781adf200 100644 --- a/src/tail.c +++ b/src/tail.c @@ -180,6 +180,9 @@ static bool from_start; /* 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 { @@ -238,6 +241,7 @@ static struct option const long_options[] = {"silent", no_argument, NULL, 'q'}, {"sleep-interval", required_argument, NULL, 's'}, {"verbose", no_argument, NULL, 'v'}, + {"zero-terminated", no_argument, NULL, 'z'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -296,6 +300,9 @@ With more than one FILE, precede each with a header giving the file name.\n\ with inotify and --pid=P, check process P at\n\ least once every N seconds\n\ -v, --verbose always output 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); @@ -499,7 +506,7 @@ file_lines (const char *pretty_filename, int fd, uintmax_t n_lines, *read_pos = pos + bytes_read; /* Count the incomplete line on files that don't end with a newline. */ - if (bytes_read && buffer[bytes_read - 1] != '\n') + if (bytes_read && buffer[bytes_read - 1] != line_end) --n_lines; do @@ -510,7 +517,7 @@ file_lines (const char *pretty_filename, int fd, uintmax_t n_lines, while (n) { char const *nl; - nl = memrchr (buffer, '\n', n); + nl = memrchr (buffer, line_end, n); if (nl == NULL) break; n = nl - buffer; @@ -595,7 +602,7 @@ pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines, { char const *buffer_end = tmp->buffer + n_read; char const *p = tmp->buffer; - while ((p = memchr (p, '\n', buffer_end - p))) + while ((p = memchr (p, line_end, buffer_end - p))) { ++p; ++tmp->nlines; @@ -649,7 +656,7 @@ pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines, goto free_lbuffers; /* Count the incomplete line on files that don't end with a newline. */ - if (last->buffer[last->nbytes - 1] != '\n') + if (last->buffer[last->nbytes - 1] != line_end) { ++last->nlines; ++total_lines; @@ -671,7 +678,7 @@ pipe_lines (const char *pretty_filename, int fd, uintmax_t n_lines, size_t j; for (j = total_lines - n_lines; j; --j) { - beg = memchr (beg, '\n', buffer_end - beg); + beg = memchr (beg, line_end, buffer_end - beg); assert (beg); ++beg; } @@ -857,7 +864,7 @@ start_lines (const char *pretty_filename, int fd, uintmax_t n_lines, *read_pos += bytes_read; char *p = buffer; - while ((p = memchr (p, '\n', buffer_end - p))) + while ((p = memchr (p, line_end, buffer_end - p))) { ++p; if (--n_lines == 0) @@ -2047,7 +2054,7 @@ parse_options (int argc, char **argv, { int c; - while ((c = getopt_long (argc, argv, "c:n:fFqs:v0123456789", + while ((c = getopt_long (argc, argv, "c:n:fFqs:vz0123456789", long_options, NULL)) != -1) { @@ -2124,6 +2131,10 @@ parse_options (int argc, char **argv, *header_mode = always; break; + case 'z': + line_end = '\0'; + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -2221,6 +2232,7 @@ main (int argc, char **argv) count_lines = true; forever = from_start = print_headers = false; + line_end = '\n'; obsolete_option = parse_obsolete_option (argc, argv, &n_units); argc -= obsolete_option; argv += obsolete_option; -- cgit v1.2.3-54-g00ecf