Hello Ingo and the list, i'm sorry for being so late, having first talked about "More referenceability for -mdoc would be an improvement!?" on 2014-09-11 [1]. So here i present a first working prototype for mdocmx(7), as above.
[1] http://thread.gmane.org/gmane.comp.printing.groff.general/12302 Without too much blabla: if you read a manual and see a reference that you want to follow you currently have to take several steps in order to do so with the usual Unix manual system; whereas our brains have created nice shortcuts after doing so many thousands of times, our software has not. This is why sapiens sapiens is so successful: mother nature buffers and corrects all our faults. Or at least something along these lines. Mdoc(7) is the best language for manual pages that we have, as it structures the text semantically. Well, not comparable to the SGML predecessor from the 60s (but the 60s and 70s were or must have been the better times anyway), but much better than the man(7) macros which only do some weak structuring. But even if you convert a correct and laborious mdoc(7) document to HTML or PDF you will not gain a table of contents, an index or even references, the latter not even inside the document itself. To overcome this status quo many projects manage their manuals in a different language, say, a SGML package, and convert to manual pages, HTML and PDF just as necessary. Even those projects suffer from the necessity to install the manual in multiple formats in order to improve the situation however, and of course the installed manual page is as static and unresponsive as it always has been. Exactly the latter is what mdocmx(7) tries to improve for the mdoc(7) semantic markup language. What is mdocmx(7)? 1. A single new multiplexer macro for mdoc(7): ".Mx". Note that ".Mx -enable"d documents immediately and without any further effort (except preprocessing) support anchors and references for mdoc(7) builtin commands like .Sh and .Ss on the one hand and .Sx and .Xr on the other. Documented in the attached mdocmx.7. Patch for groff(1)'s v1.22.3 ([2]) mdoc(7) attached as part of groff-1_22_3.diff. Status: . Documents instrumented with this extension seem to get swallowed by all tested mdoc(7) environments without causing any problems. Of course, if warnings are enabled or similar modes are chosen in troff(1) or mandoc(1) warnings will occur. But these are not deemed to be default modes of operation. . ".Mx -toc" is not yet expanded from within the macros, but requires a preprocessor (as below) run. . It works well for the shipped manuals and the authors MUA manual, but has yet a problem with the example shown in mdocmx(7), for example; i.e., there one reads '.Mx Ic "final anchor"' and that anchor is actually visible, whereas it shouldn't (it is not a reference). Thus: not mature yet. (I blame mdoc(7) for that. But anyway don't blame me!) . Dew-fresh, comments need revisit etc. Shouldn't be made public, but i'm in a hurry... . E.g., could include PDF and HTML support macros and automatically support more output devices than just grotty(1), and different to that without even touching any C(++) code. [2] http://ftp.gnu.org/gnu/groff/groff-1.22.3.tar.gz 2. A preprocessor, written in portable sh(1) and awk(1): mdocmx(1). It will walk the document and collect .Mx sequences in order to create the anchors. It will not perform any work, acting rather like cat(1), unless the document is a mdocmx(7) document that has a ".Mx -enable" in the document prologue. It will not perform any work and also rather act like cat(1) if the document was already preprocessed by mdocmx(1). The latter property of mdocmx(7) allows a developer to locally preprocess the manual and distribute the result. Note this preprocessor is only necessary for single-pass troff(1) implementations. Multi-pass troff(1) as well as specialized utilities like mandoc(1) don't require it, of course. (In fact mandoc(1) should only need the less(1) patch as below, and be standalone beside that.) Attached as mdocmx.sh. Documented in the attached mdocmx.1. Status: mature, though possibly a bit dumb. 3. A patch for the less(1) pager for understanding new, specially crafted ^H backslash sequences. Different to the (rather) well known ^H formatting sequences these are completely hidden (remain uninterpreted for display). Once in the pager, the new control sequence (for now) ^X-^R (control-X, then control-R) will display a prompt "[anchor]:" where the number of an anchor can be entered. Then the document is searched and upon success the anchor will be scrolled into view. Shall the anchor refer to a .Xr, i.e., an external reference, "Read external manual: !man 7 mdoc" (replace the 7 and the mdoc with the manual in question) is displayed and one may either accept via RETURN or drop via rather anything else (BS, ESC,..). In the former case the current pager instance is temporarily replaced in order to read the referenced manual; upon return from this, "!back from external manual (press RETURN)" is displayed. Patch for less(1) v458 (the last stable release [3]) attached as less-v458.diff. Status: mature, but documentation missing. . The fine pager lv(1) works properly (if $GROFF_NO_SGR is set, since it doesn't understand ISO 6429/ECMA-48). A patch for it is possibly added once mdocmx(7) is mature. . A tested p(1) pager is however completely broken. . The protocol used is as follows (commented in patch too): - Local anchor: '{DIGITS}', each char doubled and ^H'd away. - .Xr anchor: '{!SECTION;MANUAL}', each char doubled and ^H'd away. In fact an .Xr anchor is a normal local anchor, which is searched and being scrolled to, directly followed by the shown .Xr anchor. No whitespace in between. To be the least intrusive possible we simply use the normal searching and scrolling capabilities: because of the scrolling .Xr anchors are not shared for multiple section/name matches, but a running anchor number is used. [3] http://www.greenwoodsoftware.com/less/less-458.tar.gz 4. troff(1) output device extensions. For now grotty(1) is supported. Patch for groff(1)'s v1.22.3 ([4]) grotty(1) attached as part of groff-1_22_3.diff. Status: mature, but documentation missing. [4] http://ftp.gnu.org/gnu/groff/groff-1.22.3.tar.gz Fine. I hope you like it. I do. Except that the mdoc(7) macros are not yet really done and the manuals need to be updated. But for mandoc(1) all that doesn't matter anyway, it is off the hook and can support nice stuff in less(1) right away. :-(. :) Patches apply simply via "cd DIR && patch -E -p1 < PATCH". Have fun, --steffen
'\" m -- preprocess: mdocmx(1) .\"@ mdocmx.7 - mdoc .Mx anchor reference manual. .\" .\" Written 2014 by Steffen (Daode) Nurpmeso <sdao...@users.sf.net>. .\" Public Domain . .Dd November 11, 2014 .Dt MDOCMX 7 .Os .Mx -enable . .Sh NAME .Nm .Mx .Nd Reference extension for the mdoc semantic markup language . .Sh SYNOPSIS .Nm .Ar -enable .Pp .Nm .Nm .Ar macro .Nm .Ar macro Ar key .Pp .Nm .Ar -toc . .Mx -toc . .Sh DESCRIPTION .Nm introduces creation of referenceable manual index anchors. To enable this extension the standard .Xr mdoc 7 document prologue \(en .Ic \&Dd , .Ic \&Dt and .Ic \&Os \(en is augmented with the new command .Nm .Ar -enable . Because macros driven by single-pass troff implementations cannot create forward references .Xr mdoc 7 documents which use this extension need to be preprocessed with the .Xr mdocmx 1 preprocessor, which is a regular part of .Xr mdocmx 7 and implemented in portable .Xr sh 1 and .Xr awk 1 . Specialized manual formatters and macros driven by multi-pass troff interpreters may not require a preprocessor to support this extension. It is also possible to preprocess the manual once and distribute the resulting document \(en refer to the .Sx COMPATIBILITY section for more on that. . .Ss Creating referenceable anchors After the extension was .Ar -enable Ns d in the document prologue the second group of .Nm usage forms can be used to enqueue index anchor requests. These requests form a stack which will be consumed (popped) by the later occurrence of a (corresponding) .Xr mdoc 7 .Ar macro which supports referenceable index entries. The indices are managed with distinct namespaces for each supported .Ar macro , meaning that, e.g., `\&.Mx Ic sendmail' and `\&.Mx Va sendmail' will create distinct index anchors. .Pp Using the plain macro .Nm without arguments creates a stack entry for which both, the name of the .Ar macro as well as the .Ar key will be taken from the document content. .Nm .Ar macro will create a stack entry that will be consumed by the next occurrence of .Ar macro only, then taking the .Ar key off the document content, whereas .Nm .Ar macro Ar key creates a stack entry that also has its .Ar key defined, and which will be consumed once an exactly matching macro / key pair is seen in the document only. (The .Sx EXAMPLES section gives a use case for this form.) .Pp Using the .Xr mdocmx 1 preprocessor will also create referenceable anchors for the .Xr mdoc 7 section header macros .Ic .Sh and .Ic .Ss , so that a .Xr mdoc 7 macro package which supports the .Xr mdocmx 7 extension will be enabled to actually create references with the .Ic .Sx macro. The following macros gain support for referenceable anchors via .Nm : .Bl -tag -width ".It Ic .Dv" .Mx .It Ic .Ar Command argument. .Mx .It Ic .Cm Command modifier. .Mx .It Ic .Dv Defined variable or preprocessor constant. .Mx .It Ic .Er Error constant. .Mx .It Ic .Ev Environment variable. .Mx .It Ic .Fl Command line option (flag). .Mx .It Ic .Fn Function name. .Mx .It Ic .Fo Function name (in function block syntax). This is mapped to .Ic .Fn , .Ic .Fo has no index by itself. .Mx .It Ic .Ic Internal or interactive command. .Mx .It Ic .Pa File system path. .Mx .It Ic .Va Variable name. .El . .Ss Creating a table of contents The final group of usage forms of the .Xr mdocmx 7 reference extension allows the creation of a document table of content, which is of special interest when converting a .Xr mdoc 7 document into formats such as HTML, XHTML and PDF. If so desired the .Xr mdocmx 1 preprocessor will replace any occurrence of `.Mx -toc' in the document with a table of contents (see .Sx IMPLEMENTATION NOTES for an example), otherwise `.Mx -toc' lines will remain a vivid part of the document text and thus target of interpretation of the actual document formatter, which ought to ignore such commands (except when it directly supports the .Xr mdocmx 7 extension and the document target format is one of the mentioned, in which case it may create a regulary table of content in place). . .Sh IMPLEMENTATION NOTES The .Nm request cannot share a line with other macros, neither in the document prologue nor in its content. Be aware that the content of the .Fl width argument to .Xr mdoc 7 lists etc. is evaluated as if it were normal document content, e.g., in `.Bl -tag -width ".It Fn _atexit"' the `Fn _atexit' will be evaluated and may thus get used by .Nm . .Pp When developing a manual it may be helpful to increase verbosity of the preprocessor on its standard error I/O channel by using the .Fl v command line flag in order to get a notion on what is going on: .Bd -literal -offset indent $ mdocmx -vv -t Sh < mdocmx.7 2> stderr.txt | \e groff -Tascii -mdoc | less $ cat stderr.txt .Ed .Pp The .Xr mdocmx 1 preprocessor is implemented in standard .Xr sh 1 and .Xr awk 1 and relies on the latter utilities' input field splitting capabilities. This means that whitespace in between fields is lost on input and reinstantiated as a single space character on output. With verbosity enabled a warning is issued in cases where this could actually be a problem. .Pp The preprocessor also doesn't handle escaped whitespace well, e.g., "White\e Space" isn't well supported; arguments with embedded whitespace should instead be enclosed in quotation marks. . .Sh EXAMPLES A complete, but completely fanciful .Xr mdoc 7 document that uses the .Xr mdocmx 7 extension would for example be: .Bd -literal -offset indent \&.Dd November 8, 2014 \&.Dt MDOCMX-EXAMPLE 7 \&.Os \&.Mx -enable \&.Sh NAME \&.Nm mdocmx-example \&.Nd An example for the mdocmx mdoc reference extension \&.Mx -toc \&.Sh DESCRIPTION Sors salutis et virtutis michi nunc contraria. \&.Bl -tag -width ".It Fn _a_e_i_" \&.Mx \&.It Ic .Ar This will create an anchor for a macro `Ic', key `.Ar'. \&.Mx \&.It Ic .Cm Anchor for `Ic', key `.Cm'. \&.Mx \&.It Ic .Dv And an anchor for `Ic', key `.Dv'. \&.Mx Ic \&.Mx Ic "final anchor" \&.Mx Fn _atexit \&.It Fn exit No anchor here. \&.It Fn at_quick_exit , Fn _atexit Not for the first, but for the second `Fn' there will be an anchor with the key `_atexit'. \&.It Ic "no anchor here" \&.It Ic "final anchor" Pops the pushed `Ic' / `final anchor' macro / key pair. \&.It Ic ciao Pops the `Ic' and assigns the key `Ciao'. \&.El .Ed . .Sh COMPATIBILITY Using the .Xr mdocmx 7 extension in .Xr mdoc 7 manual pages should not cause any compatibility problems in sofar as all tested environments silently ignore the unknown commands by default. Because of this, and due to the nature of this extension, an interesting, backward as well as forward compatible approach to use .Xr mdocmx 7 may be to preprocess manuals on developer machines and instead distribute the resulting documents. . .Sh SEE ALSO .Xr awk 1 , .Xr mandoc 1 , .Xr mdocmx 1 , .Xr sh 1 , .Xr troff 1 , .Xr mdoc 7 . .Sh HISTORY The .Nm environment appeared in 2014. . .Sh AUTHORS Original idea and draft implementation by .An Steffen Po Daode Pc Nurpmeso Aq Mt sdao...@users.sf.net . Command design by .An Ingo Schwarze Aq Mt schwa...@openbsd.org . .\" s-ts-mode
diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp index 60d46e8..9bac7da 100644 --- a/src/devices/grotty/tty.cpp +++ b/src/devices/grotty/tty.cpp @@ -30,6 +30,10 @@ extern "C" const char *Version_string; #define putstring(s) fputs(s, stdout) +#ifndef PTR2SIZE +# define PTR2SIZE(X) ((size_t)(X)) +#endif + #ifndef SHRT_MIN #define SHRT_MIN (-32768) #endif @@ -216,6 +220,7 @@ class tty_printer : public printer { void line(int, int, int, int, color *, color *); void draw_line(int *, int, const environment *); void draw_polygon(int *, int, const environment *); + void _special_mdocmx(char const *, environment const *); public: tty_printer(); ~tty_printer(); @@ -434,42 +439,119 @@ void tty_printer::add_char(output_character c, int w, void tty_printer::special(char *arg, const environment *env, char type) { if (type == 'u') { - add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill, - CU_MODE); - return; + add_char(*arg - '0', 0, env->hpos, env->vpos, env->col, env->fill, CU_MODE); + goto jleave; } if (type != 'p') - return; - char *p; - for (p = arg; *p == ' ' || *p == '\n'; p++) + goto jleave; + + char *p, *tag_cmd; + + for (p = arg; *p == ' ' || *p == '\n'; ++p) ; - char *tag = p; - for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++) + tag_cmd = p; + for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; ++p) ; - if (*p == '\0' || strncmp(tag, "tty", p - tag) != 0) { + if (*p == '\0' || strncmp(tag_cmd, "tty", PTR2SIZE(p - tag_cmd)) != 0) { error("X command without `tty:' tag ignored"); - return; + goto jleave; } - p++; - for (; *p == ' ' || *p == '\n'; p++) + + for (++p; *p == ' ' || *p == '\n'; ++p) ; - char *command = p; - for (; *p != '\0' && *p != ' ' && *p != '\n'; p++) + tag_cmd = p; + for (; *p != '\0' && *p != ' ' && *p != '\n'; ++p) ; - if (*command == '\0') { + if (*tag_cmd == '\0') { error("empty X command ignored"); - return; + goto jleave; } - if (strncmp(command, "sgr", p - command) == 0) { - for (; *p == ' ' || *p == '\n'; p++) + + if (!strncmp(tag_cmd, "sgr", PTR2SIZE(p - tag_cmd))) { + for (; *p == ' ' || *p == '\n'; ++p) ; int n; - if (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0) - old_drawing_scheme = 1; - else - old_drawing_scheme = 0; + old_drawing_scheme = (*p != '\0' && sscanf(p, "%d", &n) == 1 && n == 0); update_options(); + } else if (!strncmp(tag_cmd, "mdocmx", PTR2SIZE(p - tag_cmd))) + _special_mdocmx(p, env); + +jleave: + ; +} + +void +tty_printer::_special_mdocmx(char const *ap, environment const *env) +{ + // Handle the special \X'' injections of the mdocmx(7) reference extension + // for the mdoc semantic markup language. + // See mdocmx(7) for the used protocol +#undef __C +#define __C(C) add_char(C, 0, env->hpos, env->vpos, env->col, env->fill, 0) + + char const *cmd, *cmd_top, *cp; + + for (; *ap == ' ' || *ap == '\n'; ++ap) + ; + cmd = ap; + for (; *ap != '\0' && *ap != ' ' && *ap != '\n'; ++ap) + ; + cmd_top = ap; + if (cmd == cmd_top) { + error("empty mdocmx X command ignored"); + goto jleave; } + + // Any mdocmx(7) command places an anchor + for (; *ap == ' ' || *ap == '\n'; ++ap) + ; + cp = ap; + for (; *ap != '\0' && *ap != ' ' && *ap != '\n'; ++ap) + ; + if (cp == ap) { + error("empty mdocmx X anchor ignored"); + goto jleave; + } + + __C('{'); __C('\b'); __C('{'); __C('\b'); + for (; cp < ap; ++cp) { + __C(*cp); __C('\b'); __C(*cp); __C('\b'); + } + __C('}'); __C('\b'); __C('}'); __C('\b'); + + // The external reference .Xr injects some more + if (!strncmp(cmd, "xr", PTR2SIZE(cmd_top - cmd))) { + __C('{'); __C('\b'); __C('{'); __C('\b'); + __C('!'); __C('\b'); __C('!'); __C('\b'); + + // Manual section + for (; *ap == ' ' || *ap == '\n'; ++ap) + ; + cp = ap; + for (; *ap != '\0' && *ap != ' ' && *ap != '\n'; ++ap) + ; + for (; cp < ap; ++cp) { + __C(*cp); __C('\b'); __C(*cp); __C('\b'); + } + + __C(';'); __C('\b'); __C(';'); __C('\b'); + + // Manual page + for (; *ap == ' ' || *ap == '\n'; ++ap) + ; + cp = ap; + for (; *ap != '\0' && *ap != ' ' && *ap != '\n'; ++ap) + ; + for (; cp < ap; ++cp) { + __C(*cp); __C('\b'); __C(*cp); __C('\b'); + } + + __C('}'); __C('\b'); __C('}'); __C('\b'); + } + +jleave: + ; +#undef __C } void tty_printer::change_color(const environment * const env) diff --git a/tmac/doc-common b/tmac/doc-common index e63fdb4..934ef3c 100644 --- a/tmac/doc-common +++ b/tmac/doc-common @@ -814,6 +814,397 @@ .. . . +.\" NS Mx user macro +.\" NS mdocmx(7) reference extension +.\" NS +.\" NS modifies: +.\" NS doc-mx-enabled - have seen .Mx -enable +.\" NS doc-mx-preprocessed - mdocmx(1) preprocessed the document +.\" NS doc-mx-error - error mode - do not act for real any more +.\" NS doc-mx-gogogo - we support the output device: act! +.\" NS doc-mx-tty - is a TTY output devie +.\" NS +.\" NS doc-mx-refno - the running count of generated references +.\" NS +.\" NS doc-mx-CMD-no - count of anchors for CMD +.\" NS +.\" NS local variables: +.\" NS doc-mx-XXX-XXX +. +.\"ds doc-mx-enabled +.\"ds doc-mx-preprocessed +.\"ds doc-mx-error +.\"ds doc-mx-gogogo +.\"ds doc-mx-tty +. +.nr doc-mx-refno 1 +. +.nr doc-mx-stack-no 0 +. +.de Mx +. if d doc-mx-error .return +. +. \" Mx is an extension and may not be used by noone else +. if \n[doc-arg-limit] \{\ +. if !'Mx'\*[doc-macro-name]' \{\ +. tm1 mdocmx(7): .Mx not callable by other macros (#\n[.c]) +. doc-reset-args +. ds doc-mx-error +. return +. \}\} +. +. if '-enable'\$1' \{\ +. ds doc-mx-error +. ie d doc-mx-enabled \ +. tm1 mdocmx(7): .Mx -enable may be used once only! (#\n[.c]) +. el .ie (\n[.$] == 1) \{\ +. \" For single-pass troff mdocmx(7) requires a preprocessor. +. \" For multi-pass troff we could now switch some mode.. ? +. tm1 mdocmx(7): document not preprocessed by mdocmx(1) (#\n[.c])! +. \"ie (1) .tm1 mdocmx(7): missing preprocessing by mdocmx(1) (#\n[.c])! +. \"el .rm doc-mx-error +. \} +. el .ie (\n[.$] > 2) .tm1 mdocmx(7): synposis: .Mx -enable (#\n[.c]) +. el .ie '-preprocessed'\$2' \{\ +. ds doc-mx-preprocessed +. rm doc-mx-error +. \} +. el .tm1 mdocmx(7): synposis: .Mx -enable (#\n[.c]) +. if d doc-mx-error .return +. +. ds doc-mx-enabled +. ds doc-mx-gogogo +. ie 'utf8'\*[.T]' .ds doc-mx-tty +. el .ie 'latin1'\*[.T]' .ds doc-mx-tty +. el .ie 'ascii'\*[.T]' .ds doc-mx-tty +. el .rm doc-mx-gogogo +. \" TODO For HTML and PDF devices include their respective support +. \" TODO packages and inject anchors and references as appropriate +. return +. \} +. +. if !d doc-mx-enabled \{\ +. tm1 mdocmx(7) not enabled via -enable in the document prologue! (#\n[.c]) +. ds doc-mx-error +. return +. \} +. if !d doc-mx-gogogo .return +. +. \" TODO -toc is expanded by preprocessor (but we *can* with preproc info!!) +. if '-toc'\$1' .return +. +. \" II. Single pass troff info generated by mdocmx(1) +. if '-anchor-spass'\$1' \{\ +. ie 'Ar'\$2' .ds doc-mx#s1 Ar +. el .ie 'Cm'\$2' .ds doc-mx#s1 Cm +. el .ie 'Dv'\$2' .ds doc-mx#s1 Dv +. el .ie 'Er'\$2' .ds doc-mx#s1 Er +. el .ie 'Ev'\$2' .ds doc-mx#s1 Ev +. el .ie 'Fl'\$2' .ds doc-mx#s1 Fl +. el .ie 'Fn'\$2' .ds doc-mx#s1 Fn +. el .ie 'Ic'\$2' .ds doc-mx#s1 Ic +. el .ie 'Pa'\$2' .ds doc-mx#s1 Pa +. el .ie 'Va'\$2' .ds doc-mx#s1 Va +. +. el .ie 'Sh'\$2' .ds doc-mx#s1 Sh +. el .ie 'Ss'\$2' .ds doc-mx#s1 Ss +. el \{\ +. tm1 mdocmx(7): .Mx -anchor-spass: invalid argument: \$2 (#\n[.c]) +. ds doc-mx-error +. return +. \} +. +. nr doc-mx-\*[doc-mx#s1]-no +1 +. ds doc-mx-\*[doc-mx#s1]-\n[doc-mx-\*[doc-mx#s1]-no]-arg \$3 +. ds doc-mx-\*[doc-mx#s1]-\n[doc-mx-\*[doc-mx#s1]-no]-ref \n[doc-mx-refno] +. nr doc-mx-refno +1 +. \" Fake that we have the anchor +. nr doc-mx-\*[doc-mx#s1]-anchor 0 +. rm doc-mx#s1 +. return +. \} +. +. \" III. .Mx stack handling +. +. \" III.1. No argument: any supported macro, any content +. if (\n[.$] == 0) \{\ +. nr doc-mx-stack-no +1 +. return +. \} +. +. \" III.2. MACRO: exactly MACRO, any content +. nr doc-mx-stack-no +1 +. ie 'Ar'\$1' .ds doc-mx#s1 Ar +. el .ie 'Cm'\$1' .ds doc-mx#s1 Cm +. el .ie 'Dv'\$1' .ds doc-mx#s1 Dv +. el .ie 'Er'\$1' .ds doc-mx#s1 Er +. el .ie 'Ev'\$1' .ds doc-mx#s1 Ev +. el .ie 'Fl'\$1' .ds doc-mx#s1 Fl +. el .ie 'Fn'\$1' .ds doc-mx#s1 Fn +. el .ie 'Fo'\$1' .ds doc-mx#s1 Fn +. el .ie 'Ic'\$1' .ds doc-mx#s1 Ic +. el .ie 'Pa'\$1' .ds doc-mx#s1 Pa +. el .ie 'Va'\$1' .ds doc-mx#s1 Va +. el \{\ +. tm1 mdocmx(7): .Mx: cannot enqueue an anchor for macro "\$1" (#\n[.c]) +. ds doc-mx-error +. return +. \} +. ds doc-mx-stack-mac-\n[doc-mx-stack-no] \*[doc-mx#s1] +. rm doc-mx#s1 +. +. \" III.3. MACRO/KEY: exactly MACRO with exactly content KEY +. shift 1 +. if (\n[.$] > 0) .ds doc-mx-stack-arg-\n[doc-mx-stack-no] \$* +.. +. +.\" NS Hook call-ins for mdoc(7) macros which support mdocmx(7). +.\" NS Because of the (grr) recursive descendent behaviour it is +.\" NS complicated to get (1) at all macro arguments and (2) arguments +.\" NS in plain text form. +.\" NS In general any supported macro calls doc-mx-mac-enter, which +.\" NS can be given further arguments in which case any call to +.\" NS doc-mx-mac-add-arg is effectively a no-op (this is the usual +.\" NS way to add arguments for those macros where this is the only +.\" NS possible time and place to get access to plain text content), +.\" NS then doc-mx-mac-add-arg is called repeatedly for each macro +.\" NS argument (usually done automatically from doc-print-recursive), +.\" NS and the macro level is left via doc-mx-mac-leave (this is also +.\" NS called from within doc-print-recursive). +.\" NS +.\" NS modifies: +.\" NS doc-mx-ard argument recursion depth counter +.\" NS doc-mx-arnNO macro name on recursion depth NO +.\" NS doc-mx-araNO argument string on recursion depth NO +.\" NS doc-mx-arxNO bypass - don't process this level! +. +.nr doc-mx-ard 0 +. +.de doc-mx-mac-enter +. if d doc-mx-error .return +. if !d doc-mx-gogogo .return +. +. doc-mx-mac-leave-check +. +. ie 'Ar'\$1' .ds doc-mx#s1 +. el .ie 'Cm'\$1' .ds doc-mx#s1 +. el .ie 'Dv'\$1' .ds doc-mx#s1 +. el .ie 'Er'\$1' .ds doc-mx#s1 +. el .ie 'Ev'\$1' .ds doc-mx#s1 +. el .ie 'Fl'\$1' .ds doc-mx#s1 +. el .ie 'Fn'\$1' .ds doc-mx#s1 +. el .ie 'Fo'\$1' .ds doc-mx#s1 +. el .ie 'Ic'\$1' .ds doc-mx#s1 +. el .ie 'Pa'\$1' .ds doc-mx#s1 +. el .ie 'Va'\$1' .ds doc-mx#s1 +. \" mdoc(7) macros supported automatically +. el .ie 'Sh'\$1' .ds doc-mx#s1 +. el .ie 'Ss'\$1' .ds doc-mx#s1 +. el .ie 'Sx'\$1' .ds doc-mx#s1 +. el .ie 'Xr'\$1' .ds doc-mx#s1 +. \" Bypass this level! +. el \{\ +. ds doc-mx-arx\n[doc-mx-ard] +. return +. \} +. rm doc-mx#s1 +. nr doc-mx-ard +1 +. ds doc-mx-arn\n[doc-mx-ard] \$1 +. +. \" Unfortunately mdoc(7) uses a recursive descendent parser (ssiigghh!) +. \" and modifies arguments before starting recursion, so in order to be +. \" able to provide references and anchors via string comparison some +. \" ugly hacks are necessary: doc-mx-mac-add-arg will effectively +. \" ignore its arguments because we _now_ create the result! +. shift +. if (\n[.$] == 0) .return +. while (\n[.$] > 0) \{\ +. doc-mx-mac-add-arg "\$1" +. shift +. \} +. ds doc-mx-arx\n[doc-mx-ard] +.. +. +.de doc-mx-mac-add-arg +. if d doc-mx-error .return +. if !d doc-mx-gogogo .return +. +. \" Level ignored? +. if d doc-mx-arx\n[doc-mx-ard] .return +. +. ie d doc-mx-ara\n[doc-mx-ard] \ +. ds doc-mx-ara\n[doc-mx-ard] \*[doc-mx-ara\n[doc-mx-ard]] \$* +. el .ds doc-mx-ara\n[doc-mx-ard] \$* +.. +. +.\" Deal with doc-print-recursive sequencing +.de doc-mx-mac-sequence +. if d doc-mx-error .return +. if !d doc-mx-gogogo .return +. +. ds doc-mx-mac#noclose +. doc-mx-mac-leave +. rm doc-mx-mac#noclose +.. +. +.de doc-mx-mac-leave-check +. if d doc-mx-error .return +. if !d doc-mx-gogogo .return +. +. if d doc-mx-mac-leave-last \{\ +. rm doc-mx-arx\n[doc-mx-ard] +. rm doc-mx-arn\n[doc-mx-ard] +. nr doc-mx-ard -1 +. rm doc-mx-mac-leave-last +. \} +.. +. +.de doc-mx-mac-leave +. if d doc-mx-error .return +. if !d doc-mx-gogogo .return +. +. \" After doc-mx-mac-sequence we get called again immediately +. if d doc-mx-mac-leave-last \{\ +. doc-mx-mac-leave-check +. return +. \} +. ds doc-mx-mac-leave-last +. +. \" Level ignored? +. if d doc-mx-arx\n[doc-mx-ard] \{\ +. if !d doc-mx-mac#noclose .rm doc-mx-arx\n[doc-mx-ard] +. \" May be one of the pre-prepared argument hacks +. if !d doc-mx-arn\n[doc-mx-ard] \{\ +. if !d doc-mx-mac#noclose .nr doc-mx-ard -1 +. return +. \} +. \} +. +. \" .Xr and .Sx are special: they can't create anchors, only references +. ie 'Xr'\*[doc-mx-arn\n[doc-mx-ard]]' \{\ +. \" Argument resplit necessary +. doc-mx-mac-leave-xr \*[doc-mx-ara\n[doc-mx-ard]] +. \} +. el .ie 'Sx'\*[doc-mx-arn\n[doc-mx-ard]]' \{\ +. ds mx#mac-leave-sx +. doc-mx-mac-key-find Sh "\*[doc-mx-ara\n[doc-mx-ard]]" +. if d mx#mac-leave-sx \ +. doc-mx-mac-key-find Ss "\*[doc-mx-ara\n[doc-mx-ard]]" +. rm mx#mac-leave-sx +. \} +. el \{\ +. doc-mx-mac-key-find \*[doc-mx-arn\n[doc-mx-ard]] \ + "\*[doc-mx-ara\n[doc-mx-ard]]" +. \} +. +. rm doc-mx-ara\n[doc-mx-ard] +. if !d doc-mx-mac#noclose \{\ +. rm doc-mx-arn\n[doc-mx-ard] +. nr doc-mx-ard -1 +. \} +.. +. +.\" .Xr creates external references which requires special handling. +.\" This one would be nice, but the problem with this approach is that +.\" $PAGER will _scroll_ to the anchor, so for .Xr we want unique anchors +.de doc-mx-mac-leave-xr-nonono +. \" Do we have seen this .Xr before?.. +. nr doc-mx#n1 \n[doc-mx-Xr-no] +. while (\n[doc-mx#n1] > 0) \{\ +. if '\$1'\*[doc-mx-Xr-\n[doc-mx#n1]-section]' \{\ +. if '\$2'\*[doc-mx-Xr-\n[doc-mx#n1]-name]' \{\ +. nr doc-mx#n1 \*[doc-mx-Xr-\n[doc-mx#n1]-ref] +. break +. \} +. \} +. nr doc-mx#n1 -1 +. \} +. +. \" ..otherwise create a new slot with a new reference number +. if (\n[doc-mx#n1] == 0) \{\ +. nr doc-mx-Xr-no +1 +. ds doc-mx-Xr-\n[doc-mx-Xr-no]-section \$1 +. ds doc-mx-Xr-\n[doc-mx-Xr-no]-name \$2 +. ds doc-mx-Xr-\n[doc-mx-Xr-no]-ref \n[doc-mx-refno] +. nr doc-mx#n1 \n[doc-mx-refno] +. nr doc-mx-refno +1 +. \} +. +\%\X'tty: mdocmx xr \n[doc-mx#n1] \$1 \$2'[\n[doc-mx#n1]]\c +. rm doc-mx#n1 +.. +.\" So use the running version with unique anchors instead +.de doc-mx-mac-leave-xr +. nr doc-mx#n1 \n[doc-mx-refno] +. nr doc-mx-refno +1 +\%\X'tty: mdocmx xr \n[doc-mx#n1] \$1 \$2'[\n[doc-mx#n1]]\c +. rm doc-mx#n1 +.. +. +.\" Try to find an occurrence of key-content $1 in macro stack $1 +.de doc-mx-mac-key-find +. \" 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 doc-mx#showref +. el .ie 'Ss'\$1' .ds doc-mx#showref +. el .if (\n[doc-mx-stack-no] > 0) \{\ +. ds mx#anchor +. ie !d doc-mx-stack-mac-\n[doc-mx-stack-no] . +. el .ie !'\$1'\*[doc-mx-stack-mac-\n[doc-mx-stack-no]]' .rm mx#anchor +. el .ie !d doc-mx-stack-arg-\n[doc-mx-stack-no] . +. el .if !'\$2'\*[doc-mx-stack-arg-\n[doc-mx-stack-no]]' .rm mx#anchor +. +. if d mx#anchor \{\ +. rm mx#anchor +. rm doc-mx-stack-arg-\n[doc-mx-stack-no] +. rm doc-mx-stack-mac-\n[doc-mx-stack-no] +. nr doc-mx-stack-no -1 +. +. if !d doc-mx-preprocessed \{\ +. nr doc-mx-\$1-no +1 +. ds doc-mx-\$1-\n[doc-mx-\$1-no]-arg \$2 +. ds doc-mx-\$1-\n[doc-mx-\$1-no]-ref \n[doc-mx-refno] +. nr doc-mx-refno +1 +\%\X'tty: mdocmx \$1 \*[doc-mx-\$1-\n[doc-mx-\$1-no]-ref]'\c +. ds doc-mx-\$1-\n[doc-mx-\$1-no]-anchor +. return +. \} +. \" Remove the faked anchor to enforce it's real creation +. rm doc-mx-\$1-\n[doc-mx-\$1-no]-anchor +. \} +. \} +. +. nr doc-mx#n1 \n[doc-mx-\$1-no] +. while (\n[doc-mx#n1] > 0) \{\ +. if '\$2'\*[doc-mx-\$1-\n[doc-mx#n1]-arg]' \{\ +. ds doc-mx#s1 \*[doc-mx-\$1-\n[doc-mx#n1]-ref] +. \" Special call hook (for doc-mx-mac-leave, .Sx command)? +. ie d mx#mac-leave-sx \{\ +\%[\*[doc-mx#s1]]\c +. \} +. el .ie d doc-mx-\$1-\n[doc-mx-\$1-no]-anchor \{\ +\%[\*[doc-mx#s1]]\c +. \} +. el .ie d doc-mx#showref \{\ +\%\X'tty: mdocmx \$1 \*[doc-mx#s1]'[\*[doc-mx#s1]]\c +. \} +. el \{\ +. ds doc-mx-\$1-\n[doc-mx-\$1-no]-anchor +\%\X'tty: mdocmx \$1 \*[doc-mx#s1]'\c +.\"\%\X'tty: mdocmx \$1 \*[doc-mx#s1]'[\*[doc-mx#s1]]\c +. \} +. rm doc-mx#s1 +. rm mx#mac-leave-sx +. break +. \} +. nr doc-mx#n1 -1 +. \} +. rm doc-mx#n1 +. rm doc-mx#showref +.. +. +. .\" NS doc-hyphen-flags global register .\" NS the parameter for the `.hy' request .\" NS @@ -1041,6 +1432,7 @@ .\" NS doc-reg-Sh .\" NS doc-reg-Sh1 .\" NS doc-section-XXX +.\" NS doc-mx-Sh .\" NS .\" NS width register `Sh' set in doc-common . @@ -1062,7 +1454,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Sh-font]\c -. doc-print-recursive +. doc-print-recursive Sh . \} . el \{\ . tm Usage: .Sh section_name ... (#\n[.c]) @@ -1080,6 +1472,7 @@ . . ds doc-macro-name Sh . doc-parse-args \$@ +. ds doc-dpr-preloader "\$* . . if t \ . ad @@ -1147,7 +1540,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Sh-font]\c -. doc-print-recursive +. doc-print-recursive Sh . . if t \ . ss \n[doc-reg-Sh] \n[doc-reg-Sh1] @@ -1166,6 +1559,7 @@ .\" NS local variable: .\" NS doc-reg-Ss .\" NS doc-reg-Ss1 +.\" NS doc-mx-Ss .\" NS .\" NS width register `Ss' set above . @@ -1179,7 +1573,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Sh-font]\c -. doc-print-recursive +. doc-print-recursive Ss . \} . el \{\ . tm Usage: .Ss subsection_name ... (#\n[.c]) @@ -1197,6 +1591,7 @@ . . ds doc-macro-name Ss . doc-parse-args \$@ +. ds doc-dpr-preloader "\$* . . sp . if !\n[cR] \ @@ -1211,7 +1606,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Sh-font]\c -. doc-print-recursive +. doc-print-recursive Ss . . ss \n[doc-reg-Ss] \n[doc-reg-Ss1] . diff --git a/tmac/doc-syms b/tmac/doc-syms index 084dd82..f11d20f 100644 --- a/tmac/doc-syms +++ b/tmac/doc-syms @@ -72,7 +72,8 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. ds doc-dpr-preloader UNIX +. doc-print-recursive Ux .. . . @@ -158,7 +159,7 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. doc-print-recursive Bx .. . . @@ -260,7 +261,7 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. doc-print-recursive At .. . . @@ -324,7 +325,7 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. doc-print-recursive Dx .. . . @@ -388,7 +389,7 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. doc-print-recursive Fx .. . . @@ -453,7 +454,7 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. doc-print-recursive Nx .. . . @@ -505,7 +506,7 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. doc-print-recursive Ox .. . . @@ -557,7 +558,7 @@ . if \n[doc-num-args] \ . doc-parse-space-vector . -. doc-print-recursive +. doc-print-recursive Bsx .. . . @@ -726,7 +727,7 @@ . \" replacing argument with result . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-St1] . -. doc-print-recursive +. doc-print-recursive St . \} . el \{\ . doc-St-usage @@ -856,6 +857,7 @@ . el \{\ . tmc "mdoc warning: .Lb: no description for library . tm1 " `\*[doc-arg\n[doc-arg-ptr]]' available (#\n[.c]) +. ds doc-dpr-preloader \*[doc-arg\n[doc-arg-ptr]] . ds doc-str-Lb1 library \*[Lq]\*[doc-arg\n[doc-arg-ptr]]\*[Rq] . \} . @@ -864,7 +866,7 @@ . . if \n[doc-in-library-section] \ . br -. doc-print-recursive +. doc-print-recursive Lb . if \n[doc-in-library-section] \ . br . \} diff --git a/tmac/doc.tmac b/tmac/doc.tmac index 80c6b88..7243892 100644 --- a/tmac/doc.tmac +++ b/tmac/doc.tmac @@ -473,27 +473,46 @@ .\" NS doc-str-dpr . .de doc-print-recursive +. if (\n[.$] > 0) \{\ +. if d doc-dpr-mxopen .doc-mx-mac-leave +. ie d doc-dpr-preloader \{\ +. doc-mx-mac-enter \$1 \*[doc-dpr-preloader] +. rm doc-dpr-preloader +. \} +. el .doc-mx-mac-enter "\$1" +. ds doc-dpr-mxopen +. \} +. . nr doc-reg-dpr1 \n[doc-type\n[doc-arg-ptr]] . ds doc-str-dpr "\*[doc-arg\n[doc-arg-ptr]] . . ie (\n[doc-reg-dpr1] == 1) \{\ +. if d doc-dpr-mxopen \{\ +. rm doc-dpr-mxopen +. doc-mx-mac-leave +. \} . nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c . \*[doc-str-dpr] . \} . el \{\ . nr doc-reg-dpr \n[doc-arg-ptr] +. nr doc-arg-ptr +1 . . \" the `\%' prevents hyphenation on a dash (`-') -. ie (\n[doc-reg-dpr1] == 2) \ +. ie (\n[doc-reg-dpr1] == 2) \{\ +. doc-mx-mac-add-arg \*[doc-str-dpr] . nop \%\*[doc-str-dpr]\&\c +. \} . el \{\ +. doc-mx-mac-sequence . \" punctuation character . nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c . nop \)\*[doc-str-dpr]\f[]\s[0]\c . \} . -. nr doc-arg-ptr +1 . ie (\n[doc-arg-limit] < \n[doc-arg-ptr]) \{\ +. rm doc-dpr-mxopen +. doc-mx-mac-leave . \" last argument . nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c . doc-print-and-reset @@ -562,7 +581,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-\$0-font]\c -. doc-print-recursive +. doc-print-recursive "\$0" .\" \} . \} . el \{\ @@ -624,7 +643,7 @@ . nr doc-arg-limit \n[doc-arg-ptr] . doc-parse-space-vector . \} -. doc-print-recursive +. doc-print-recursive Ar . \} .. . @@ -691,17 +710,17 @@ . in +\n[doc-indent-synopsis]u . ti -\n[doc-indent-synopsis]u . nop \*[doc-Nm-font]\c -. doc-print-recursive +. doc-print-recursive Cd . if !\n[doc-indent-synopsis-active] \ . in -\n[doc-indent-synopsis]u . \} . el \{\ . nop \*[doc-Nm-font]\c -. doc-print-recursive +. doc-print-recursive Cd . \}\} . el \{\ . nop \*[doc-Nm-font]\c -. doc-print-recursive +. doc-print-recursive Cd . \}\} . el \{\ . tm Usage: .Cd configuration_file_declaration ... (#\n[.c]) @@ -857,6 +876,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . +. ds doc-dpr-preloader \*[doc-arg\n[doc-arg-ptr]] . ie \n[doc-in-synopsis-section] \{\ . ie "\*[doc-macro-name]"In" \{\ . doc-do-func-decl @@ -866,19 +886,19 @@ . br . nr doc-arg-ptr +1 . ie (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \ -. doc-print-recursive +. doc-print-recursive In . el \ . doc-reset-args . \} . el \{\ . ds doc-arg\n[doc-arg-ptr] "<\*[doc-Pa-font]\*[doc-arg\n[doc-arg-ptr]] . as doc-arg\n[doc-arg-ptr] \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]> -. doc-print-recursive +. doc-print-recursive In . \}\} . el \{\ . ds doc-arg\n[doc-arg-ptr] "<\*[doc-Pa-font]\*[doc-arg\n[doc-arg-ptr]] . as doc-arg\n[doc-arg-ptr] \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]> -. doc-print-recursive +. doc-print-recursive In . \}\} . el \{\ . tm Usage: .In include_file ... (#\n[.c]) @@ -980,6 +1000,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . +. ds doc-dpr-preloader "\*[doc-command-name]" . ie !(\n[doc-type\n[doc-arg-ptr]] == 2) \{\ . ie "\*[doc-command-name]"" \ . tm Usage: .Nm name ... (#\n[.c]) @@ -1015,7 +1036,7 @@ . . nop \*[doc-Nm-font]\c . \} -. doc-print-recursive +. doc-print-recursive Nm . \} .. . @@ -1062,7 +1083,7 @@ . nr doc-arg-limit \n[doc-arg-ptr] . doc-parse-space-vector . \} -. doc-print-recursive +. doc-print-recursive Pa . \} . el \{\ . nop \*[doc-Pa-font]~\f[]\s[0]\c @@ -1120,7 +1141,7 @@ . nop \)\*[doc-Tn-font-size]\c . ie !\n[doc-is-reference] \{\ . nop \)\*[doc-Tn-font-shape]\c -. doc-print-recursive +. doc-print-recursive Tn . \} . el \ . doc-do-references @@ -2070,7 +2091,7 @@ . if \n[doc-arg-limit] \{\ . nr doc-arg-ptr +1 . ie (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \ -. doc-print-recursive +. doc-print-recursive Ns . el \ . doc-reset-args . \} @@ -2089,7 +2110,7 @@ . nop \)'\)\c . nr doc-arg-ptr +1 . ie (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \ -. doc-print-recursive +. doc-print-recursive Ap . el \ . doc-reset-args . \} @@ -2268,7 +2289,7 @@ . ie (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . \" skip `Sm' argument . nr doc-arg-ptr +1 -. doc-print-recursive +. doc-print-recursive Sm . \} . el \ . doc-reset-args @@ -3773,7 +3794,7 @@ . \*[doc-arg1] . el \{\ . nr doc-arg-ptr 1 -. doc-print-recursive +. doc-print-recursive It . \}\}\} . el \{\ . tm1 "mdoc warning: .It macros in lists of type `\*[doc-str-It]' @@ -4269,18 +4290,21 @@ . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \{\ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] -. ds doc-arg\n[doc-arg-ptr] \*[doc-Xr-font]\*[doc-arg\n[doc-arg-ptr]]\f[]\s[0] . . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-reg-Xr (\n[doc-arg-ptr] + 1) . \" modify second argument if it is a string and . \" remove space in between . if (\n[doc-type\n[doc-reg-Xr]] == 2) \{\ +. ds doc-dpr-preloader \*[doc-arg\n[doc-reg-Xr]] \ + \*[doc-arg\n[doc-arg-ptr]] . ds doc-arg\n[doc-reg-Xr] \*[lp]\*[doc-arg\n[doc-reg-Xr]]\*[rp] . ds doc-space\n[doc-arg-ptr] . \} . \} -. doc-print-recursive +. +. ds doc-arg\n[doc-arg-ptr] \*[doc-Xr-font]\*[doc-arg\n[doc-arg-ptr]]\f[]\s[0] +. doc-print-recursive Xr . \} . el \ . doc-Xr-usage @@ -4458,7 +4482,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Li-font]\c -. doc-print-recursive +. doc-print-recursive Dl . \} . el \ . tm Usage: .Dl argument ... (#\n[.c]) @@ -4490,7 +4514,7 @@ . ds doc-macro-name D1 . doc-parse-args \$@ . nr doc-arg-ptr 1 -. doc-print-recursive +. doc-print-recursive D1 . \} . el \ . tm Usage: .D1 argument ... (#\n[.c]) @@ -4556,7 +4580,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Ft-font]\c -. doc-print-recursive +. doc-print-recursive Vt . . if \n[doc-in-synopsis-section] \{\ . ie \n[doc-have-old-func] \ @@ -4624,7 +4648,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Ft-font]\c -. doc-print-recursive +. doc-print-recursive Ft .. . . @@ -4698,7 +4722,7 @@ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Fa-font]\c -. doc-print-recursive +. doc-print-recursive Fa . . if \n[doc-in-synopsis-section] \ . if \n[doc-have-func] \ @@ -4855,6 +4879,8 @@ . return . \} . +. ds doc-dpr-preloader \*[doc-arg\n[doc-arg-ptr]] +. . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . nop \*[doc-Fn-font]\*[doc-arg\n[doc-arg-ptr]]\c @@ -4877,11 +4903,14 @@ . nr doc-arg-ptr -1 . nop \)\*[doc-space\n[doc-arg-ptr]]\c . nr doc-arg-ptr +1 -. -. doc-print-recursive +. doc-print-recursive Fn . \} -. el \ +. el \{\ +. doc-mx-mac-enter Fn \*[doc-dpr-preloader] +. rm doc-dpr-preloader +. doc-mx-mac-leave . doc-print-and-reset +. \} . . if \n[doc-in-synopsis-section] \ . if !\n[doc-indent-synopsis-active] \ @@ -5019,6 +5048,9 @@ . nr doc-arg-ptr +1 . doc-print-prefixes . if (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \{\ +. doc-mx-mac-enter Fo "\*[doc-arg\n[doc-arg-ptr]]" +. doc-mx-mac-leave +. . nr doc-func-arg-count 1 . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] @@ -5085,7 +5117,7 @@ . ie (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \{\ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] -. doc-print-recursive +. doc-print-recursive Fc . \} . el \ . doc-print-and-reset @@ -5540,7 +5572,7 @@ . \} . el \{\ . nop \*[doc-Em-font]\c -. doc-print-recursive +. doc-print-recursive %B . \} .. . @@ -6112,7 +6144,7 @@ . \} . el \{\ . nop \*[doc-Em-font]\c -. doc-print-recursive +. doc-print-recursive %T . \} .. . @@ -6338,7 +6370,7 @@ . ie (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \{\ . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] -. doc-print-recursive +. doc-print-recursive An . \} . el \{\ . tm Usage: .An author_name ... (#\n[.c])
mdocmx.sh
Description: Bourne shell script
'\" m -- preprocess: mdocmx(1) .\"@ mdocmx.1 - mdocmx(7) preprocessor for single-pass troff. .\" .\" Written 2014 by Steffen (Daode) Nurpmeso <sdao...@users.sf.net>. .\" Public Domain . .Dd November 11, 2014 .Dt MDOCMX 1 .Os .Mx -enable . .Sh NAME .Nm mdocmx .Nd Preprocessor for the mdoc semantic markup language reference extension . .Sh SYNOPSIS .Nm .Op Fl v .Op Fl t Ar \&Sh | \&Ss . .Mx -toc . .Sh DESCRIPTION .Nm can be used to overcome the restriction that single-pass troff implementations cannot create forward references. It will collect all referenceable anchors of the .Xr mdoc 7 semantic markup language, i.e., .Ic .Sh and .Ic .Ss , as well as those from the .Xr mdocmx 7 reference extension (solely controlled by the command .Ic .Mx ) Ns . The resulting document can then be used by the normal (yet .Xr mdocmx 7 enabled) .Xr mdoc 7 macros to create references to all anchors in and of the document. . .Sh COMPATIBILITY .Nm is implemented in portable .Xr sh 1 and .Xr awk 1 . . .Sh SEE ALSO .Xr awk 1 , .Xr sh 1 , .Xr troff 1 , .Xr mdoc 7 , .Xr mdocmx 7 . .Sh HISTORY The .Nm environment appeared in 2014. . .Sh AUTHORS Written by .An Steffen Po Daode Pc Nurpmeso Aq Mt sdao...@users.sf.net . .\" s-ts-mode
diff --git a/cmd.h b/cmd.h index 9a72160..1106f61 100644 --- a/cmd.h +++ b/cmd.h @@ -66,6 +66,7 @@ #define A_PREV_TAG 54 #define A_FILTER 55 #define A_F_UNTIL_HILITE 56 +#define A_MDOCMX 84 #define A_INVALID 100 #define A_NOACTION 101 diff --git a/command.c b/command.c index ed5b323..0f4f552 100644 --- a/command.c +++ b/command.c @@ -55,6 +55,12 @@ extern int shift_count; extern int oldbot; extern int forw_prompt; +/* For mdocmx */ +#if HILITE_SEARCH +extern int hilite_search; +#endif +public char *mdocmx_line; + #if SHELL_ESCAPE static char *shellcmd = NULL; /* For holding last shell command for "!!" */ #endif @@ -80,6 +86,8 @@ static struct ungot* ungot = NULL; static int unget_end = 0; static void multi_search(); +static void mdocmx_search(); +static void _mdocmx_check_xr(); /* * Move the cursor to start of prompt line before executing a command. @@ -282,6 +290,9 @@ exec_mca() error("|done", NULL_PARG); break; #endif + case A_MDOCMX: + mdocmx_search(cbuf); + break; } } @@ -956,6 +967,159 @@ multi_search(pattern, n) } /* + * mdocmx(7) -- reference extension for mdoc(7) semantic markup language. + * Search for a specially crafted anchor in the document and scroll the + * respective position into view. + * If a matching anchor is found it may also be a reference to an external + * manual page: in this case prepare the necessary man(1) command and give the + * user the option to confirm the action + */ + static void +mdocmx_search(cbuf) + char const *cbuf; +{ + const int srch_flags = SRCH_NO_REGEX | SRCH_MDOCMX; + + PARG parg; + char *q, *qc; + long l; +#if HILITE_SEARCH + int save_hilite_search; +#endif + + parg.p_string = (char*)cbuf; + l = strtol(cbuf, &q, 10); + + if (*cbuf == '\0') + error("Need a reference anchor", NULL_PARG); + else if (*q != '\0' || l <= 0) + error("Invalid reference anchor: %s", &parg); + /* Local references are scrolled into view */ + else { + /* Local anchor: '{DIGITS}', each char doubled and ^H'd away */ + q = ecalloc(strlen(cbuf)*4 + 4*2 +1, sizeof *q); + + q[0] = '{'; q[1] = '\b'; q[2] = '{'; q[3] = '\b'; + for (qc = q + 4; *cbuf != '\0'; qc += 4, ++cbuf) + qc[0] = *cbuf, qc[1] = '\b', + qc[2] = *cbuf, qc[3] = '\b'; + qc[0] = '}'; qc[1] = '\b'; qc[2] = '}'; qc[3] = '\b'; + /*qc[4] = '\0';*/ + +#if HILITE_SEARCH + save_hilite_search = hilite_search; + repaint_hilite(0); + hilite_search = 0; +#endif + if (search(SRCH_FORW | srch_flags, q, 1) == 0 || + search(SRCH_BACK | srch_flags, q, 1) == 0) + _mdocmx_check_xr(q); + else + error("No such anchor: %s", &parg); +#if HILITE_SEARCH + hilite_search = save_hilite_search; + repaint_hilite(1); +#endif + + free(q); + } +} + +/* + * We have found the mdocmx(7) anchor -- is it an external .Xr reference? + */ + static void +_mdocmx_check_xr(anchor) + char const *anchor; +{ +#if HAVE_STRSTR + char c, *cp, *sect, *sect_top, *man, *man_top, *buf; + + /* Find the anchor on the line again; play safe */ + if ((cp = strstr(mdocmx_line, anchor)) == NULL) + goto jleave; + cp += strlen(anchor); + + /* Does a .Xr follow? + * .Xr anchor: '{!SECTION;MANUAL}', each char doubled and ^H'd away. + * In the following we simply jump off when we see faulty syntax: + * i wonder wether an error message should be used instead */ + if (strncmp(cp, "{\b{\b!\b!\b", sizeof("{\b{\b!\b!\b") -1)) + goto jleave; + + for (sect = (cp += sizeof("{\b{\b!\b!\b") -1);; cp += 4) { + if ((c = cp[0]) == '\0' || cp[1] != '\b' || + cp[2] != c || cp[3] != '\b') + goto jleave; + if (c == ';') + break; + } + sect_top = cp; + if (sect_top == sect) { + error("Bogus mdocmx(7) section reference", NULL_PARG); + goto jleave; + } + + for (man = (cp += 4);; cp += 4) { + if ((c = cp[0]) == '\0' || cp[1] != '\b' || + cp[2] != c || cp[3] != '\b') + goto jleave; + if (c == '}') + break; + } + man_top = cp; + if (man_top == man) { + error("Bogus mdocmx(7) manual reference", NULL_PARG); + goto jleave; + } + + /* This is an external reference! */ + if (secure) { + error("Feature not available in secure mode", NULL_PARG); + goto jleave; + } + +# define __Y "!man " +# define __X "Read external manual: " + buf = ecalloc(sizeof(__X __Y) -1 + + (int)(sect_top - sect) + 1 + (int)(man_top - man) +1); + + memcpy(buf, __X __Y, sizeof(__X __Y) -1); + cp = buf + sizeof(__X __Y) -1; + for (; sect < sect_top; sect += 4) + *cp++ = *sect; + *cp++ = ' '; + for (; man < man_top; man += 4) + *cp++ = *man; + *cp++ = '\0'; + + cmd_putstr(buf); + switch (getcc()) { + case '\n': + case '\r': +# if HAVE_SYSTEM + lsystem(buf + sizeof(__X) -1 + 1, "!back from external manual"); +# else + error("Command not available", NULL_PARG); +# endif + /* FALLTHRU */ + default: + break; + } + + free(buf); +# undef __X +# undef __Y + +jleave: + free(mdocmx_line); + +#else /* HAVE_STRSTR */ + (void)anchor; +#endif +} + +/* * Forward forever, or until a highlighted line appears. */ static int @@ -1784,6 +1948,12 @@ commands() case A_NOACTION: break; + case A_MDOCMX: + start_mca(A_MDOCMX, "[anchor]:", + (void*)NULL, CF_QUIT_ON_ERASE); + c = getcc(); + goto again; + default: bell(); break; diff --git a/decode.c b/decode.c index 6d0312d..3c99fc8 100644 --- a/decode.c +++ b/decode.c @@ -163,7 +163,8 @@ static unsigned char cmdtable[] = 'Q',0, A_QUIT, ':','q',0, A_QUIT, ':','Q',0, A_QUIT, - 'Z','Z',0, A_QUIT + 'Z','Z',0, A_QUIT, + CONTROL('X'),CONTROL('R'),0, A_MDOCMX }; static unsigned char edittable[] = diff --git a/less.h b/less.h index fada513..ef742f0 100644 --- a/less.h +++ b/less.h @@ -344,6 +344,7 @@ struct textlist #define SRCH_NO_REGEX (1 << 12) /* Don't use regular expressions */ #define SRCH_FILTER (1 << 13) /* Search is for '&' (filter) command */ #define SRCH_AFTER_TARGET (1 << 14) /* Start search after the target line */ +#define SRCH_MDOCMX (1 << 15) /* mdocmx(7) anchor search */ #define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \ (((t) & ~SRCH_FORW) | SRCH_BACK) : \ diff --git a/lesskey.c b/lesskey.c index 3d7571e..e987a45 100644 --- a/lesskey.c +++ b/lesskey.c @@ -126,6 +126,7 @@ struct cmdname cmdnames[] = { "index-file", A_INDEX_FILE }, { "invalid", A_UINVALID }, { "left-scroll", A_LSHIFT }, + { "mdocmx", A_MDOCMX }, { "next-file", A_NEXT_FILE }, { "next-tag", A_NEXT_TAG }, { "noaction", A_NOACTION }, diff --git a/search.c b/search.c index 24d4210..d37c210 100644 --- a/search.c +++ b/search.c @@ -827,6 +827,26 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos) if (is_filtered(linepos)) continue; + /* Special case MDOCMX searches: we actually really want to + * match the plain line content, so avoid any useless + * allocations and text conversions */ + if (search_type & SRCH_MDOCMX) { + line_match = match_pattern(info_compiled(&search_info), + search_info.text, line, line_len, &sp, &ep, 0, + search_type); + if (line_match) { + extern char *mdocmx_line; + + mdocmx_line = (char*)ecalloc(1, line_len +1); + memcpy(mdocmx_line, line, line_len); + mdocmx_line[line_len] = '\0'; + if (plinepos != NULL) + *plinepos = linepos; + return (0); + } + continue; + } + /* * If it's a caseless search, convert the line to lowercase. * If we're doing backspace processing, delete backspaces.