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
>
> .de B
> . ft B
> \\$*
> . ft
> ..
>
> 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
>
> This would be my desired output
>
> *my bold words*, not so bold words
>
> But I can't find a way to achieve it consistently. The best I could
> find was
>
> .B my bold words
> \h'-\w'\~\~'u'
> ,
> not so bold words
>
> 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.
...
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.
.\" .B bold-text text-in-previous-typeface
.de B
. if \\n(.$=0 .tm \\$0 macro expects arguments
. ft B
\\$1\c
.if \\n(.$=1 \" empty comment; puts a space on the output
. ft
..
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.
.\" .BR bold-text roman-text
.de BR
. nr sF \\n(.f \" save font selection
. if \\n(.$<2 .tm \\$0 macro expects 2 arguments, got \\n(.$
. ft B
\\$1\c
. ft R
\\$2
. ft \\n(sF \" restore font selection
. rr sF
..
.
.\" .BI bold-text italic-text
.de BR
. nr sF \\n(.f \" save font selection
. if \\n(.$<2 .tm \\$0 macro expects 2 arguments, got \\n(.$
. ft I
\\$1\c
. ft R
\\$2
. ft \\n(sF \" restore font selection
. rr sF
..
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
signature.asc
Description: PGP signature
