Re: checkmm revisited

"G. Branden Robinson" <[email protected]>
Newsgroups gmane.comp.printing.groff.general
Message-ID <20260822033450.m3dw7tg7c6ysom2n@illithid>
At 2026-08-21T22:00:06-0400, kollar wrote:
> I’ve been going through UTP, getting ready to start UTP Revisited,

Oh, good!  I hope you noticed
<https://github.com/larrykollar/Unix-Text-Processing/pull/9>.

;-)

>and came across `checkmm` in the -mm chapter again. I searched the
>mailing list archive, and found the primary mention was a thread I
>started during UTP Revival[1], again asking about availability.
> 
> So, as one does in 2026, I asked the magic box (AI) about availability. It said that (paraphrased) "checkmm was made a part of DWB, which was never placed under an open license, but copies of the source are available on TUHS for Ultrix 3.1 and SVR4." Doing a web search “site:tuhs.org checkmm” turned up a copy of the source at:
> 
> 	https://www.tuhs.org/cgi-bin/utree.pl?file=Ultrix-3.1/src/cmd/troff/checkmm/
> 
> OK, there’s some progress. The copyright statement is “Copyright (c)
> Digital Equipment Corporation 1984, 1985, 1986. All Rights Reserved.”
> HP owns DEC’s IP now; I don’t suppose anyone knows anyone who could
> ask HP to donate 11K of 40 year old code? (Or heck, open all of
> Ultrix’s code.)

They probably don't _own_ all of Ultrix's code.  This is a perennial
problem with proprietary code bases.  Deals get done and stuff from
third parties gets grafted in without any thought given to distributing
the work as a whole in the future under FLOSS terms.

> The magic box, which does often talk nonsense, went on to say (direct
> quote this time. minus Markdown markup):
> 
> > Modern Linux systems use GNU Troff (groff). Instead of a standalone
> > checkmm binary, groff implements macro checking natively. When you
> > run "groff -mm -b -w w yourfile.mm", the -b (backtrace) and -w w
> > (enable all warnings) flags will automatically detect and print
> > syntax errors, unmatched macros, or structural issues.

Yes.  This is broadly correct.

> I expect there’s some “start with a fact, then heap BS on BS” going on
> here.

Not really, IMO.

> Groff’s diagnostics are useful, but can it detect unclosed macro
> pairs and structural issues?

Precise terminology can be helpful.

GNU troff is the formatter, and no, it can't.  It has no mechanism for
tracking macro "pairing".

A macro package itself, however, _can_.

Here's the set of error diagnostics implemented in groff mm in Git HEAD.
(Actually, this is from my working copy, but I haven't added any
lately.)

Lines 2094, 2489, 3304, and 3323 seem to be on point.

$ grep -Fn .@error contrib/mm/m.tmac
632:.   if !\B'\\$1' .@error \\$0: argument is not numeric: '\\$1'
633:.   if \\$1<0 .@error \\$0: negative motion '\\$1' not supported
662:.   if !\B'\\$1' .@error \\$0: argument is not numeric: '\\$1'
663:.   if (\\$1<0):(\\$1>1) .@error \\$0: invalid argument: '\\$1'
902:.   el          .@error \\$0: format argument is not numeric; got \
1005:.          if !\B'\\$1' .@error \\$0: width parameter is not \
1011:.          if !\B'\\$1' .@error \\$0: height parameter is not \
1101:.el          .@error \\$0: width argument is not numeric: '\\$1'
1103:.el          .@error \\$0: height argument is not numeric: '\\$2'
1187:.if \\n[.$]=0 .@error \\$0: expected 1 or 2 arguments, got \\n[.$]
1196:.if \\n[.$]=0 .@error \\$0: expected 1 to 3 arguments, got \\n[.$]
1197:.if !\B'\\$1' .@error \\$0: level argument is not numeric: '\\$1'
1868:.if \\n[pg*cols-per-page]>1 .@error \\$0: multicolumn mode already \
1883:.if \\n[pg*cols-per-page]>1 .@error \\$0: multicolumn mode already \
1957:.if \\n[pg*cols-per-page]<2 .@error \\$0: not in multi-column mode
2035:.if \\n[.$]=0 .@error \\$0: expected 1 or 2 arguments, got \\n[.$]
2094:.if \\n[ft*busy] .@error \\$0: cannot nest; missing FE?
2136:.if \\n[@pl]u<\\n[dn]u .@error \\$0: footnote bigger than page area
2401:.  el .@error \\*[ds@macro]: unrecognized format '\\$1'
2409:.  el .@error \\*[ds@macro]: unrecognized fill style '\\$2'
2489:.if \\n-[ds*lvl]<0 .@error DE: no corresponding DS
2552:.if \\n[.$]<4 .@error \\$0: expected at least 4 arguments, got \\n[.$]
2604:.if \\n[li*lvl]<1 .@error \\$0: no list active; call AL, BL, BVL, DL, \
2703:.if \\n[li*lvl]<1 .@error \\$0: no list active; call AL, BL, BVL, ... \
2717:.  el          .@error \\$0: argument is not numeric: '\\$1'
2719:.if \\n[li*i]>\\n[li*lvl] .@error \\$0 invalid argument: \\n[li*i] \
2804:.if \\n[.$]<1 .@error \\$0: expected 1 to 3 arguments, got \\n[.$]
2873:.el .@error \\$0: .TH without .TS H"
2877:.ie '\\n[.z]'tbl*header-div' .@error \\$0: .TS H but no .TH before .TE
3304:.if \\n[box*ll] .@error \\$0: cannot nest; missing B2?
3323:.if !\\n[box*ll] .@error \\$0: no corresponding B1
3505:.\" .if \\n[.$]<2 .@error "APP: too few arguments"
3520:.if \\n[.$]<2 .@error \\$0: expected 2 or 3 arguments, got \\n[.$]
3646:.if d cov*abstract .@error \\$0: only one abstract allowed
3647:.if !''\\n[.z]' .@error \\$0: no diversion allowed (previous .AS?)
3769:.if \\n[.$]<1 .@error \\$0: expected an argument
3783:.if \\n[.$]<1 .@error \\$0: expected 1 or 2 arguments, got \\n[.$]
3784:.if !r qrf*pass .@error \\$0: references uninitialized; call INITR first
3811:.if \\n[.$]<1 .@error \\$0: expected 1 or 2 arguments, got \\n[.$]
3812:.if !r qrf*pass .@error \\$0: references uninitialized; call INITR first
3830:.if \\n[.$]<1 .@error \\$0: expected 1 or 2 arguments, got \\n[.$]
3831:.if !r qrf*pass .@error \\$0: references uninitialized; call INITR first
3849:.if \\n[.$]<1 .@error \\$0: expected 1 or 2 arguments, got \\n[.$]
3850:.if !r qrf*pass .@error \\$0: references uninitialized; call INITR first
3868:.if \\n[.$]<1 .@error \\$0: expected an argument
3869:.if !r qrf*pass .@error \\$0: references uninitialized; call INITR first
3891:.if \\n[.$]<2 .@error \\$0: expected 2 or 3 arguments, got \\n[.$]
3898:.if !\\n[ind*type-is-valid] .@error \\$0: invalid index type '\\$1'
3901:.if '\\$2'' .@error \\$0: index file name not specified
3902:.if d ind*file .@error \\$0: index file name already set
3914:.if !d ind*file .@error \\$0: no active index; call INITI first
3915:.if \\n[.$]<1 .@error \\$0: expected an argument
3916:.if '\\$1'' .@error \\$0: cannot record empty index entry
3917:.if \\n[nl]<0 .@error \\$0: cannot record index entry when not on a page
3990:.if !d let@sg_\\*[let*type] .@error SG: letter type '\\*[let*type]' \
4098:.if !d let@head_\\*[let*type] .@error \\$0: unknown letter type '\\$1'
4333:.if !d Let\\$1 .@error \\$0: unrecognized option '\\$1'

