Subject: bug#64485: "diff -y" broken since yesterday

From: Bruno Haible <[email-masked]>
Date: Wed, 05 Jul 2023 19:30:31 +0200

Hi Paul,

I'm trying to understand the meaning of 'in_position' and 'out_position' in
function 'print_half_line'. While doing this, I spotted a bug:

$ cat foo1.txt
a
ab
abc
$ cat foo2.txt
ab
abcd
$ src/diff -t -y foo1.txt foo2.txt
a                                                                <
ab                                                                   ab
abc                                                                |  abcd

This is obviously not how it's intended to be. The attached patch fixes it.
The cause is that in the case of a character of width 1, the assignment

  out_position = in_position;

was lost in commit 05cdf3102ef3fb261db311a3e5d090fb63dbc792 (yesterday).
With this patch, the result is again as it should be:

$ src/diff -t -y foo1.txt foo2.txt
a                                                               <
ab                                                                 ab
abc                                                             |  abcd

The patch also improves comments in this function. So that next time,
I (or anyone else) don't spent 30 minutes to understand the difference
between 'in_position' and 'out_position'.

Bruno

>From 9826aa322e65815288ae63c3a0bb81c1c1267cd8 Mon Sep 17 00:00:00 2001
From: Bruno Haible <[email-masked]>
Date: Wed, 5 Jul 2023 19:26:52 +0200
Subject: [PATCH] diff: Fix "diff -y" output

This fixes a regression from 2023-07-04.

* src/side.c (print_half_line): Restore the assignment to out_position.
---
 src/side.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/src/side.c b/src/side.c
index 8404c3a..49c7fe5 100644
--- a/src/side.c
+++ b/src/side.c
@@ -66,14 +66,19 @@ tab_from_to (intmax_t from, intmax_t to)
 }
 
 /* Print the text for half an sdiff line.  This means truncate to
-   width observing tabs, and trim a trailing newline.  Return the
-   last column written (not the number of chars).  */
+   OUT_BOUND columns, observing tabs, and trim a trailing newline.
+   Return the presumed column position on the output device after
+   the write (not the number of chars).  */
 
 static intmax_t
 print_half_line (char const *const *line, intmax_t indent, intmax_t out_bound)
 {
   FILE *out = outfile;
+  /* IN_POSITION is the current column position if we were outputting the
+     entire line, i.e. ignoring OUT_BOUND.  */
   intmax_t in_position = 0;
+  /* OUT_POSITION is the current column position.  It stays <= OUT_BOUND
+     at any moment.  */
   intmax_t out_position = 0;
   char const *text_pointer = line[0];
   char const *text_limit = line[1];
@@ -188,9 +193,14 @@ print_half_line (char const *const *line, intmax_t indent, intmax_t out_bound)
         case 'z': case '{': case '|': case '}': case '~':
 	  if (ckd_add (&in_position, in_position, 1))
 	    return out_position;
-	  FALLTHROUGH;
+	  if (in_position <= out_bound)
+	    {
+	      out_position = in_position;
+	      putc (c, out);
+	    }
+	  break;
 
-	/* Print width already handled.  */
+	/* Print width 0.  */
 	case '\0': case '\f': case '\v':
 	  if (in_position <= out_bound)
 	    putc (c, out);
-- 
2.34.1