summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--doc/coreutils.texi4
-rw-r--r--src/head.c31
-rw-r--r--src/tail.c26
-rwxr-xr-xtests/misc/head.pl4
-rwxr-xr-xtests/misc/tail.pl4
6 files changed, 58 insertions, 14 deletions
diff --git a/NEWS b/NEWS
index 192d8fa9e..eb7fc5633 100644
--- a/NEWS
+++ b/NEWS
@@ -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;