.\"@ mdocmx.tmac - mdocmx(7) macros. .\"@ mdocmx(7) extends the mdoc(7) semantic markup language by references, .\"@ allowing mdoc(7) to create anchors and table of contents. .\"@ Note this file will create warnings for using .\" inside .eo .. .ec. .\" .\" Written 2014 - 2015 by Steffen (Daode) Nurpmeso . .\" Public Domain .eo . .\"@ For mdoc(7) copy & paste -- comments can be found below: .ig eo .\" NS mdocmx(7) extension .\" NS Only loaded upon request, yet we need stub dummies otherwise: .\" NS - .Mx is the user interface .\" NS - .mx-mac-(enter|add-arg|sequence|leave) are the call-in .\" NS hooks for mdoc(7) macros (as long as mdoc(7) itself isn't .\" NS rewritten to use a yet non-existing .\" NS .doc-output-node TYPE [DATA]" .\" NS to produce output etc. instead of directly doing so. .\" NS (Of course this only if native macros use _those_ directly..) .\" NS Notes: .\" NS - mdocmx(7) tests \n[doc-in-synopsis-section] (less doc-). .\" NS - It tests \n[doc-arg-limit] and uses doc-macro-name and .\" NS doc-reset-args if not 0 (names less doc-, of course). .de Mx . ie !'\V[MDOCMX_ENABLE]'' \{\ . mso mdocmx.tmac . Mx \$@ . \} . el .als Mx mx-mac-enter .. .\" NS Note these may not .als each other or over-defining will fail! .de mx-mac-enter .. .de mx-mac-add-arg .. .de mx-mac-sequence .. .de mx-mac-leave .. .eo . .\"@ User strings: .\" mx-debug .\" mx-disable .\" mx-toc-emerged .\" mx-toc-force - special value: "tree" .if !d mx-toc-name .ds mx-toc-name TABLE OF CONTENTS .\" mx-toc-numbered . .\" -- >8 -- 8< -- . .\" ds mx:enabled .\" ds mx:preprocessed .\" ds mx:gogogo .\" ds mx:istty .\" ds mx:cleans - storage of .mx:cleanup-string .\" ds mx:cleanstop - reference actively prevented via \&\& prefix .\" ds mx:refok - some output is suppressed in SYNOPSIS / xy .nr mx:refno 1 .nr mx:stack-no 0 . .\" NS Mx user macro {{{ .de Mx . if d mx:enabled .if !d mx:gogogo .return . . \" Mx is a mdoc(7) extension and may not be used by noone else . \" mdoc(7) doc-arg-limit, doc-macro-name and doc-reset-args are stripped . if \n[arg-limit] \{\ . if !'Mx'\*[macro-name]' \{\ . mx:perr ".Mx not callable by other macros (like \*[macro-name])" . reset-args . rm mx:gogogo . return . \}\} . . \" I. "-enable [-preprocessed] [devices]" extension in document prologue . if '-enable'\$1' \{\ . shift . if d mx:enabled \{\ . mx:perr ".Mx -enable may be used once only!" . rm mx:gogogo . return . \} . ds mx:enabled . . if (\n[.$] > 0) \{\ . if '-preprocessed'\$1' \{\ . shift . ds mx:preprocessed . \}\} . \" For now, only . if !d mx:preprocessed \{\ . mx:perr "document not preprocessed by mdocmx(1)" . return . \} . . \" If the string "mx-disable" is defined, suppress mdocmx(7) . if d mx-disable .return . \" It's not nice, but for security reasons all parts of mdocmx(7) . \" (mdoc(7), grotty(1), less(1)) won't enable mdocmx(7) unless the . \" environment variable MDOCMX_ENABLE is set with a value. We, too . if '\V[MDOCMX_ENABLE]'' .return . ds mx:gogogo . . ie 'utf8'\*[.T]' .ds mx:istty . el .ie 'latin1'\*[.T]' .ds mx:istty . el .ie 'ascii'\*[.T]' .ds mx:istty . \" TODO For HTML and PDF devices include their respective support . \" TODO packages and inject anchors and references as appropriate . \" TODO Requires mdoc(7) output reword via a .doc-out-put TYPE [DATA] . \" TODO that is doc-out-flush'ed at the end, so that a completely . \" TODO prepared recursion tree can be inspected as a whole. . el .ie 'pdf'\*[.T]' \{\ . mso pdfmark.tmac . als mx-dump-xr mx-dump-xr-pdf . als mx-dump-anchor mx-dump-anchor-pdf . als mx-dump-ref mx-dump-ref-pdf . \} . el .ie 'html'\*[.T]' \{\ . \" grohtml is real shit.. . mso html.tmac . als mx-dump-xr mx-dump-xr-html . als mx-dump-anchor mx-dump-anchor-html . als mx-dump-ref mx-dump-ref-html . \} . el .rm mx:gogogo . . ie d mx:istty \{\ . ds mx#s1 tty . als mx-dump-xr mx-dump-xr-tty . als mx-dump-anchor mx-dump-anchor-tty . als mx-dump-ref mx-dump-ref-tty . \} . el \ . ds mx#s1 \*[.T] . rm mx#s2 . while (\n[.$] > 0) \{\ . ds mx#s2 . if '-preprocessed'\$1' \{\ . mx:perr "synopsis: .Mx -enable [devices]" . break . \} . if '\$1'\*[mx#s1]' \{\ . rm mx#s2 . break . \} . shift . \} . if d mx#s2 .rm mx:gogogo . rm mx#s2 . rm mx#s1 . return . \} . . if !d mx:enabled \{\ . mx:perr "not enabled via -enable in the document prologue!" . rm mx:gogogo . return . \} . . \" II. "-disable" to (henceforth) actively suppress mdocmx(7) . if '-disable'\$1' \{\ . rm mx:gogogo . return . \} . . \" III. "-toc [devices] [-tree] [devices]" for creating a table of contents . if '-toc'\$1' \{\ . ie d mx:istty .ds mx#s1 tty . el .ds mx#s1 \*[.T] . rm mx#s2 . rm mx#s3 . while (\n[.$] > 1) \{\ . shift . if '-tree'\$1' \{\ . ds mx#s2 -tree . continue . \} . if '\$1'\*[mx#s1]' \{\ . rm mx#s3 . break . \} . ds mx#s3 . \} . \" If we wouldn't produce a TOC check mx-toc-force . if d mx#s3 .if d mx-toc-force \{\ . rm mx#s3 . ie '\*[mx-toc-force]'tree' .ds mx#s2 -tree . el .rm mx#s2 . \} . if !d mx#s3 .mx:toc-dump \*[mx#s2] . rm mx#s3 . rm mx#s2 . rm mx#s1 . return . \} . . \" IV. "-anchor-spass MAC KEY [REL]" single-pass info gen. by mdocmx(1) . if '-anchor-spass'\$1' \{\ . mx:check-macname-mapped "\$2" . if !d mx#s1 \{\ . mx:perr ".Mx -anchor-spass: invalid argument: \$2" . rm mx:gogogo . return . \} . if d mx#s2 \{\ . if !(\n[.$] == 4) \{\ . mx:perr ".Mx -anchor-spass: missing .Sh / .Ss relation" . rm mx:gogogo . return . \} . \} . nr mx:\*[mx#s1]-no +1 . ds mx#s3 mx:\*[mx#s1]-\n[mx:\*[mx#s1]-no] . \" Even though mdocmx(1) has cleaned that up, it cannot expand . \" \*[STRING]s, so we have to cleanup the expansion . mx:cleanup-string "\$3" . \" xxx don't check mx:cleanstop, it's an anchor, not a reference . ds \*[mx#s3]-arg \*[mx:cleans] . if d mx#s2 \ . ds \*[mx#s3]-rel \$4 . ds \*[mx#s3]-ref \n[mx:refno] . nr mx:refno +1 . \" Anchor is placed when we actually see it . \"rm \*[mx#s3]-anchor . \" We have a need for fast lookup but lack a hashtable/xy . \" So "subdivide" the list for mx#s1 . mx:subdivide mx#s3 "\*[mx:cleans]" . ds mx#s4 mx:\*[mx#s1]-\*[mx#s3] . nr \*[mx#s4]-no +1 . ds \*[mx#s4]-\n[\*[mx#s4]-no]-rel \n[mx:\*[mx#s1]-no] . rm mx#s4 . rm mx#s3 . rm mx#s2 . rm mx#s1 . return . \} . . \" V. .Mx stack handling . \"if \n[in-synopsis-section] \{\ . \" mx:perr "in SYNOPSIS .Mx only supports -enable or -toc" . \" return . \"\} . . \" V.1. No argument: any supported macro, any content . if (\n[.$] == 0) \{\ . nr mx:stack-no +1 . return . \} . . \" V.2. MACRO: exactly MACRO, any content . nr mx:stack-no +1 . mx:check-macname "\$1" . if d mx#s2 .rm mx#s1 . rm mx#s2 . if !d mx#s1 \{\ . mx:perr ".Mx: cannot enqueue an anchor for macro \$1" . rm mx:gogogo . return . \} . ds mx:stack-mac-\n[mx:stack-no] \*[mx#s1] . rm mx#s1 . \" FALLTHRU . . \" V.3. MACRO/KEY: exactly MACRO with exactly content KEY. . if (\n[.$] > 1) \{\ . while (\n[.$] > 1) \{\ . shift . mx:cleanup-string "\$1" . \" xxx don't check mx:cleanstop, it's an anchor, not a reference . ie d mx#s1 .ds mx#s1 \*[mx#s1] \*[mx:cleans] . el .ds mx#s1 \*[mx:cleans] . \} . ds mx:stack-arg-\n[mx:stack-no] \*[mx#s1] . rm mx#s1 . \} .. .\" }}} .Mx . .\" mdocmx(7) hook call-ins for mdoc(7) macros {{{ .\" In theory a macro would call mx-mac-enter, giving its name, .\" then repeatedly call mx-mac-add-arg for each argument and .\" finalize that iteration via mx-mac-leave. .\" Since most mdoc(7) macros go through doc-print-recursive, that .\" one would drive the latter two in most cases. .\" .\" But it is complicated to get (1) at all macro arguments and .\" (2) arguments in text form: there is no "textof" or "stringof" .\" in GNU troff as of the time of this writing. .\" And without "textof" or "stringof" it is not gracefully possible .\" to get at the arguments since many macros inject format .\" information into them, leave alone user formatting stuff. .\" The latter can't be helped, but the former usually can be .\" prevented by collecting the argument *before* the formatting .\" injection occurs: to support this, mx-mac-enter may be .\" given those arguments, too, in which case any mx-mac-add-arg .\" before the next mx-mac-leave is effectively turned into a noop. .\" .\" Of course the above is a naive view on the recursive mdoc(7) .\" approach, with some commands which only change formatting styles .\" or similar (.Dq, .Do/.Dc..), and with others which switch some .\" global status to bypass the recursion (.Bk/.Ek, .Fo/.Fc..). .\" .\" The real targeted approach that should be graceful: .\" Prepare output via a new "node-put TYPE [DATA]" macro just like it .\" is currently done for input so that, once the uppermost recursion .\" level is about to be left we can traverse the entire recursion as .\" a flat tree. . .\" Recursion depth .nr mx:ard 0 .\" Define "a hashmap" of all macros which require that upon -leave .\" time the arguments are splitted up again, e.g. <^.Fn "int funcname"$> .ds mx:mac-needs-argsplit-Fn .\" A "hashmap" of all macros for which no error should be reported if .\" no KEY is seen: e.g., .Fl can be used without arguments, which causes .\" "-" to be printed, but it won't be parsed by us. (Obviously neither .\" anchors nor references to these default values can be generated.) .ds mx:mac-empty-key-ok-Fl . .de mx-mac-enter . if !d mx:gogogo .return . . \" Deal with automatic macro reopens after punctuation characters. . \" As in ".Va arg1 , arg2": both are .Va here. But it may also be . \" (silly example) ".Va arg1 , Fl flag1", in which case the .Fl has . \" to terminate the former .Va, which is what we do here. See below. . if d mx:mac-leave-last \{\ . mx:mac-cleanup-level . nr mx:ard -1 . \} . ie (\n[mx:ard] <= 0) .nr mx:ard 1 . el .nr mx:ard +1 . mx:mac-cleanup-level . . mx:check-macname-extended-mapped "\$1" . rm mx#s2 . \" Unless our business, bypass this level . if !d mx#s1 \{\ . ds mx:arz\n[mx:ard] . ds mx:arx\n[mx:ard] . return . \} . ds mx:arn\n[mx:ard] \*[mx#s1] . \" Some macros don't have their own index but are warped to another . \" macro's index - our .Mx stack however uses the real name! . if !'\$1'\*[mx#s1]' .ds mx:arw\n[mx:ard] \$1 . rm mx#s1 . . \" Got clean arguments to be used instead of further -add-arg calls? . shift . if (\n[.$] == 0) .return . while (\n[.$] > 0) \{\ . mx-mac-add-arg "\$1" . shift . \} . \" Turn further mx-mac-add-arg into noops! . ds mx:arx\n[mx:ard] .. . .de mx-mac-add-arg . if !d mx:gogogo .return . \" Be aware of cases where some recursive print sidesteps into us . if (\n[mx:ard] <= 0) .return . . rm mx:mac-leave-last . . \" Level ignored? . if d mx:arx\n[mx:ard] .return . . mx:cleanup-string "\$*" . ds mx#s1 mx:ara\n[mx:ard] . ie d \*[mx#s1] \ . ds \*[mx#s1] \*[\*[mx#s1]] \*[mx:cleans] . \" Request to explicitly ignore this? And turn further args into noops . el .ie d mx:cleanstop \{\ . ds mx:arz\n[mx:ard] . ds mx:arx\n[mx:ard] . \} . el \ . ds \*[mx#s1] \*[mx:cleans] . rm mx#s1 .. . .\" Deal with doc-print-recursive sequencing: .\" from our point of view punctuation characters finalize the current .\" macro and immediately reopen the very same macro. .\" GNU mdoc(7) seems to do it like that for e.g. .Va, but not for .Fn. .\" But this is beyond our level of understanding, if a new macro is .\" invoked it'll start a new mx-mac-enter cycle, which will truly .\" finalize the current macro. .de mx-mac-sequence . if !d mx:gogogo .return . \" Be aware of cases where some recursive print sidesteps into us . if (\n[mx:ard] <= 0) .return . . ds mx:mac-noclose . mx-mac-leave . rm mx:mac-noclose .. . .de mx-mac-leave . if !d mx:gogogo .return . \" Be aware of cases where some recursive print sidesteps into us . if (\n[mx:ard] <= 0) .return . . \" After mx-mac-sequence we may get called again immediately . if d mx:mac-leave-last \{\ . if !d mx:mac-noclose \{\ . mx:mac-cleanup-level . nr mx:ard -1 . \} . return . \} . ds mx:mac-leave-last . . \" Level ignored? . if d mx:arz\n[mx:ard] \{\ . mx:mac-cleanup-level . nr mx:ard -1 . return . \} . if d mx:arx\n[mx:ard] \{\ . \" TODO It may however also be one of the -mac-enter argument hacks. . \" TODO In this case simply return. . if !d mx:arn\n[mx:ard] \{\ . if !d mx:mac-noclose \{\ . mx:mac-cleanup-level . nr mx:ard -1 . \} . return . \} . \} . . \" .Xr and .Sx are special: they can't create anchors, only references . ds mx#s1 mx:ara\n[mx:ard] . if !d \*[mx#s1] \ . ds \*[mx#s1] . ds mx#s2 mx:arn\n[mx:ard] . ie 'Xr'\*[\*[mx#s2]]' \{\ . \" Argument resplit necessary . mx:mac-leave-xr \*[\*[mx#s1]] . \} . el .ie 'Sx'\*[\*[mx#s2]]' \{\ . ds mx:mac-leave-sx . mx:mac-key-find Sh "\*[\*[mx#s1]]" . if d mx:mac-leave-sx \ . mx:mac-key-find Ss "\*[mx:ara\n[mx:ard]]" . rm mx:mac-leave-sx . \} . el .ie d mx:mac-needs-argsplit-\*[\*[mx#s2]] \ . mx:mac-key-find-argsplit \*[\*[mx#s2]] \*[\*[mx#s1]] . el \ . mx:mac-key-find \*[\*[mx#s2]] "\*[\*[mx#s1]]" . rm mx#s2 . rm mx#s1 . . rm mx:ara\n[mx:ard] . if !d mx:mac-noclose \{\ . rm mx:arn\n[mx:ard] . nr mx:ard -1 . \} .. .\" }}} Hook call-ins for mdoc(7) macros . .\" Hidden stuff {{{ . .de mx:perr . tm1 mdocmx(7) error: \$* (#\n[.c]) .. . .\" Is it a macro that mdocmx(7) shall take care of? .de mx:check-macname-extended-mapped . mx:check-macname "\$1" . ie !d mx#s1 \{\ . ie 'Sx'\$1' .ds mx#s1 Sx . el .if 'Xr'\$1' .ds mx#s1 Xr . \} . el .if '\*[mx#s1]'Fo' .ds mx#s1 Fn .. .de mx:check-macname-mapped . mx:check-macname "\$1" . if '\*[mx#s1]'Fo' .ds mx#s1 Fn .. .de mx:check-macname . rm mx#s1 . rm mx#s2 . ie 'Ar'\$1' .ds mx#s1 Ar . el .ie 'Cm'\$1' .ds mx#s1 Cm . el .ie 'Dv'\$1' .ds mx#s1 Dv . el .ie 'Er'\$1' .ds mx#s1 Er . el .ie 'Ev'\$1' .ds mx#s1 Ev . el .ie 'Fl'\$1' .ds mx#s1 Fl . el .ie 'Fn'\$1' .ds mx#s1 Fn . el .ie 'Fo'\$1' .ds mx#s1 Fo . el .ie 'Ic'\$1' .ds mx#s1 Ic . el .ie 'In'\$1' .ds mx#s1 In . el .ie 'Pa'\$1' .ds mx#s1 Pa . el .ie 'Va'\$1' .ds mx#s1 Va . el .if 'Vt'\$1' .ds mx#s1 Vt . if d mx#s1 .return . . \" ..with [REL] . ie 'Sh'\$1' .ds mx#s1 Sh . el .ie 'Ss'\$1' .ds mx#s1 Ss . el .return . ds mx#s2 \*[mx#s1] .. . .\" ..if so, shall it NOT generate output in SYNOPSIS? .de mx:check-refok . ds mx:refok . ie 'Fl'\$1' . . el .return . \" doc-in-synopsis, but doc- prefix is stripped away.. . if !\n[in-synopsis-section] .return . rm mx:refok .. . .\" We shall generate a TOC; shallow unless given a (-tree) argument .de mx:toc-dump . Sh "\*[mx-toc-name]" . ie d mx-toc-emerged \ . Bl -inset . el \ . Bl -inset -compact . . nr mx#t-d#n1 0 . while (\n[mx#t-d#n1] < \n[mx:Sh-no]) \{\ . nr mx#t-d#n1 +1 . ds mx#t-d#s \*[mx:Sh-\n[mx#t-d#n1]-arg] . ie d mx-toc-numbered \ . It \n[mx#t-d#n1]. Sx "\*[mx#t-d#s]" . el \ . It Sx "\*[mx#t-d#s]" . . if (\n[.$] > 0) \{\ . nr mx#t-d#n2 \*[mx:Sh-\n[mx#t-d#n1]-rel] . nr mx#t-d#n3 0 . rm mx#t-d#s . while (\n[mx#t-d#n3] < \n[mx:Ss-no]) \{\ . nr mx#t-d#n3 +1 . if (\*[mx:Ss-\n[mx#t-d#n3]-rel] < \n[mx#t-d#n2]) .continue . if (\*[mx:Ss-\n[mx#t-d#n3]-rel] > \n[mx#t-d#n2]) .break . if !d mx#t-d#s \{\ . ds mx#t-d#s . Bl -tag -offset indent -compact . \} . It Sx "\*[mx:Ss-\n[mx#t-d#n3]-arg]" . \} . if d mx#t-d#s .El . rr mx#t-d#n3 . rr mx#t-d#n2 . \} . \} . rm mx#t-d#s . rr mx#t-d#n1 . El .. . .\" "Clean up", i.e., vaporise a string down to what we'll use as .\" identifiers for anchors and references. .\" Note: must be in sync with mdocmx(1)! .de mx:cleanup-string . ds mx:cleans \$1 . rm mx:cleanstop . . \" Because of the quirkiness of .substring, prevent endless loops (e.g. \!) . \" Also don't mind mysterious .substring args, _that_ works for GNU troff! . nr mx#c-s#n 0 . while (1) \{\ . ds mx#c-s#s \*[mx:cleans] . substring mx#c-s#s -1 . ie '\&'\*[mx#c-s#s]' . . el .ie '\%'\*[mx#c-s#s]' . . el .ie '\/'\*[mx#c-s#s]' . . el .ie '\c'\*[mx#c-s#s]' . . el .ie ' '\*[mx#c-s#s]' . . el .ie ' '\*[mx#c-s#s]' . . el .break . nr mx#c-s#n +1 . if (\n[mx#c-s#n] > 10) .break . substring mx:cleans 0 -2 . \} . . nr mx#c-s#n 0 . while (1) \{\ . ds mx#c-s#s \*[mx:cleans] . substring mx#c-s#s 0 0 . ie '\&'\*[mx#c-s#s]' \{\ . \" \&\& prefix actively prevents reference lookup . \" Please see mx:subdivide for why we test two characters . \" individually instead of simply testing \&\& . ds mx#c-s#s \*[mx:cleans] . substring mx#c-s#s 1 1 . if '\&'\*[mx#c-s#s]' \{\ . ds mx:cleanstop . break . \} . \} . el .ie '\%'\*[mx#c-s#s]' . . el .ie ' '\*[mx#c-s#s]' . . el .ie ' '\*[mx#c-s#s]' . . el .break . nr mx#c-s#n +1 . if (\n[mx#c-s#n] > 8) .break . substring mx:cleans 1 . \} . rm mx#c-s#s . rr mx#c-s#n .. . .\" Create a "hash" of the argument string \$2 and store it in number .\" register \$1 (in order to subdivide the per-command lists). .\" \$2 must have been undergone mx:cleanup-string massage. .de mx:subdivide . \" Use the .hash request to get at that if available! . ie d hash \{\ . hash mx#sd#n \$2 . ds \$1 \n[mx#sd#n] . \} . el \{\ . \" Otherwise we need to find a different way. Use the length, . length mx#sd#n \$2 . \" and furtherly subdivide (necessary, think mdoc(7) manual..) . \" by using the first two letters (not one, ditto). . \" Circumvent the ".substring returns anything" problem via \A'', . \" be aware that groff(1) expands x in \A'x' during parse, so that, . \" e.g, \[, from ".substring 0 1" results in parse errors!! . ds mx#sd#s1 \$2 . ds mx#sd#s2 \$2 . substring mx#sd#s1 0 0 . substring mx#sd#s2 1 1 . ie \A'\*[mx#sd#s1]=\*[mx#sd#s2]' \ . ds \$1 "\*[mx#sd#s1]\n[mx#sd#n]\*[mx#sd#s2] . el \ . ds \$1 "zzz\n[mx#sd#n] . rm mx#sd#s . \} . rr mx#sd#n .. . .de mx:mac-cleanup-level . rm mx:mac-leave-last . rm mx:ara\n[mx:ard] . rm mx:arz\n[mx:ard] . rm mx:arx\n[mx:ard] . rm mx:arw\n[mx:ard] . rm mx:arn\n[mx:ard] .. . .\" .Xr creates external references which requires special handling. .\" Because $PAGER will _scroll_ to the anchor, we need a running .\" anchor numbers .de mx:mac-leave-xr . ds mx#mlx . if ''\$1' \{\ . mx:perr ".Xr: missing manual reference, something is wrong" . rm mx#mlx . \} . if ''\$2' \{\ . \" Though pretty useless for a reference, the manual section . \" is indeed optional, so don't complain if it is missing! . \"mx:perr .Xr: missing manual section, something is wrong . rm mx#mlx . \} . if !d mx#mlx .return . rm mx#mlx . . nr mx#n1 \n[mx:refno] . nr mx:refno +1 . mx-dump-xr \n[mx#n1] "\$1" "\$2" . rr mx#n1 .. . .\" Try to find an occurrence of key-content $2 in macro stack $1 .de mx:mac-key-find-argsplit . mx:mac-key-find \$1 \$[\n[.$]] .. .de mx:mac-key-find . \" E.g.: <^.Fn "int fun" , "int arg1"$> . if ''\$2' \{\ . if !d mx:mac-empty-key-ok-\$1 \ . mx:perr "empty key for macro \$1: something is wrong" . return . \} . \" In SYNOPSIS we have to act quite differently . mx:check-refok \$1 . . \" If .Mx stack is not empty, check if we can pop it: define mx#anchor . ie 'Sx'\$1' . . el .ie 'Xr'\$1' . . el .ie 'Sh'\$1' .ds mx#showref . el .ie 'Ss'\$1' .ds mx#showref . el .if (\n[mx:stack-no] > 0) \{\ . ds mx#anchor . . \" On the stack there are real macro names, so compare against the . \" real name if we warped that to another one (index) instead . ie d mx:arw\n[mx:ard] \ . ds mx#s1 \*[mx:arw\n[mx:ard]] . el \ . ds mx#s1 \$1 . ie !d mx:stack-mac-\n[mx:stack-no] \ . \" . el .ie !'\*[mx#s1]'\*[mx:stack-mac-\n[mx:stack-no]]' \ . rm mx#anchor . el .ie !d mx:stack-arg-\n[mx:stack-no] \ . \" . el .if !'\$2'\*[mx:stack-arg-\n[mx:stack-no]]' \ . rm mx#anchor . rm mx#s1 . . if d mx#anchor \{\ . if !d mx:refok \ . mx:perr "cannot (Synopsis section?) .Mx the macro \$1 here" . rm mx:stack-arg-\n[mx:stack-no] . rm mx:stack-mac-\n[mx:stack-no] . nr mx:stack-no -1 . . if !d mx:preprocessed \{\ . nr mx:\$1-no +1 . ds mx:\$1-\n[mx:\$1-no]-arg \$2 . ds mx:\$1-\n[mx:\$1-no]-ref \n[mx:refno] . nr mx:refno +1 . mx-dump-anchor "\$1" "\*[mx:\$1-\n[mx:\$1-no]-ref]" . ds mx:\$1-\n[mx:\$1-no]-anchor . rm mx#anchor . \" We have a need for fast lookup but lack a hashtable/xy . \" So "subdivide" the list for \$1 . mx:subdivide mx#s1 "\$2" . nr mx:\$1-\*[mx#s1]-no +1 . ds mx:\$1-\*[mx#s1]-\n[mx:\$1-\*[mx#s1]-no]-rel \n[mx:\$1-no] . rm mx#s1 . return . \} . \} . \} . . \" Iterate over the subdivision list . nr mx#n1 \n[mx:\$1-no] . mx:subdivide mx#s1 "\$2" . if r mx:\$1-\*[mx#s1]-no \{\ . nr mx#n2 \n[mx:\$1-\*[mx#s1]-no] . . while (\n[mx#n2] > 0) \{\ . nr mx#n1 \*[mx:\$1-\*[mx#s1]-\n[mx#n2]-rel] . ds mx#s2 \*[mx:\$1-\n[mx#n1]-arg] . . if '\$2'\*[mx#s2]' \{\ . ds mx#s2 \*[mx:\$1-\n[mx#n1]-ref] . \" Special call hook (for mx-mac-leave, .Sx command)? . ie d mx:mac-leave-sx \ . mx-dump-ref "\*[mx#s2]" . \" Anchor already exists, print a reference . el .ie d mx:\$1-\n[mx#n1]-anchor \{\ . if d mx:refok \ . mx-dump-ref "\*[mx#s2]" . \} . \" Define anchor and print the reference (section headings) . el .ie d mx#showref \{\ . mx-dump-anchor "\$1" "\*[mx#s2]" . mx-dump-ref "\*[mx#s2]" . \} . \" Anchor doesn't yet exist, but not defined - print a reference . el .ie !d mx#anchor \{\ . if d mx:refok \ . mx-dump-ref "\*[mx#s2]" . \} . \" Only define anchor . el \{\ . ds mx:\$1-\n[mx#n1]-anchor . mx-dump-anchor "\$1" "\*[mx#s2]" . \} . rm mx:mac-leave-sx . break . \} . nr mx#n2 -1 . \} . \} . rm mx#s2 . rm mx#s1 . rr mx#n2 . rr mx#n1 . rm mx#anchor . rm mx#showref .. .\" }}} Hidden stuff . .\" Output formats {{{ . .\" TTY .de mx-dump-xr-tty \%\&\fR\X'tty: mdocmx xr \$1 \$2 \$3'[\$1]\fP\&\c .. .de mx-dump-anchor-tty . ie !d mx-debug \{\ \%\&\X'tty: mdocmx \$1 \$2'\&\c . \} . el \{\ \%\&\fR\X'tty: mdocmx \$1 \$2'@address@hidden&\c . \} .. .de mx-dump-ref-tty \%\&\fR[\$1]\fP\&\c .. .\" HTML .de mx-dump-xr-html . \" Cannot help it .. .de mx-dump-anchor-html . TAG "\$2" . if d mx-debug \ . HTML @\$2@ .. .de mx-dump-ref-html . URL "#\$1" "[\$1]" .. .\" PDF .de mx-dump-xr-pdf . \" Cannot help it .. .de mx-dump-anchor-pdf . ie d mx-debug \ . pdfhref M -N "\$2" -E "@\$2@" . el \ . pdfhref M -N "\$2" .. .de mx-dump-ref-pdf . pdfhref L -D "\$1" -P "\%" -A "\c" "[\$1]" .. .\" }}} Output formats . .ec .\" s-ts-mode