dd conv=unblock: print final newline consistently
authorJim Meyering <meyering@redhat.com>
Wed, 9 Sep 2009 14:48:02 +0000 (16:48 +0200)
committerJim Meyering <meyering@redhat.com>
Thu, 10 Sep 2009 10:25:04 +0000 (12:25 +0200)
* src/dd.c (dd_copy) [C_UNBLOCK]: Always print the final newline for
non-empty output, not just when output size is a multiple of cbs.
* doc/coreutils.texi (dd invocation) [conv=unblock]: Mention that dd
prints a newline after each output record, not just when replacing
trailing spaces.
Reported by Ulrich Drepper.
* tests/dd/unblock: New file.  Test for this.
* tests/Makefile.am (TESTS): Add it.
* NEWS (Bug fixes): Mention it.

NEWS
doc/coreutils.texi
src/dd.c
tests/Makefile.am
tests/dd/unblock [new file with mode: 0755]

diff --git a/NEWS b/NEWS
index 7805fe5..26dcd59 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   printing a summary to stderr.
   [bug introduced in coreutils-6.11]
 
+  dd cbs=N conv=unblock would fail to print a final newline when the size
+  of the input was not a multiple of N bytes.
+  [the non-conforming behavior dates back to the initial implementation]
+
   df no longer requires that each command-line argument be readable
   [bug introduced in coreutils-7.3]
 
index 16ff613..93f9390 100644 (file)
@@ -7775,8 +7775,8 @@ input newline with a space and padding with spaces as necessary.
 
 @item unblock
 @opindex unblock
-Replace trailing spaces in each @samp{cbs}-sized input block with a
-newline.
+Remove any trailing spaces in each @samp{cbs}-sized input block,
+and append a newline.
 
 The @samp{block} and @samp{unblock} conversions are mutually exclusive.
 
index 04665f9..76a31e9 100644 (file)
--- a/src/dd.c
+++ b/src/dd.c
@@ -1785,10 +1785,11 @@ dd_copy (void)
         output_char (space_character);
     }
 
-  if ((conversions_mask & C_UNBLOCK) && col == conversion_blocksize)
-    /* Add a final '\n' if there are exactly `conversion_blocksize'
-       characters in the final record. */
-    output_char (newline_character);
+  if (col && (conversions_mask & C_UNBLOCK))
+    {
+      /* If there was any output, add a final '\n'.  */
+      output_char (newline_character);
+    }
 
   /* Write out the last block. */
   if (oc != 0)
index 42a12cf..1de53bf 100644 (file)
@@ -309,6 +309,7 @@ TESTS =                                             \
   dd/skip-seek2                                        \
   dd/skip-seek-past-file                       \
   dd/stderr                                    \
+  dd/unblock                                   \
   dd/unblock-sync                              \
   df/total-verify                              \
   du/2g                                                \
diff --git a/tests/dd/unblock b/tests/dd/unblock
new file mode 100755 (executable)
index 0000000..6a3634c
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+# Exercise dd's conv=unblock mode
+
+# Copyright (C) 2009 Free Software Foundation, Inc.
+
+# 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 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
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# 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, see <http://www.gnu.org/licenses/>.
+
+use strict;
+
+(my $program_name = $0) =~ s|.*/||;
+
+# Turn off localization of executable's output.
+@ENV{qw(LANGUAGE LANG LC_ALL)} = ('C') x 3;
+my $out = 'out';
+
+my @t =
+  (
+   # An empty test name signals that these are the arguments to use for the
+   # following tests.
+   ['', [qw (cbs=3 conv=unblock status=noxfer < )]],
+   ['0', '', ''],
+   ['1', "a\n  ", "a\n\n\n"],
+   ['2', "a\n ",  "a\n\n"],
+   ['3', "a  ",   "a\n"],
+   ['4', "a \n ", "a \n\n\n"],
+   ['5', "a \n",  "a \n\n"],
+   ['6', "a   ",  "a\n\n"],
+   ['7', "a  \n", "a\n\n\n"],
+  );
+
+my @Tests;
+my $args;
+foreach my $t (@t)
+  {
+    $t->[0] eq ''
+      and $args = $t->[1], next;
+
+    push @Tests, [$t->[0], @$args, {IN=>$t->[1]}, {OUT=>$t->[2]},
+                  {ERR_SUBST=>'s/^\d+\+\d+ records (?:in|out)$//'},
+                  {ERR=>"\n\n"}];
+  }
+
+my $save_temps = $ENV{DEBUG};
+my $verbose = $ENV{VERBOSE};
+
+my $prog = 'dd';
+my $fail = run_tests ($program_name, $prog, \@Tests, $save_temps, $verbose);
+exit $fail;