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

Attachment: signature.asc
Description: PGP signature

Reply via email to