Hello, today i'm presenting "the first release" of mdocmx(7). Shall no comments arise i'll open a bug ticket with exactly the same content in the near future. It will also be included in my fork of GPLv2 groff, S-roff. (But that will take quite some time.)
To reiterate what this thread [1,2] was about: i terribly missed any kind of interactivity in mdoc(7) (*any* Unix manual that is) and suggested extending mdoc(7) with some commands to make references, indexing etc. at all possible, i.e., by being able to specify anchors that can be referenced. [1] http://lists.gnu.org/archive/html/groff/2014-09/msg00145.html [2] http://lists.gnu.org/archive/html/groff/2014-12/msg00045.html Ingo Schwarze responded with a much better solution, using a single multiplexer command, so i jumped on that train and developed an almost release-candidate prototype until christmas 2014. Since then it matured even more. (The most notable changes are that the macros have been outsourced into their own file and that all parts of the pipeline now require $MDOCMX_ENABLE to be set and nonempty in order to understand mdocmx(7), instead of the other way 'round.) As a goody i've added PDF (via pdfmark) and HTML support today: just like i said real support requires mdoc(7) to be rewritten [2]. Real is "appealing to me" in that respect. But nothing prevented us to support the very same kind of output that we use on the TTY also for PDF. (And HTML, but i don't mention that because what the HTML driver generates is.. a pity.) So, what can mdocmx(7) do to improve manuals? The answer can be found in mdocmx.7 that is attached. A large manual that makes use of it is that of the MUA i maintain, to be included in the next release that will happen in a few minutes from now, v14.8 ("Albatros"). The following components are attached: . groff-1_22_3-mdocmx.diff: Adds support to grotty(1) and mdoc(7). . groff-rel-to-head.diff: For users of [master], patch this into groff-1_22_3-mdocmx.diff. . less-v471-mdocmx.diff: Adds support to less(1) v471 [3], the latest development version. Iirc it applies to the last official release [4] with a very simple conflict that even non-programmers should be able to resolve. Inside less(1) the new command ^A (hold control-key and press a) will prompt for a destination anchor. [3] http://www.greenwoodsoftware.com/less/less-471.tar.gz [4] http://www.greenwoodsoftware.com/less/less-458.tar.gz . mdocmx.sh: mdocmx(1), the preprocessor, written in sh(1) and awk(1). This is necessary because troff(1) doesn't support multipass. (In fact mdocmx(7)-enabled manuals can be preprocessed and the result can be shipped, so that it is _not_ necessary, then.) . mdocmx.tmac: mdocmx(7), the actual macro package. . mdocmx.7, mdocmx.1: Manual pages for mdocmx(7) and mdocmx(1). I personally use the following shell functions: mdoc() { ( export MDOCMX_ENABLE=1 mdocmx.sh "${1}" | groff -Tutf8 -mdoc -dmx-toc-force=tree | LESS= less --RAW-CONTROL-CHARS --ignore-case --no-init ) } mdocdbg() { ( export MDOCMX_ENABLE=1 mdocmx.sh -vv "${1}" | groff -Tutf8 -mdoc -dmx-toc-force=tree -dmx-debug=1 | LESS= less --RAW-CONTROL-CHARS --ignore-case --no-init ) } --steffen
diff --git a/src/devices/grotty/grotty.man b/src/devices/grotty/grotty.man
index 07ab62a..f9f4153 100644
--- a/src/devices/grotty/grotty.man
+++ b/src/devices/grotty/grotty.man
@@ -372,7 +372,7 @@ Print the version number.
.\" --------------------------------------------------------------------
.
.B grotty
-understands a single X command produced using the
+understands two X commands produced using the
.B \[rs]X
escape sequence.
.
@@ -384,6 +384,21 @@ If
is non-zero or missing, enable SGR output (this is the default),
otherwise use the old drawing scheme for bold and underline.
.
+.TP
+.BI \[rs]X'tty:\ mdocmx\ ...'
+.
+This request is used by the
+.BR mdocmx (@MAN7EXT@)
+reference extension of the
+.BR mdoc (@MAN7EXT@)
+semantic markup language and generates
+.SM BACKSPACE
+character sequences that allow supporting pager programs to perform
+reference navigation, but are ignored otherwise.
+This request should not be used directly;
+its interpretation will be suppressed unless the environment variable
+.B MDOCMX_ENABLE
+is set to a non-empty value.
.
.\" --------------------------------------------------------------------
.SH ENVIRONMENT
@@ -400,6 +415,19 @@ Colors are disabled.
.
.TP
.SM
+.B MDOCMX_ENABLE
+Can be set to enable generation and interpretation of markup of the
+.BR mdocmx (@MAN7EXT@)
+reference extension of the
+.BR mdoc (@MAN7EXT@)
+semantic markup language.
+Note that it must be set to a non-empty value because of
+.BR @g@troff (@MAN1EXT@)
+language shortcomings (the macros won't see it otherwise).
+.
+.
+.TP
+.SM
.B GROFF_FONT_PATH
A list of directories in which to search for the
.BI dev name
diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp
index 60d46e8..7b41974 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
@@ -56,6 +60,7 @@ static int italic_flag;
static int reverse_flag_option = 0;
static int reverse_flag;
static int old_drawing_scheme = 0;
+static int _mdocmx_enable = 0;
static void update_options();
static void usage(FILE *stream);
@@ -216,6 +221,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 +440,120 @@ 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 (_mdocmx_enable &&
+ !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');
+ for (; cp < ap; ++cp) {
+ __C(*cp); __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');
+
+ // 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(';'); __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('}'); __C('\b');
+ }
+
+jleave:
+ ;
+#undef __C
}
void tty_printer::change_color(const environment * const env)
@@ -863,6 +947,12 @@ int main(int argc, char **argv)
static char stderr_buf[BUFSIZ];
if (getenv("GROFF_NO_SGR"))
old_drawing_scheme = 1;
+ do {
+ char const *cp = getenv("MDOCMX_ENABLE");
+ /* Require non-empty value since only then macros see it.. */
+ if (cp != NULL && *cp != NULL)
+ _mdocmx_enable = 1;
+ } while (0);
setbuf(stderr, stderr_buf);
setlocale(LC_CTYPE, "");
int c;
diff --git a/tmac/doc-common b/tmac/doc-common
index e63fdb4..6271c75 100644
--- a/tmac/doc-common
+++ b/tmac/doc-common
@@ -1062,7 +1062,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 +1080,7 @@
.
. ds doc-macro-name Sh
. doc-parse-args \$@
+. ds doc-mx-dpr-preloader "\$*
.
. if t \
. ad
@@ -1147,7 +1148,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]
@@ -1179,7 +1180,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 +1198,7 @@
.
. ds doc-macro-name Ss
. doc-parse-args \$@
+. ds doc-mx-dpr-preloader "\$*
.
. sp
. if !\n[cR] \
@@ -1211,7 +1213,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..1a3a129 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-mx-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-mx-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..969a502 100644
--- a/tmac/doc.tmac
+++ b/tmac/doc.tmac
@@ -74,6 +74,31 @@
.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 Note: mdocmx(7) tests \n[doc-in-synopsis-section] (less doc-)
+.de Mx
+. if !'\V[MDOCMX_ENABLE]'' \{\
+. mso mdocmx.tmac
+. Mx \$@
+. \}
+..
+.de mx-mac-enter
+..
+.de mx-mac-add-arg
+..
+.de mx-mac-sequence
+..
+.de mx-mac-leave
+..
+.
+.
.\" NS doc-macro-name global string
.\" NS name of calling request (set in each user-requestable macro)
.
@@ -382,6 +407,7 @@
. nop \|\-\|\c
.
. nr doc-reg-Fl 1
+. mx-mac-enter Fl
. doc-flag-recursion
. \}\}
..
@@ -403,6 +429,8 @@
. ds doc-str-dfr "\*[doc-arg\n[doc-arg-ptr]]
.
. ie (\n[doc-reg-dfr1] == 1) \{\
+. rm doc-mx#dfr
+. mx-mac-leave
. nop \f[]\s[0]\c
. \*[doc-str-dfr]
. \}
@@ -416,22 +444,29 @@
. ie "\*[doc-str-dfr]"\*[Ba]" \{\
. if \n[doc-reg-Fl] \
. nop \|\-\*[doc-space]\c
+. ds doc-mx#dfr "|
. nop \)\*[Ba]\c
. \}
. el \{\
. ie "\*[doc-str-dfr]"\f[R]|\f[]" \{\
. if \n[doc-reg-Fl] \
. nop \|\-\*[doc-space]\c
+. ds doc-mx#dfr "|
. nop \f[R]|\f[]\c
. \}
. el \{\
. \" two consecutive hyphen characters?
. ie "\*[doc-str-dfr]"-" \
. nop \|\-\^\-\|\c
-. el \
+. el \{\
+. if !d doc-mx#dfr .ds doc-mx#dfr
+. mx-mac-add-arg "\*[doc-mx#dfr]\*[doc-str-dfr]
+. rm doc-mx#dfr
. nop \|\%\-\*[doc-str-dfr]\&\c
+. \}
. \}\}\}
. el \{\
+. mx-mac-sequence
. nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c
. nop \)\*[doc-str-dfr]\f[]\s[0]\c
. \}
@@ -441,10 +476,26 @@
. if (\n[doc-reg-dfr1] == 4) \
. nop \|\-\c
. nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c
+. rm doc-mx#dfr
+. mx-mac-leave
. doc-print-and-reset
. \}
. el \{\
. nr doc-arg-ptr +1
+.
+. ie (\n[doc-arg-limit] == \n[doc-arg-ptr]) \
+. \" Next iteration will leave
+. el .ie !(\n[doc-type\n[doc-arg-ptr]] == 2) \{\
+. rm doc-mx#dfr
+. mx-mac-sequence
+. \}
+. el .if !'\*[Ba]'\*[doc-arg\n[doc-arg-ptr]]' \{\
+. if !'\*[Ba]'\*[doc-str-dfr]' \{\
+. rm doc-mx#dfr
+. mx-mac-sequence
+. \}
+. \}
+.
. ie (\n[doc-type\n[doc-arg-ptr]] == 3) \{\
. ie (\n[doc-type\n[doc-reg-dfr]] == 4) \
. nop \|\-\c
@@ -473,10 +524,24 @@
.\" NS doc-str-dpr
.
.de doc-print-recursive
+. if (\n[.$] > 0) \{\
+. if d doc-mx-dpr-open .mx-mac-leave
+. ie d doc-mx-dpr-preloader \{\
+. mx-mac-enter "\$1" "\*[doc-mx-dpr-preloader]
+. rm doc-mx-dpr-preloader
+. \}
+. el .mx-mac-enter "\$1"
+. ds doc-mx-dpr-open
+. \}
+.
. 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-mx-dpr-open \{\
+. rm doc-mx-dpr-open
+. mx-mac-leave
+. \}
. nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c
. \*[doc-str-dpr]
. \}
@@ -484,10 +549,13 @@
. nr doc-reg-dpr \n[doc-arg-ptr]
.
. \" the `\%' prevents hyphenation on a dash (`-')
-. ie (\n[doc-reg-dpr1] == 2) \
+. ie (\n[doc-reg-dpr1] == 2) \{\
+. mx-mac-add-arg "\*[doc-str-dpr]
. nop \%\*[doc-str-dpr]\&\c
+. \}
. el \{\
. \" punctuation character
+. mx-mac-sequence
. nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c
. nop \)\*[doc-str-dpr]\f[]\s[0]\c
. \}
@@ -495,10 +563,18 @@
. nr doc-arg-ptr +1
. ie (\n[doc-arg-limit] < \n[doc-arg-ptr]) \{\
. \" last argument
+. rm doc-mx-dpr-open
+. mx-mac-leave
. nop \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]\c
. doc-print-and-reset
. \}
. el \{\
+. \" If a macro follows we want to place visual references
+. \" _before_ the following whitespace
+. if (\n[doc-type\n[doc-arg-ptr]] == 1) \{\
+. rm doc-mx-dpr-open
+. mx-mac-leave
+. \}
. nop \)\*[doc-space\n[doc-reg-dpr]]\c
. doc-print-recursive
. \}\}
@@ -562,7 +638,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 +700,7 @@
. nr doc-arg-limit \n[doc-arg-ptr]
. doc-parse-space-vector
. \}
-. doc-print-recursive
+. doc-print-recursive Ar
. \}
..
.
@@ -691,17 +767,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,28 +933,33 @@
. nr doc-curr-font \n[.f]
. nr doc-curr-size \n[.ps]
.
+.
. ie \n[doc-in-synopsis-section] \{\
+. mx-mac-enter "\*[doc-macro-name]" "\*[doc-arg\n[doc-arg-ptr]]"
. ie "\*[doc-macro-name]"In" \{\
. doc-do-func-decl
. nop \*[doc-Fd-font]#include <\*[doc-arg\n[doc-arg-ptr]]>
. ft \n[doc-curr-font]
. ps \n[doc-curr-size]u
+. mx-mac-leave
. br
. nr doc-arg-ptr +1
. ie (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \
-. doc-print-recursive
+. doc-print-recursive NoMxPlease
. 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
+. mx-mac-leave
+. doc-print-recursive NoMxPlease
. \}\}
. el \{\
+. ds doc-mx-dpr-preloader "\*[doc-arg\n[doc-arg-ptr]]
. 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 "\*[doc-macro-name]"
. \}\}
. el \{\
. tm Usage: .In include_file ... (#\n[.c])
@@ -980,6 +1061,7 @@
. nr doc-curr-font \n[.f]
. nr doc-curr-size \n[.ps]
.
+. ds doc-mx-dpr-preloader "\*[doc-command-name]
. ie !(\n[doc-type\n[doc-arg-ptr]] == 2) \{\
. ie "\*[doc-command-name]"" \
. tm Usage: .Nm name ... (#\n[.c])
@@ -1010,12 +1092,14 @@
. \}
. ti -\n[doc-indent-synopsis]u
. \}\}
-. if "\*[doc-command-name]"" \
+. if "\*[doc-command-name]"" \{\
. ds doc-command-name "\*[doc-arg\n[doc-arg-ptr]]
+. ds doc-mx-dpr-preloader "\*[doc-command-name]
+. \}
.
. nop \*[doc-Nm-font]\c
. \}
-. doc-print-recursive
+. doc-print-recursive Nm
. \}
..
.
@@ -1033,7 +1117,8 @@
.
.de Pa
. if !\n[doc-arg-limit] \{\
-. ds doc-macro-name Pa
+. ie d doc-mx-pa-is-mt .ds doc-macro-name Mt
+. el .ds doc-macro-name Pa
. doc-parse-args \$@
.
. \" default value
@@ -1041,8 +1126,10 @@
. nop \*[doc-Pa-font]~\f[]\s[0]
. \}
.
-. if !\n[doc-arg-limit] \
+. if !\n[doc-arg-limit] \{\
+. rm doc-mx-pa-is-mt
. return
+. \}
.
. nr doc-arg-ptr +1
. doc-print-prefixes
@@ -1062,9 +1149,14 @@
. nr doc-arg-limit \n[doc-arg-ptr]
. doc-parse-space-vector
. \}
-. doc-print-recursive
+. ie d doc-mx-pa-is-mt \{\
+. rm doc-mx-pa-is-mt
+. doc-print-recursive Mt
+. \}
+. el .doc-print-recursive Pa
. \}
. el \{\
+. rm doc-mx-pa-is-mt
. nop \*[doc-Pa-font]~\f[]\s[0]\c
. doc-print-and-reset
. \}
@@ -1120,7 +1212,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 +2162,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 +2181,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 +2360,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 +3865,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 +4361,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-mx-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 +4553,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 +4585,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 +4651,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 +4719,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 +4793,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 +4950,8 @@
. return
. \}
.
+. mx-mac-enter Fn "\*[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
@@ -4869,6 +4966,7 @@
. \}\}
.
. nop \)\*[rp]\)\c
+. mx-mac-leave
. if \n[doc-in-synopsis-section] \
. nop \);\)\c
.
@@ -4878,7 +4976,7 @@
. nop \)\*[doc-space\n[doc-arg-ptr]]\c
. nr doc-arg-ptr +1
.
-. doc-print-recursive
+. doc-print-recursive NoMxPlease
. \}
. el \
. doc-print-and-reset
@@ -5018,11 +5116,13 @@
.
. nr doc-arg-ptr +1
. doc-print-prefixes
+. mx-mac-enter Fo
. if (\n[doc-arg-limit] >= \n[doc-arg-ptr]) \{\
. nr doc-func-arg-count 1
. nr doc-curr-font \n[.f]
. nr doc-curr-size \n[.ps]
.
+. mx-mac-add-arg "\*[doc-arg\n[doc-arg-ptr]]
. nop \*[doc-Fn-font]\*[doc-arg\n[doc-arg-ptr]]\c
. nop \f[]\s[0]\*[lp]\)\c
. doc-reset-args
@@ -5060,10 +5160,10 @@
. nr doc-func-arg-count 0
. nr doc-in-func-enclosure 0
.
-. ie \n[doc-in-synopsis-section] \
-. nop \|\*[rp];\)
-. el \
-. nop \|\*[rp]\)
+. nop \|\*[rp]\c
+. mx-mac-leave
+. ie \n[doc-in-synopsis-section] ;\)
+. el \)
.
. \" finish function box
. br
@@ -5085,7 +5185,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 +5640,7 @@
. \}
. el \{\
. nop \*[doc-Em-font]\c
-. doc-print-recursive
+. doc-print-recursive %B
. \}
..
.
@@ -6112,7 +6212,7 @@
. \}
. el \{\
. nop \*[doc-Em-font]\c
-. doc-print-recursive
+. doc-print-recursive %T
. \}
..
.
@@ -6338,7 +6438,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])
@@ -6490,6 +6590,7 @@
.
.de Mt
. \" XXX: error handling missing
+. ds doc-mx-pa-is-mt
. Pa \$@
..
.
--- groff-1_22_3-mdocmx.diff 2015-04-30 13:21:19.000000000 +0200
+++ head-snap-mdocmx.diff 2015-04-30 13:20:20.000000000 +0200
@@ -1,5 +1,5 @@
diff --git a/src/devices/grotty/grotty.man b/src/devices/grotty/grotty.man
-index 07ab62a..f9f4153 100644
+index 0c7ed8d..41c3d54 100644
--- a/src/devices/grotty/grotty.man
+++ b/src/devices/grotty/grotty.man
@@ -372,7 +372,7 @@ Print the version number.
@@ -240,10 +240,10 @@ index 60d46e8..7b41974 100644
setbuf(stderr, stderr_buf);
setlocale(LC_CTYPE, "");
int c;
-diff --git a/tmac/doc-common b/tmac/doc-common
-index e63fdb4..6271c75 100644
---- a/tmac/doc-common
-+++ b/tmac/doc-common
+diff --git a/tmac/doc-common-u b/tmac/doc-common-u
+index 24d86d8..84524ee 100644
+--- a/tmac/doc-common-u
++++ b/tmac/doc-common-u
@@ -1062,7 +1062,7 @@
. nr doc-curr-font \n[.f]
. nr doc-curr-size \n[.ps]
@@ -296,10 +296,10 @@ index e63fdb4..6271c75 100644
.
. ss \n[doc-reg-Ss] \n[doc-reg-Ss1]
.
-diff --git a/tmac/doc-syms b/tmac/doc-syms
+diff --git a/tmac/doc-syms-u b/tmac/doc-syms-u
index 084dd82..1a3a129 100644
---- a/tmac/doc-syms
-+++ b/tmac/doc-syms
+--- a/tmac/doc-syms-u
++++ b/tmac/doc-syms-u
@@ -72,7 +72,8 @@
. if \n[doc-num-args] \
. doc-parse-space-vector
@@ -399,10 +399,10 @@ index 084dd82..1a3a129 100644
. if \n[doc-in-library-section] \
. br
. \}
-diff --git a/tmac/doc.tmac b/tmac/doc.tmac
-index 80c6b88..969a502 100644
---- a/tmac/doc.tmac
-+++ b/tmac/doc.tmac
+diff --git a/tmac/doc.tmac-u b/tmac/doc.tmac-u
+index 8b351b4..3aa8e51 100644
+--- a/tmac/doc.tmac-u
++++ b/tmac/doc.tmac-u
@@ -74,6 +74,31 @@
.eo
.
diff --git a/cmd.h b/cmd.h
index 44fdab1..10b2310 100644
--- a/cmd.h
+++ b/cmd.h
@@ -67,6 +67,7 @@
#define A_FILTER 55
#define A_F_UNTIL_HILITE 56
#define A_GOEND_BUF 57
+#define A_MDOCMX 84
#define A_INVALID 100
#define A_NOACTION 101
diff --git a/command.c b/command.c
index 89a390e..b3bb08b 100644
--- a/command.c
+++ b/command.c
@@ -57,6 +57,15 @@ extern int oldbot;
extern int forw_prompt;
extern int same_pos_bell;
+/* For mdocmx */
+#if MDOCMX
+# if HILITE_SEARCH
+extern int hilite_search;
+# endif
+public char *mdocmx_line /* = NULL */;
+static int mdocmx_enabled /* = 0 */;
+#endif
+
#if SHELL_ESCAPE
static char *shellcmd = NULL; /* For holding last shell command for "!!" */
#endif
@@ -83,6 +92,10 @@ struct ungot {
static struct ungot* ungot = NULL;
static void multi_search();
+#if MDOCMX
+static void mdocmx_search();
+static void _mdocmx_check_xr();
+#endif
/*
* Move the cursor to start of prompt line before executing a command.
@@ -285,6 +298,11 @@ exec_mca()
error("|done", NULL_PARG);
break;
#endif
+#if MDOCMX
+ case A_MDOCMX:
+ mdocmx_search(cbuf);
+ break;
+#endif
}
}
@@ -953,6 +971,163 @@ multi_search(pattern, n, silent)
}
/*
+ * mdocmx(7) -- reference extension for mdoc(7) semantic markup language.
+ * This is only enabled if the MDOCMX_ENABLE environment variable is non-empty.
+ * 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.
+ * Anyway: set lastmark() so that it is possible to jump back; let the empty
+ * anchor be an alias for the desire to do so
+ */
+#if MDOCMX
+ 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);
+
+ /* For convenience */
+ if (*cbuf == '\0')
+ gomark('\'');
+ 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)*2 + 2*2 +1, sizeof *q);
+
+ q[0] = '{'; q[1] = '\b';
+ for (qc = q + 2; *cbuf != '\0'; qc += 2, ++cbuf)
+ qc[0] = *cbuf, qc[1] = '\b';
+ qc[0] = '}'; qc[1] = '\b';
+ /*qc[2] = '\0';*/
+
+ lastmark();
+# 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", sizeof("{\b!\b") -1))
+ goto jleave;
+
+ for (sect = (cp += sizeof("{\b!\b") -1);; cp += 2) {
+ if ((c = cp[0]) == '\0' || cp[1] != '\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 += 2);; cp += 2) {
+ if ((c = cp[0]) == '\0' || cp[1] != '\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("External references 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 += 2)
+ *cp++ = *sect;
+ *cp++ = ' ';
+ for (; man < man_top; man += 2)
+ *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:
+# else /* HAVE_STRSTR */
+ (void)anchor;
+# endif
+ free(mdocmx_line);
+}
+#endif /* MDOCMX */
+
+/*
* Forward forever, or until a highlighted line appears.
*/
static int
@@ -1796,6 +1971,30 @@ commands()
case A_NOACTION:
break;
+ case A_MDOCMX:
+#if MDOCMX
+ if (!mdocmx_enabled) {
+ char const *cp = getenv("MDOCMX_ENABLE");
+ if (cp != NULL && *cp != '\0')
+ mdocmx_enabled = 1;
+ else
+ mdocmx_enabled = -1;
+ }
+ if (mdocmx_enabled < 0) {
+ error("MDOCMX_ENABLE environ not set or empty",
+ NULL_PARG);
+ break;
+ }
+
+ start_mca(A_MDOCMX, "[anchor]:",
+ (void*)NULL, CF_QUIT_ON_ERASE);
+ c = getcc();
+ goto again;
+#else
+ error("Command not available", NULL_PARG);
+ break;
+#endif
+
default:
bell();
break;
diff --git a/configure.ac b/configure.ac
index 4ac9a35..3d6bf4d 100755
--- a/configure.ac
+++ b/configure.ac
@@ -575,6 +575,12 @@ AH_TOP([
#define LOGFILE (!SECURE)
/*
+ * MDOCMX is 1 if you wish to support mdocmx(7), the reference extension
+ * of the mdoc(7) semantic markup language that is used for manual pages.
+ */
+#define MDOCMX (!SECURE)
+
+/*
* GNU_OPTIONS is 1 if you wish to support the GNU-style command
* line options --help and --version.
*/
diff --git a/decode.c b/decode.c
index c44b464..6717a33 100644
--- a/decode.c
+++ b/decode.c
@@ -164,7 +164,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('A'),0, A_MDOCMX
};
static unsigned char edittable[] =
diff --git a/defines.h.in b/defines.h.in
old mode 100755
new mode 100644
index 46232c0..b68a198
--- a/defines.h.in
+++ b/defines.h.in
@@ -88,6 +88,12 @@
#define LOGFILE (!SECURE)
/*
+ * MDOCMX is 1 if you wish to support mdocmx(7), the reference extension
+ * of the mdoc(7) semantic markup language that is used for manual pages.
+ */
+#define MDOCMX (!SECURE)
+
+/*
* GNU_OPTIONS is 1 if you wish to support the GNU-style command
* line options --help and --version.
*/
diff --git a/less.h b/less.h
index 4f5590e..9a876d6 100644
--- a/less.h
+++ b/less.h
@@ -364,6 +364,9 @@ struct wchar_range_table
#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 */
+#if MDOCMX
+#define SRCH_MDOCMX (1 << 15) /* mdocmx(7) anchor search */
+#endif
#define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \
(((t) & ~SRCH_FORW) | SRCH_BACK) : \
diff --git a/less.hlp b/less.hlp
index e4cbf7a..7fc33cf 100755
--- a/less.hlp
+++ b/less.hlp
@@ -66,6 +66,8 @@
Each "find open bracket" command goes backward to the open bracket
matching the (_N-th) close bracket in the bottom line.
+ ^A_<_t_e_x_t_> Go to mdocmx(7) anchor <text>.
+
m_<_l_e_t_t_e_r_> Mark the current position with <letter>.
'_<_l_e_t_t_e_r_> Go to a previously marked position.
'' Go to the previous position.
diff --git a/less.nro b/less.nro
index 9789eaf..f8c17d2 100644
--- a/less.nro
+++ b/less.nro
@@ -97,6 +97,17 @@ Scroll horizontally left N characters, default half the screen width
(see the \-# option).
If a number N is specified, it becomes the default for future RIGHTARROW
and LEFTARROW commands.
+.IP "^A"
+Enter the
+.I mdocmx
+(7) (a reference extension for the
+.I mdoc
+(7) semantic markup language used for manual pages)
+anchor to go to; the position before scrolling to an anchor will be
+recognized as the last position mark and can thus be scrolled to either
+by using ^A again with an empty anchor or by using the "''" command sequence.
+This command is only available if the MDOCMX_ENABLE environment variable is
+set to a non-empty value.
.IP "r or ^R or ^L"
Repaint the screen.
.IP R
diff --git a/lesskey.c b/lesskey.c
index d91b9b7..d917f94 100644
--- a/lesskey.c
+++ b/lesskey.c
@@ -127,6 +127,9 @@ struct cmdname cmdnames[] =
{ "index-file", A_INDEX_FILE },
{ "invalid", A_UINVALID },
{ "left-scroll", A_LSHIFT },
+#if MDOCMX
+ { "mdocmx", A_MDOCMX },
+#endif
{ "next-file", A_NEXT_FILE },
{ "next-tag", A_NEXT_TAG },
{ "noaction", A_NOACTION },
diff --git a/search.c b/search.c
index c1aa6a9..f57dc4c 100644
--- a/search.c
+++ b/search.c
@@ -1269,6 +1269,28 @@ 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 MDOCMX
+ 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;
+ }
+#endif
+
/*
* If it's a caseless search, convert the line to lowercase.
* If we're doing backspace processing, delete backspaces.
mdocmx.sh
Description: Bourne shell script
.\"@ 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 <[email protected]>. .\" 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'@\$2@\fP\&\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 <small>@\$2@</small> .. .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
'\" -- preprocess: mdocmx(1) .\"@ mdocmx.7 - mdocmx(7) reference manual. .\"@ mdocmx(7) extends the mdoc(7) semantic markup language by references, .\"@ allowing mdoc(7) to create anchors and table of contents. .\" .\" Written 2014 - 2015 by Steffen (Daode) Nurpmeso <[email protected]>. .\" Public Domain . .Dd April 27, 2015 .Dt MDOCMX 7 .Os .Mx -enable . . .Sh NAME . .Nm .Mx .Nd Reference extension for the mdoc semantic markup language . . .Sh SYNOPSIS . .Nm .Fl enable .Op Ar output-formats .Pp .Nm .Fl disable .Pp .Nm .Nm .Ar macro .Nm .Ar macro Ar key .Pp .Bk .Nm .Fl toc .Op Ar output-formats .Op Fl tree Ar output-formats .Ek . . .Mx -toc -tree html xhtml pdf ps . . .Sh DESCRIPTION . .Xr mdocmx 7 introduces referenceable index anchors to the .Xr mdoc 7 semantic markup language that is used for .Ux manual pages by defining a single new multiplexer command: .Nm . A precondition for enabling this extension in all involved parts of the manual page formatting system, including the .Ev PAGER used to view the formatted output, is to set the environment variable .Ev MDOCMX_ENABLE to a non-empty value (the value is necessary to make the variable perceivable on the macro level, i.e., to overcome a deficiency of the .Xr troff 1 command set). . .Pp The .Xr mdocmx 7 reference extension augments the standard .Xr mdoc 7 document prologue \(en .Ic \&.Dd , .Ic \&.Dt and .Ic \&.Os \(en with the new command .Ql Nm Fl enable . It can be restricted to specific output formats by adding those .Xr troff 1 output devices for which expansion is desired as further arguments to .Ql Fl enable ; for convenience all typewriter-like devices can be addressed via .Ql Ar tty . It is not an error to specify a device for which no special .Xr mdocmx 7 support is available, but such requests are simply ignored. . .Pp 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. . .Pp Sometimes it may be desirable to actively suppress any processing of the .Xr mdocmx 7 reference extension: this can either be accomplished by using .Nm with the .Ql Fl disable argument or by defining the string .Ql Dv mx-disable , as in . .Bd -literal -offset indent $ MDOCMX_ENABLE=anyval; export MDOCMX_ENABLE $ < xy.z mdocmx.sh | troff -Tutf8 -mdoc -a -dmx-disable=1 .Ed . .Ss Creating referenceable anchors . After the extension was .Ql Fl enable Ns d in the document prologue the third 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., .Ql Ic .Mx Ns \0\&Ic sendmail and .Ql Ic .Mx Ns \0\&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. .Ql 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 .Ql 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 commands .Ic .Sh and .Ic .Ss automatically, 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 command, and, dependent on the output device, cross-references defined via the command .Ic .Xr will also be backed with functionality. The following macros gain support for referenceable anchors via .Nm : . .Pp .Bl -tag -compact -width ".It Ic ._B" .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 .In An .Ql include file for, e.g., the C programming language. .Mx .It Ic .Pa File system path. .Mx .It Ic .Va Variable name. .Mx .It Ic .Vt Variable type, type reference. .El . .Ss String cleanup and reference prevention . Before strings get used for anchor creation or reference look up any surrounding whitespace will be removed, as well as any preceding .Ql \e& , .Ql \e% and postposed .Ql \e& , .Ql \e% , .Ql \e/ , and .Ql \ec escape characters. However, reference look up can be actively prevented by prefixing two zero-width glyphs (after possible whitespace), i.e., .Ql \e&\e& . . .Ss Creating table of contents . The final usage form 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 or PDF. To restrict the creation of the table of contents to special output formats, add the names of those .Xr troff 1 output devices for which expansion is desired as further arguments to .Ql Fl toc ; for convenience all typewriter-like devices can be addressed via .Ql Ar tty . . .Pp By default only .Ic .Sh section headers are a vivid part of the TOC; in order to include .Ic .Ss subsections also add a .Ql Fl tree argument. Note that if .Ql Fl tree is used in conjunction with output-device restrictions it will only affect those devices that appear later on the line. . .Pp In the first of the following examples a table of content will be generated for PDF and typewriter-like devices. In the second example a tree of contents will instead be generated for the output formats PDF and HTML, whereas typewriter-like devices will see a flat table of contents with only section headers. . .Bd -literal -offset indent \&.Mx -toc pdf tty \&.Mx -toc tty -tree html pdf .Ed . .Ss Strings that affect mdocmx . .Sy Note that due to deficiencies in some implementations of .Xr troff 1 strings given on the command line (via option .Ql Fl d Ns ) have to be given an argument in order to be perceived on the macro level. . .Bl -tag -width ".It Dv _docmx-_oc-_merged" .Mx .It Dv mx-debug If defined .Xr mdocmx 7 macros will offer some verbosity. In addition not only references will produce visual output, but also anchors. . .Mx .It Dv mx-disable Has the same effect as .Ql Nm Fl disable . . .Mx .It Dv mx-toc-emerged Normally compact display is used for the table of contents, but when this string is set an emerged display is used for the first level that lists the headings. . .Mx .It Dv mx-toc-force Defining this string can be used to enforce the creation of a table of content as specified, even if the documents .Ql Fl toc configuration wouldn't create one for the targeted output device. A flat table of content will be generated unless the string value is .Ql tree . . .Mx .It Dv mx-toc-name If defined its content is used as the headline of the table of contents, which can be used for, e.g., localization purposes. The default is .Dq TABLE OF CONTENTS . (Note that if the table of contents has instead been generated by the .Xr mdocmx 1 preprocessor then the resulting document already includes a definition of this string to ensure compatibility with, at least, .Xr mandoc 1 . ) . .Mx .It Dv mx-toc-numbered If defined the first level of the table of contents will be numbered. .El . . .Sh IMPLEMENTATION NOTES . The .Nm request cannot share a line with other macros, neither in the document prologue nor in its content. Whereas that is mostly owed to the necessity of ensuring (backward) compatibility with environments that don't support .Xr mdocmx 7 , it also simplified implementation of the preprocessor. . .Ss Internal extended synopsis . In addition to those usage forms that have been described above the .Ic .Mx multiplexer command also understands further flags and arguments which are of possible interest for formatter and macro implementors. These further flags and arguments are only generated by the .Xr mdocmx 1 preprocessor and are solely ment to communicate the preprocessed state of the document to the actual consumers. . .Pp For one a .Ql Fl preprocessed flag is appended to the single .Ql Fl enable command in the document prologue. And then an additional .Ql Fl anchor-spass form is introduced, which takes two or three arguments \(en the macro (name of the command) for which this defines an anchor as well as its key, possibly followed by a numeric argument that describes the relationship in between section headings: for .Ic .Sh commands it defines a running one-based index count of section headers, for .Ic .Ss commands it instead specifies the index of the section header they belong to, therefore creating the possibility to generate TOCs. . . .Sh ENVIRONMENT . Only if the environment variable .Mx .Ql Ev MDOCMX_ENABLE is set to a non-empty value will the .Xr mdocmx 7 macros generate the necessary information that the chosen output device of .Xr troff 1 can, sufficient support provided, use to generate table of contents, internal as well as external references. All parts of the processing pipeline should be expected to require this environment variable to be set (to a non-empty value). . . .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 April 22, 2015 \&.Dt MDOCMX-EXAMPLE 7 \&.Os \&.Mx -enable tty \&. \&.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 \&.Ql \e&Ic , key \&.Ql .Ar . \&.Mx \&.It Ic .Cm Anchor for \&.Ql \e&Ic , key \&.Ql .Cm . \&.Mx \&.It Ic .Dv And an anchor for \&.Ql \e&Ic , key \&.Ql .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 \&.Ql \e&Fn there will be an anchor with the key \&.Ql _atexit . \&.It Ic "no anchor here" \&.It Ic "final anchor" Pops the pushed \&.Ql \e&Ic / \&.Ql final anchor macro / key pair. \&.It Ic ciao Pops the \&.Ql \e&Ic and assigns the key \&.Ql 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 with .Xr mdocmx 1 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 . Idea and implementation by .An Steffen Nurpmeso Aq Mt [email protected] . .An Ingo Schwarze Aq Mt [email protected] designed the original command semantics. . . .Sh CAVEATS . Be aware that the content of the .Ql Fl width argument to .Xr mdoc 7 lists etc. is evaluated as if it were normal document content; e.g., in the following example the .Ql \&Fn _atexit will be evaluated and may thus get used by .Nm : . .Pp .Dl ".Bl -tag -width "".It Fn _atexit""" . .Pp When developing a manual it may be helpful to increase verbosity of the .Xr mdocmx 1 preprocessor on its standard error I/O channel by using the .Ql Fl v command line flag in order to get a notion on what is going on: . .Bd -literal -offset indent $ MDOCMX_ENABLE=1; export MDOCMX_ENABLE $ mdocmx.sh -vv < mdocmx.7 2> stderr.txt | \e groff -Tutf8 -mdoc -dmx-toc-force=tree -dmx-debug=1 | less $ cat stderr.txt .Ed . .\" s-ts-mode
'\" -- preprocess: mdocmx(1) .\"@ mdocmx.1 - mdocmx(7) preprocessor for single-pass troff. .\"@ mdocmx(7) extends the mdoc(7) semantic markup language by references, .\"@ allowing mdoc(7) to create anchors and table of contents. .\" .\" Written 2014 - 2015 by Steffen (Daode) Nurpmeso <[email protected]>. .\" Public Domain . .Dd April 22, 2015 .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 t | Fl T Ar \&Sh | sh | \&Ss | ss Op Fl c .Op Fl v . . .Mx -toc html xhtml pdf ps . . .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 in a .Xr mdoc 7 document, i.e., .Ic .Sh and .Ic .Ss section headers as well as any explicitly set anchor of the .Xr mdocmx 7 reference extension. The resulting .Xr mdoc 7 document is itself distributable. It will be displayed normally unless .Xr mdocmx 7 enabled .Xr mdoc 7 macros or special formatter programs with .Xr mdocmx 7 support are used which can make use of the generated anchors. . .Bl -tag -width ".Fl _[9] Ar _" .Mx .It Fl c In conjunction with .Fl t or .Fl T this will request compact display for the first level of the generated table of contents (the level covering .Ic .Ss subsections will always be displayed in compact format). . .Mx .It Fl t Replace any .Ql .Mx -toc command line in the document with a table of content that consists of section headers only. . .Mx .It Fl T Ar \&Sh | sh | \&Ss | ss Replace any .Ql .Mx -toc command line in the document with a table of contents that either consists of only section headers .Pf ( Sq \&Sh Ns ) or the full tree of headers and their subsections .Pf ( Sq \&Ss Ns ) . . .Mx .It Fl v Enable warning messages. Use multiple times to gain processing verbosity, too. Verbosity is highly recommendet when developing .Xr mdocmx 7 enabled .Xr mdoc 7 documents. .El . . .Sh IMPLEMENTATION NOTES . .Nm works without any ado for any tested real-life .Xr mdoc 7 document, but its capabilities can be exceeded by making heavy use of all the possibilities that .Xr troff 1 and the .Xr mdoc 7 semantic markup language have to offer. A future multi-pass .Xr troff 1 will not require any preprocessor at all, nor do specialized formatters already today. Note that these restrictions should apply to .Xr mdocmx 7 enabled documents that make use of anchors created by the extension command .Ic .Mx only \(en normal .Xr mdoc 7 command based anchors should always be parsed correctly in all cases. Use verbosity on the preprocessor and enable debug on the macro side when developing manuals. . . .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 Nurpmeso Aq Mt [email protected] . . .\" s-ts-mode
