With POSIX (and the LI18NUX2000 patch) backspaces back up by 1 column.
This matches terminal output.  Note less(1) and thus groff(1)
use per character backup accounting for their legacy bold/underline
markup format, so we'll keep --characters to enable that mode if needed.
This also avoids the issue of multiple backspaces
using the width of a single previous character.

* src/fold.c (adjust_column): Without -c,
have backspace back up by 1 column.
* doc/coreutils.texi (fold invocation): Mention that --characters
does per character width backspace accounting.
* tests/fold/fold-characters.sh: Add/Adjust test cases.
* NEWS: Mention the change in behavior.
---
 NEWS                          |  4 ++++
 doc/coreutils.texi            |  2 ++
 src/fold.c                    |  3 ++-
 tests/fold/fold-characters.sh | 20 +++++++++++++++++++-
 4 files changed, 27 insertions(+), 2 deletions(-)

diff --git a/NEWS b/NEWS
index 2460c14e6..f34d77837 100644
--- a/NEWS
+++ b/NEWS
@@ -39,6 +39,10 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   outputting to a terminal.  The previous release enabled this for all
   outputs by default, requiring setting QUOTING_STYLE=literal to disable.
 
+  'fold' when processing backspaces characters will no longer account for the
+  whole width of a previous character, unless the --characters option is used.
+  This aligns with POSIX and terminal behavior.
+
 ** Improvements
 
   'cut -c' is up to 10x faster in UTF-8 locales, and
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index a8013be0c..2eb57d4b0 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -2902,6 +2902,8 @@ characters.
 @optItemx{fold,--characters,}
 Count characters rather than columns, meaning that lines containing
 characters wider than one column will be visually longer.
+Also a backspace character will account for the whole width of the
+previous character rather than one column.
 
 @optItem{fold,-s,}
 @optItemx{fold,--spaces,}
diff --git a/src/fold.c b/src/fold.c
index 170cdb752..154353d8c 100644
--- a/src/fold.c
+++ b/src/fold.c
@@ -117,7 +117,8 @@ adjust_column (size_t column, mcel_t g)
   if (counting_mode != COUNT_BYTES)
     {
       if (g.ch == '\b')
-        column -= MIN (last_character_width, column);
+        column -= MIN (counting_mode == COUNT_CHARACTERS
+                       ? last_character_width : 1, column);
       else if (g.ch == '\r')
         column = 0;
       else if (g.ch == '\t')
diff --git a/tests/fold/fold-characters.sh b/tests/fold/fold-characters.sh
index a6a083c53..f079bd8ad 100755
--- a/tests/fold/fold-characters.sh
+++ b/tests/fold/fold-characters.sh
@@ -95,7 +95,7 @@ test $(env printf '\xC3' | fold | wc -c) = 1 || fail=1
 # Ensure backspace clamps at position 0
 # From v9.8 to v9.12 inclusive, an internal unsigned could wrap,
 # causing premature line wrapping.
-test $(env printf 'A\uB250\b\bB\n' | fold -w80 | wc -l) = 1 || fail=1
+test $(env printf 'A\uB250\b\b\b\bB\n' | fold -w80 | wc -l) = 1 || fail=1
 
 # A backspace after an initial tab must move back one column.
 env printf '\t\bX\n' > exp5 || framework_failure_
@@ -115,4 +115,22 @@ env printf 'a\u0301\t\bX\n' > exp7 || framework_failure_
 fold -w8 exp7 > out7 || fail=1
 compare exp7 out7 || fail=1
 
+# A backspace moves one column, even after a two column character.
+env printf '\uB250\bXX\n' > input8 || framework_failure_
+env printf '\uB250\bX\nX\n' > exp8 || framework_failure_
+fold -w2 input8 > out8 || fail=1
+compare exp8 out8 || fail=1
+# In character mode, the wide character and backspace cancel out.
+fold --characters -w2 input8 > out8 || fail=1
+compare input8 out8 || fail=1
+
+# A backspace moves one column, even after a zero width character.
+env printf 'a\u0301\bXX\n' > input9 || framework_failure_
+fold -w2 input9 > out9 || fail=1
+compare input9 out9 || fail=1
+# In character mode, the combining character also counts as one.
+env printf 'a\u0301\bX\nX\n' > exp9 || framework_failure_
+fold --characters -w2 input9 > out9 || fail=1
+compare exp9 out9 || fail=1
+
 Exit $fail
-- 
2.55.0


Reply via email to