If you spot any missing diagnostics, especially ones that checkmm(1)
users might have grown fond of, please report a bug.

> Now if it really can, putting “alias checkmm groff -mm -b -ww” in our
> .profile would give us an easy replacement.

I'd add "-z" because you want only the errors, not formatted output.

> Is anyone using -mm a lot and willing to test this?

I maintain it.  Does that count?  ;-)

> [1] How the **** did 20 years go by that fast?

The size of size of a year's worth of experience or memories is that a
person perceives is, other things equal, a logarithmic function of their
age.  When your're 10 years old, 5 years was half your lifetime ago:
"forever".  When your're 50, 5 years was only 10% of your lifetime
ago--a tangible, but only relatively a short while back.

When I was a kid I always wondered why adults occasionally seen (usually
relatives) were always so stunned by how fast I was growing up.  Even
accounting for the experiential difference of waking up to the same
gradually changing person in the mirror every morning versus dropping in
on relatives once a year for holidays or similar, eventually I figured
out (or read about, and forgot my source) the foregoing mathematical
explanation.

Granted, Sometimes all things aren't equal.  Joys and traumas make the
curve noisy.

May we have much more joy than trauma in our own lives.

Regards,
Branden
signature.asc (application/pgp-signature, 833 B)
-----BEGIN PGP SIGNATURE-----

iQIzBAABCAAdFiEEh3PWHWjjDgcrENwa0Z6cfXEmbc4FAmqJGNMACgkQ0Z6cfXEm
bc64QxAAq4lnnl5j/T6zwj5gLIyoD72Qh+YYtM59DAsFxApxDDEPScZGWSg8sTkq
bIrqq1SKeJX5cEf95700DczkTuve3KG/4xCAQx7sjXaVoyRO0fFqLAohSk178usg
e1W8DpyaWFVx/zHJ4TXHKfuPs+t3Zhg1cRup+rptUM0fKq1CMSyJrRVAxNczCDX6
DGiHKznOyXLQQSHhxdM4H0E43yqh5h1FOLKVI3gUfH3SWmIwuSA/jd6WOydm7uRs
nVEBZIA0XggWjx9HPqRtbuJUy7dWn/NMvm1CUZxnvGrwVO7Bka2TUoOGOgFvm103
SAp8FvuUpjyAQ+bXrbPOrpHOFbRBnSO+WEMmNhRnPk9S3CGZZY2LF1N94qmR1mSm
xujJUcxxtrosK2+7Lo06KOexta3ggg86WnQNC2GN3wSQh5hetd3iFqARRnvqYIeS
DAuw2lqg372wNUzjZRpNVOpVzE3EjvyIPXaAC2Gb/Z6sfkLA6wQ/rXHISgakqoXU
9kN7hXoB3V7jSiW+s61ifPVq9DUWdfDxi6iCiXMoCui8Eq5L/ece18mBGAEFO0WH
3MXT75euW55PQTzzUjlP8NcXzz17daV1sCs/EjATegpcpgpuXuN2oo/5i+dI6q6l
nw1Ulsa0xPVKQkUOxky+5OiE2WhSeqYfjLzGQeR9O0cg18kOBGQ=
=D07G
-----END PGP SIGNATURE-----
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.