diff options
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | doc/coreutils.texi | 4 | ||||
-rw-r--r-- | src/head.c | 31 | ||||
-rw-r--r-- | src/tail.c | 26 | ||||
-rwxr-xr-x | tests/misc/head.pl | 4 | ||||
-rwxr-xr-x | tests/misc/tail.pl | 4 |
6 files changed, 58 insertions, 14 deletions
@@ -39,6 +39,9 @@ GNU coreutils NEWS -*- outline -*- Its status=progress output now uses the same format as ordinary status, perhaps with trailing spaces to erase previous progress output. + head, tail now have -z, --zero-terminated options to work with + NUL delimited items. + md5sum now supports the --ignore-missing option to allow verifying a subset of files given a larger list of checksums. This also affects sha1sum, sha224sum, sha256sum, sha384sum and sha512sum. diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 80e9a032d..a7a89ad98 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -2817,6 +2817,8 @@ Never print file name headers. @opindex --verbose Always print file name headers. +@optZeroTerminated + @end table For compatibility @command{head} also supports an obsolete option syntax @@ -3042,6 +3044,8 @@ every @var{number} seconds. @opindex --verbose Always print file name headers. +@optZeroTerminated + @end table For compatibility @command{tail} also supports an obsolete usage 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} @@ -126,6 +130,9 @@ With more than one FILE, precede each with a header giving the file name.\n\ -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 (_("\ @@ -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} @@ -297,6 +301,9 @@ With more than one FILE, precede each with a header giving the file name.\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); fputs (_("\ @@ -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; diff --git a/tests/misc/head.pl b/tests/misc/head.pl index 78644f2a5..1f565cf16 100755 --- a/tests/misc/head.pl +++ b/tests/misc/head.pl @@ -72,6 +72,10 @@ my @Tests = ['no-oct-2', '-010', {IN=>"\n"x12}, {OUT=>"\n"x10}], ['no-oct-3', '-n 08', {IN=>"\n"x12}, {OUT=>"\n"x8}], ['no-oct-4', '-c 08', {IN=>"\n"x12}, {OUT=>"\n"x8}], + + # --zero-terminated + ['zero-1', '-z -n 1', {IN=>"x\0y"}, {OUT=>"x\0"}], + ['zero-2', '-z -n 2', {IN=>"x\0y"}, {OUT=>"x\0y"}], ); @Tests = triple_test \@Tests; diff --git a/tests/misc/tail.pl b/tests/misc/tail.pl index c23102fe8..0d9bc4834 100755 --- a/tests/misc/tail.pl +++ b/tests/misc/tail.pl @@ -101,6 +101,10 @@ my @tv = ( # With textutils-1.22, this failed. ['f-pipe-1', '-f -n 1', "a\nb\n", "b\n", 0], + +# --zero-terminated +['zero-1', '-z -n 1', "x\0y", "y", 0], +['zero-2', '-z -n 2', "x\0y", "x\0y", 0], ); my @Tests; |