Subject: Re: bug#9116: Bug in unexpand --all of <spaces><tab>

From: Pádraig Brady <[email-masked]>
Date: Mon, 18 Jul 2011 16:25:11 +0100

On 18/07/11 12:18, Hallvard B Furuseth wrote:
> Unexpand --all of <7 printables, 2-8 spaces, tab, word> loses a tab.
> 
> perl -lwe 'print 1234567, " " x $_, "\t$_" for (1..9)' | unexpand --all
> -->
> 1234567 	1
> 1234567	2
> 1234567	3
> 1234567	4
> 1234567	5
> 1234567	6
> 1234567	7
> 1234567	8
> 1234567			9
> 
> Coreutils-8.12.  Old bug, has existed at least since version 6.8.

Yep, 5.97 has the same issue at least.
Interestingly the i18n patch gets it right:

$ printf "1234567        \t8\n" | unexpand -a
1234567         8
$ printf "1234567        \t8\n" | LANG=C unexpand -a
1234567 8

Looking at this for a few minutes suggests the following patch.
Though it's probably wrong, as I'm not sure why the current
code is not converting the trailing space in a field to a tab,
which is even enforced with test misc/unexpand::infloop-3.
Note the i18n patch does not maintain this trailing space,
nor does freebsd, which is what I'd expect.

$ printf "[ \t\t ]\n" | unexpand -t 2,3 | tr '\t ' ts
[ttts]
$ printf "[ \t\t ]\n" | LANG=C unexpand -t 2,3 | tr '\t ' ts
[stts]

I'll look at this later this evening.

cheers,
Pádraig.

diff --git a/src/unexpand.c b/src/unexpand.c
index 0014375..1489c4b 100644
--- a/src/unexpand.c
+++ b/src/unexpand.c
@@ -381,11 +381,14 @@ unexpand (void)

                           /* Discard pending blanks, unless it was a single
                              blank just before the previous tab stop.  */
-                          if (! (pending == 1 && one_blank_before_tab_stop))
+                          if (one_blank_before_tab_stop)
                             {
-                              pending = 0;
+                              pending = 1;
+                              pending_blank[0] = '\t';
                               one_blank_before_tab_stop = false;
                             }
+                          else
+                            pending = 0;
                         }
                       else
                         {