Re: How to go back to last word end

"G. Branden Robinson" <[email protected]> Thu, 30 Jul 2026 15:35:15 -0500
Newsgroups gmane.comp.printing.groff.general
Message-ID <20260730203515.pg2co2ezzkxip6r3@illithid>
--3d6kypik36nvhss2
Content-Type: text/plain; protected-headers=v1; charset=us-ascii
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Subject: Re: How to go back to last word end
MIME-Version: 1.0

Hi Matteo,

At 2026-07-30T21:34:38+0200, Matteo Bini wrote:
> Dear GNU roff users,
> I'm having an issue writing a simple macro.

Worry not!  You're experiencing a rite of passage that *roff macro
writers have encountered steadily for 50 years.

> This is the code for my bold macro
>=20
> .de B
> .  ft B
> \\$*
> .  ft
> ..
>=20
> which works quite well, except when I want to add a punctuation mark
> right after the last bold word.

Right.

> .B my bold words
> ,
> not so bold words
>=20
> This would be my desired output
>=20
> *my bold words*, not so bold words
>=20
> But I can't find a way to achieve it consistently. The best I could
> find was
>=20
> .B my bold words
> \h'-\w'\~\~'u'
> ,
> not so bold words
>=20
> However this doesn't work when groff adds adjustment space, when it's
> in b or n adjust mode.

Yes, that's pretty fragile--not always reliable.

> Is there a way to go back to the last word end, before the space?

Better, there is way to not move forward from the end of the word in the
first place--until your macro's caller says it's okay.

> And if there is, is it portable to other roff implementations?

Absolutely.

While other solutions almost certainly exist, the idiomatic one here is
the output line continuation escape sequence, `\c`.

groff(7):

Line continuation
     When filling is enabled, input and output line breaks generally do
     not correspond.  The roff language therefore distinguishes input
     and output line continuation.
=2E..
     The \c escape sequence continues an output line.  Nothing on the
     input line after it is formatted.  In contrast to \newline, a line
     after \c is treated as a new input line, so a control character is
     recognized at its beginning.  The visual results depend on whether
     filling is enabled.  An intervening control line that causes a
     break overrides \c, flushing out the pending output line in the
     usual way.  ...

You could apply this escape sequence in a couple of different ways,
depending on how you want to design your macros.  Each has lengthy
precedent.

I write the following in a portable, AT&T-compatible *roff dialect.

First, there's the ms(7) way.

=2E\" .B bold-text text-in-previous-typeface
=2Ede B
=2E  if \\n(.$=3D0 .tm \\$0 macro expects arguments
=2E  ft B
\\$1\c
=2Eif \\n(.$=3D1 \" empty comment; puts a space on the output
=2E  ft
=2E.

Next, there's the man(7) way, where the "plain" `B` macro formats _all_
its arguments in bold, much like the `B` macro you already have, and
then two other macros format alternating macro arguments in alternating
faces implied by the macro's name.  For simplicity, I'll show you macros
that handle only two arguments.

=2E\" .BR bold-text roman-text
=2Ede BR
=2E  nr sF \\n(.f \" save font selection
=2E  if \\n(.$<2 .tm \\$0 macro expects 2 arguments, got \\n(.$
=2E  ft B
\\$1\c
=2E  ft R
\\$2
=2E  ft \\n(sF \" restore font selection
=2E  rr sF
=2E.
=2E
=2E\" .BI bold-text italic-text
=2Ede BR
=2E  nr sF \\n(.f \" save font selection
=2E  if \\n(.$<2 .tm \\$0 macro expects 2 arguments, got \\n(.$
=2E  ft I
\\$1\c
=2E  ft R
\\$2
=2E  ft \\n(sF \" restore font selection
=2E  rr sF
=2E.

Neither of these work _exactly_ like those of the corresponding names in
ms(7) and man(7), because I wanted to keep the examples simple.  For a
more precise specification of those package's behavior, see groff_ms(7)
and groff_man(7) respectively.

Let me know if this helps, or if it doesn't!

Regards,
Branden

--3d6kypik36nvhss2
Content-Type: application/pgp-signature; name="signature.asc"

-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEh3PWHWjjDgcrENwa0Z6cfXEmbc4FAmprtXwACgkQ0Z6cfXEm
bc5WEg//QDeQC9Gw9QxWTnMX7dhxtSZOH6odqfcNCMkMd4u9VGFUOUPd2T64BpB6
qancTOHxFMy3au/C1nsYVHiZGBdouHkSVHk3BFtp+1qKnoBFvp5AkDk5k43WDpCu
1mO6Dm/G7ChYVe0hDjspDxry/DQLhBBvhbhRwjWMEYqezhSKn8C7i39nrD2wsa43
q4nd8IHX7jl9oV9RDSRveZ2EizentVNe+MdQ0OyfvhgkV9ujboKvl/T7ivSVVGue
UhnBp3bZF4YI81E4tMvgchOtLgInkQFEeheY3YDlpPNSNd8OLedE6l6i1nirssRW
JcS0tYHHyUV4Z34tQgm1Kh4bTeg6H7vpnj4bFN4fPDR2WfBaFZLbMOkgXmm+TtKq
oU+MHW4XfsImwBt11U5y4NAP+E68lvV6eEU+6ZhOsGz3Ygru7gQIsX1G9WUOCHfP
PN7ge6RUCiHhAtuts5JCvIBZ+8dh3H8H6DwytOtVwjxdDqtT3EBDbJUBNU4vesc7
qwBA5dhiTWc4KrzNPWIySRPznt/CSn6V1WRyo/mdyVDtDWtG49+2OmWknenQYS3o
mmuN+K2G47RypXLAXXXN1NuMf78x72zjTRWYnId5cv/Jbsq2UO/BpmWHzAe7EdUV
g48lTz0lTR87I2mZF0gpj+sJmxykXrNRBfItAhZ4nV789Hi6DUs=
=tdo1
-----END PGP SIGNATURE-----

--3d6kypik36nvhss2--