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