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
Description: PGP signature
