It was shockingly easy to implement my proposal of de-compatifying the
special logic that historical troff used to support two digits in the
single-digit form of the point-size escape.

But not so surprising in hindsight.  When you're heaping conditionals
this high in your recursive-descent parser, it is a sign that you are
headed the wrong direction, and making the syntax too hard to hold in
the head.

The heart of the change is merely this:

-    if (!inc && c != '0' && c < '4') {
+    if (compatible_flag && !inc && c != '0' && c < '4') {

The rest is commentary, documentation, and regression testing.

Why should we do this?  For those who haven't been following the sudden
burst of activity on the groff list, AT&T troff supported this weirdness
because...

the Graphic Systems C/A/T phototypesetter (the original device target
for AT&T troff) only supported a few discrete point sizes in the range
6..36.  Kernighan warned of this in the 1992 revision of CSTR #54, and
more recently, McIlroy referred to it as a "living fossil".  This DWIM
syntax is surprising to the user; it clashes with the syntax of several
other escapes (\*, \$, \f, \F, \g, \k, \m, \M, \n, \V, and \Y).  We
therefore support it only in compatibility mode.

The above also appears in a source code comment.

If I go forward with this, I'll add substantially the same material to
ChangeLog and NEWS.

This patch is not an April Fools' joke.  I mean it.  Let's fight!  :-P

Regards,
Branden
From 7e66027c5eaba8a435049fc3067400dc243d5951 Mon Sep 17 00:00:00 2001
From: "G. Branden Robinson" <g.branden.robin...@gmail.com>
Date: Wed, 1 Apr 2020 03:53:28 +1100
Subject: [PATCH] Support 2-digit \sNN only in compatibility mode.

* src/roff/troff/input.cpp: Only scan an additional digit of \sNN in
  compatibility mode.  (Also update an insufficiently general comment;
  \s(00 works just like \s0.)

* doc/groff.texi:
* man/groff.7.man: Document the changed behavior.

* src/roff/groff/groff.am:
* src/roff/groff/tests/use_point_size_escape_with_single_digit_arg.sh:
  Add regression test.
---
 doc/groff.texi                                |  5 +--
 man/groff.7.man                               | 12 +++----
 src/roff/groff/groff.am                       |  3 +-
 ...point_size_escape_with_single_digit_arg.sh | 36 +++++++++++++++++++
 src/roff/troff/input.cpp                      | 18 ++++++++--
 5 files changed, 63 insertions(+), 11 deletions(-)
 create mode 100755 src/roff/groff/tests/use_point_size_escape_with_single_digit_arg.sh

diff --git a/doc/groff.texi b/doc/groff.texi
index 65eea202..78640517 100644
--- a/doc/groff.texi
+++ b/doc/groff.texi
@@ -10051,8 +10051,9 @@ and the text begins.  Any of the following forms are valid:
 
 @table @code
 @item \s@var{n}
-Set the point size to @var{n}@tie{}points.  @var{n}@tie{}must be either
-0 or in the range 4 to@tie{}39.
+Set the point size to @var{n}@tie{}points.  @var{n}@tie{}must be a
+single digit.  In compatibility mode only, @var{n}@tie{}may also be a
+two-digit value in the range range 10 to@tie{}39.
 
 @item \s+@var{n}
 @itemx \s-@var{n}
diff --git a/man/groff.7.man b/man/groff.7.man
index e1d6a25a..8af99e55 100644
--- a/man/groff.7.man
+++ b/man/groff.7.man
@@ -3299,13 +3299,13 @@ The same as
 .ESC s \[+-]N
 Set/increase/decrease the point size to/by
 .I N
-scaled points;
+scaled points.
+.
 .I N
-must be either 0
-(restore previous size)
-or,
-for historical reasons,
-in the range 4\[en]39.
+must be a single digit,
+except in compatibility mode
+(where values from 10\[en]39 are also accepted);
+0 restores the previous point size.
 .
 Otherwise,
 same as
diff --git a/src/roff/groff/groff.am b/src/roff/groff/groff.am
index c56b9562..fae28aa5 100644
--- a/src/roff/groff/groff.am
+++ b/src/roff/groff/groff.am
@@ -46,7 +46,8 @@ groff_TESTS = \
   src/roff/groff/tests/regression_savannah_56555.sh \
   src/roff/groff/tests/string_case_xform_errors.sh \
   src/roff/groff/tests/string_case_xform_requests.sh \
-  src/roff/groff/tests/string_case_xform_unicode_escape.sh
+  src/roff/groff/tests/string_case_xform_unicode_escape.sh \
+  src/roff/groff/tests/use_point_size_escape_with_single_digit_arg.sh
 TESTS += $(groff_TESTS)
 
 groff_XFAIL_TESTS = \
diff --git a/src/roff/groff/tests/use_point_size_escape_with_single_digit_arg.sh b/src/roff/groff/tests/use_point_size_escape_with_single_digit_arg.sh
new file mode 100755
index 00000000..4dc90732
--- /dev/null
+++ b/src/roff/groff/tests/use_point_size_escape_with_single_digit_arg.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (C) 2020 Free Software Foundation, Inc.
+#
+# This file is part of groff.
+#
+# groff is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# groff is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+groff="${abs_top_builddir:-.}/test-groff"
+
+# The vertical space is so that the 36-point 'A' won't be truncated by
+# the top of the page.  That could be confusing and misleading to anyone
+# who ever has to troubleshoot this test case.
+DOC=".vs 10v
+\s36A"
+
+# Verify that the idiosyncratic behavior of \sN is supported in
+# compatibility mode...
+echo "testing \s36A in compatiblity mode (36-point 'A')" >&2
+echo "$DOC" | "$groff" -C -Z | grep -qx 's36000'
+
+# ...and not in regular mode.
+echo "testing \s36A in non-compatiblity mode (3-point '6A')" >&2
+echo "$DOC" | "$groff" -Z | grep -qx 's3000'
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index ba3842b9..e61d0e2c 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -5091,7 +5091,21 @@ static int read_size(int *x)
   }
   else if (csdigit(c)) {
     val = c - '0';
-    if (!inc && c != '0' && c < '4') {
+    // Note: Very special case!  If we have \s followed immediately by a
+    // digit (not '(', '+', or '-'), and that digit is 1, 2, or 3...read
+    // another digit!  This is because the Graphic Systems C/A/T
+    // phototypesetter (the original device target for AT&T troff) only
+    // supported a few discrete point sizes in the range 6..36.
+    // Kernighan warned of this in the 1992 revision of CSTR #54, and
+    // more recently, McIlroy referred to it as a "living fossil".  This
+    // DWIM syntax is surprising to the user; it clashes with the syntax
+    // of several other escapes (\*, \$, \f, \F, \g, \k, \m, \M, \n, \V,
+    // and \Y).  We therefore support it only in compatibility mode.
+    //
+    // See:
+    //   https://lists.gnu.org/archive/html/groff/2020-03/msg00054.html
+    // et seq.
+    if (compatible_flag && !inc && c != '0' && c < '4') {
       c = read_size_next_byte();
       if (!csdigit(c))
 	bad = 1;
@@ -5123,7 +5137,7 @@ static int read_size(int *x)
     switch (inc) {
     case 0:
       if (val == 0) {
-	// special case -- \s[0] and \s0 means to revert to previous size
+	// special case -- point size 0 means "revert to previous size"
 	*x = 0;
 	return 1;
       }
-- 
2.20.1

Attachment: signature.asc
Description: PGP signature

Reply via email to