Subject: Re: bug#66095: diff quoting for names with spaces changed after v3.10-171-g69ae797392

From: Paul Eggert <[email-masked]>
Date: Wed, 20 Sep 2023 00:13:36 -0700

Thanks for reporting that. I had forgotten about the GNU patch 
convention. I installed the attached change to GNU diffutils; please 
give it a try.

GNU patch still mishandles file names containing multi-byte characters 
in some non-UTF-8 locales, but that can be fixed later when someone gets 
around to it.

From 774b942f700f0045d307923435d287c182364699 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 19 Sep 2023 23:34:43 -0700
Subject: [PATCH] diff: go back to C quoting for diff -c/-u headers

Gleb Fotengauer-Malinovskiy reported <https://bugs/gnu/org/66095>
that the recent change to quoting style broke GNU patch.
* src/util.c: Include quotearg.h.
(current_name): New static var, replacing the the old
current_name0 and current_name1.  All uses changed.
(begin_output): Go back to quoting file names for C,
not for the shell, when they contain troublesome characters.
This is not a simple revert, as the revised code handles
multi-byte characters even in non-UTF-8 locales.
* tests/filename-quoting: Revert previous change to this file.
---
 NEWS                   | 10 +++++-----
 src/util.c             | 16 ++++++++++------
 tests/filename-quoting | 18 +++++++++---------
 3 files changed, 24 insertions(+), 20 deletions(-)

diff --git a/NEWS b/NEWS
index df5c003..3638ee3 100644
--- a/NEWS
+++ b/NEWS
@@ -4,19 +4,19 @@ GNU diffutils NEWS                                    -*- outline -*-
 
 ** Improvements
 
-  Programs now quote file names more consistently.
+  Programs now quote file names more consistently in diagnostics.
   For example; "cmp 'none of' /etc/passwd" now might output
   "cmp: EOF on ‘none of’ which is empty" instead of outputting
   "cmp: EOF on none of which is empty".  In diagnostic messages
   that traditionally omit quotes and where backward compatibility
-  seems to be important, programs continue to omit quotes unless they
+  seems to be important, programs continue to omit quotes unless
   a file name contains shell metacharacters, in which case programs
   use shell quoting.  For example, although diff continues to output
   "Only in a: b" as before for most file names, it now outputs
   "Only in 'a: b': 'c: d'" instead of "Only in a: b: c: d" because the
-  file names 'a: b' and 'c: d' contain spaces.  Lastly, diff -c and -u
-  headers now quote file names for the shell when needed, instead of
-  using their own idiosyncratic quoting style.
+  file names 'a: b' and 'c: d' contain spaces.  For compatibility
+  with previous practice, diff -c and -u headers continue to quote for
+  C rather than for the shell.
 
   diff now outputs more information when symbolic links differ, e.g.,
   "Symbolic links ‘d/f’ -> ‘a’ and ‘e/f’ -> ‘b’ differ", not just
diff --git a/src/util.c b/src/util.c
index 9d6df25..c269918 100644
--- a/src/util.c
+++ b/src/util.c
@@ -26,6 +26,7 @@
 #include <error.h>
 #include <flexmember.h>
 #include <mcel.h>
+#include <quotearg.h>
 #include <system-quote.h>
 #include <xalloc.h>
 
@@ -406,8 +407,7 @@ cleanup_signal_handlers (void)
     }
 }
 
-static char const *current_name0;
-static char const *current_name1;
+static char const *current_name[2];
 static bool currently_recursive;
 static bool colors_enabled;
 
@@ -819,8 +819,8 @@ check_color_output (bool is_pipe)
 void
 setup_output (char const *name0, char const *name1, bool recursive)
 {
-  current_name0 = name0;
-  current_name1 = name1;
+  current_name[0] = name0;
+  current_name[1] = name1;
   currently_recursive = recursive;
   outfile = nullptr;
 }
@@ -836,8 +836,12 @@ begin_output (void)
   if (outfile)
     return;
 
-  char const *names[2] = {squote (0, current_name0),
-			  squote (1, current_name1)};
+  char const *names[2];
+  for (int f = 0; f < 2; f++)
+    names[f] = quotearg_n_style (f,
+				 (strchr (current_name[f], ' ')
+				  ? c_quoting_style : c_maybe_quoting_style),
+				 current_name[f]);
 
   /* Construct the header of this piece of diff.  */
   /* POSIX 1003.1-2017 specifies this format.  But there are some bugs in
diff --git a/tests/filename-quoting b/tests/filename-quoting
index 21e8c81..2e75a03 100755
--- a/tests/filename-quoting
+++ b/tests/filename-quoting
@@ -6,23 +6,23 @@
 fail=0
 
 cat <<EOF > exp- || fail=1
-diff -N -r 'a/ ' 'b/ '
+diff -N -r "a/ " "b/ "
 0a1
 > space
 EOF
 
 cat <<EOF > exp--u || fail=1
-diff -N -r -u 'a/ ' 'b/ '
---- 'a/ '
-+++ 'b/ '
+diff -N -r -u "a/ " "b/ "
+--- "a/ "
++++ "b/ "
 @@ -0,0 +1 @@
 +space
 EOF
 
 cat <<EOF > exp--c || fail=1
-diff -N -r -c 'a/ ' 'b/ '
-*** 'a/ '
---- 'b/ '
+diff -N -r -c "a/ " "b/ "
+*** "a/ "
+--- "b/ "
 ***************
 *** 0 ****
 --- 1 ----
@@ -41,8 +41,8 @@ done
 rm -f "b/ "
 
 cat <<EOF > exp || fail=1
---- 'a/'$'\t'
-+++ 'b/'$'\001'
+--- "a/\t"
++++ "b/\001"
 @@ -1 +1 @@
 -tab
 +one
-- 
2.39.2