Hey.
Steffen Nurpmeso wrote in
<20210523212903.58vrh%[email protected]>:
|Steffen Nurpmeso wrote in
| <20210523004836.gta8l%[email protected]>:
| ...
|||> Good idea. I've further changed the Subject: to reflect the flow \
|||> of the
|||> discussion.
|| ...
|||> I also wonder if the pager wars are basically over and less(1) \
|||> won them.
|||
|||That's certainly what I thought...
|| ..
||
||Ever since less(1) started supporting OSC 8 "Hyperlinks in
||Terminal Emulators" as of version 566 i wanted to rewrite my
||mdocmx(7) extension to be based upon the OSC 8 sequences that now
||become more and more common.
...
| \X'tty osc8 [id ID] [uri URI]'
Much much better approach indeed.
Mind you, since mdocmx is sitting around since 2014/5 for my
personal fun, but OSC 8 is a nice standard that is available in
a growing number of software...
Please let me attach patches for less (v586 aka git repo, also at
[1]) which implements searching for OSC 8 sequences, grotty (git
repo from a few days ago, also at [2]) that implements the above
\X'', and a readily prepared manual page (from mdocmx, for fun).
You can look at the manual page in a less>=566 via -R, and all
will appear as desired (for mdocmx). If you would patch less, you
could type ^A (control-A) and enter the anchor to jump to. This
works even for external manual pages. (For less, when going over
git: autoreconf;make -f Makefile.aut;make.)
I think i will ask Mr. Nudelman again whether he is interested in
the patch of implementing OSC 8 id= searches.
You know, i really like being able to stay in less :)
Anyhow, with the above OSC 8 things like docbook and other XY-
to-manual page converters could easily pimp the manual pages they
produce.
[1] https://ftp.sdaoden.eu/less-osc8-search.patch
[2] https://ftp.sdaoden.eu/grotty-osc8.patch
Ciao,
P.S.: I have not tested the grotty patch with any other groff but
the 1.22.3 i have here. Just made it fit nicely in 1.22.4 aka git
head.
--steffen
|
|Der Kragenbaer, The moon bear,
|der holt sich munter he cheerfully and one by one
|einen nach dem anderen runter wa.ks himself off
|(By Robert Gernhardt)
From d374e9180166c7a655c7ee0a542df6c3643b2c74 Mon Sep 17 00:00:00 2001
Message-Id: <d374e9180166c7a655c7ee0a542df6c3643b2c74.1621728853.git.stef...@sdaoden.eu>
From: Steffen Nurpmeso <[email protected]>
Date: Sun, 23 May 2021 01:27:41 +0200
Subject: [PATCH] Support OSC 8 anchors
---
cmd.h | 1 +
command.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++++
configure.ac | 7 ++
decode.c | 3 +-
defines.ds | 7 ++
defines.o2 | 7 ++
defines.o9 | 7 ++
defines.wn | 7 ++
less.h | 3 +
less.hlp | 1 +
less.nro.VER | 6 ++
lesskey_parse.c | 3 +
search.c | 21 ++++++
13 files changed, 246 insertions(+), 1 deletion(-)
diff --git a/cmd.h b/cmd.h
index 7141817bae..b4942d44ed 100644
--- a/cmd.h
+++ b/cmd.h
@@ -69,6 +69,7 @@
/* Note "X116" refers to extended (1006) X11 mouse reporting. */
#define A_X116MOUSE_IN 68
#define A_CLR_SEARCH 70
+#define A_OSC8_SEARCH 84
/* These values must not conflict with any A_* or EC_* value. */
#define A_INVALID 100
diff --git a/command.c b/command.c
index d4e271324d..4948cc9a7d 100644
--- a/command.c
+++ b/command.c
@@ -57,6 +57,14 @@ extern int incr_search;
extern int utf_mode;
#endif
+#if HILITE_SEARCH
+extern int hilite_search;
+#endif
+
+#if OSC8_SEARCH
+public char *osc8_search_line /* = NULL */;
+#endif
+
#if SHELL_ESCAPE
static char *shellcmd = NULL; /* For holding last shell command for "!!" */
#endif
@@ -84,6 +92,11 @@ static struct ungot* ungot = NULL;
static void multi_search LESSPARAMS((char *pattern, int n, int silent));
+#if OSC8_SEARCH
+static void osc8_search();
+static void a_osc8_found();
+#endif
+
/*
* Move the cursor to start of prompt line before executing a command.
* This looks nicer if the command takes a long time before
@@ -302,6 +315,11 @@ exec_mca(VOID_PARAM)
(void) pipe_mark(pipec, cbuf);
error("|done", NULL_PARG);
break;
+#endif
+#if OSC8_SEARCH
+ case A_OSC8_SEARCH:
+ osc8_search(cbuf);
+ break;
#endif
}
}
@@ -1122,6 +1140,151 @@ multi_search(pattern, n, silent)
}
}
+/*
+ * OSC 8 search. Search for an according OSC 8 id= parameter, and scroll the
+ * respective position into view.
+ * If a matching anchor is found it may also be a reference to an external
+ * manual page, if the OSC 8 URI starts with man://: 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 OSC8_SEARCH
+ static void
+osc8_search(cbuf)
+ char const *cbuf;
+{
+ int const srch_flags = SRCH_NO_REGEX | SRCH_OSC8;
+
+ 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 {
+ static char const a_id[] = {ESC,']','8',';','i','d','=','\0'};
+
+ q = ecalloc(sizeof(a_id) + strlen(cbuf) + 1, sizeof *q);
+
+ memcpy(&q[0], a_id, sizeof(a_id) -1);
+ for (qc = &q[sizeof(a_id) -1]; *cbuf != '\0'; ++qc, ++cbuf)
+ *qc = *cbuf;
+ *qc++ = ';';
+ *qc = '\0';
+
+# undef a_OSC8_ID
+
+ 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)
+ a_osc8_found(q);
+ else
+ error("No such anchor: %s", &parg);
+# if HILITE_SEARCH
+ hilite_search = save_hilite_search;
+ repaint_hilite(1);
+# endif
+
+ free(q);
+ }
+}
+
+ static void
+a_osc8_found(anchor)
+ char const *anchor;
+{
+# if HAVE_STRSTR
+ char *cp, *sect, *sect_top, *man, *man_top, *buf;
+
+ /* Find the anchor on the line again; play safe */
+ if ((cp = strstr(osc8_search_line, anchor)) == NULL)
+ goto jleave;
+ cp += strlen(anchor);
+
+ /* We only act upon man://. (XXX Anything -> LESS_BROWSER?? */
+ if(strncmp(cp, "man://", sizeof("man://") -1))
+ goto jleave;
+ cp += sizeof("man://") -1;
+
+ sect = man_top = NULL;
+ for(man = sect_top = cp; *sect_top != ESC; ++sect_top){
+ if(*sect_top == '\0'){
+ sect = NULL;
+ break;
+ }else if(*sect_top == '.'){
+ man_top = §_top[-1];
+ sect = §_top[1];
+ }
+ }
+ if(sect == NULL || sect >= sect_top || man >= man_top){
+ error("Bogus OSC 8 man://ual reference", NULL_PARG);
+ goto jleave;
+ }
+
+ /* An external reference we can! */
+ if (secure) {
+ error("External references not available in secure mode",
+ NULL_PARG);
+ goto jleave;
+ }
+
+# define __Y "!man "
+# define __X "Read external manual: "
+ buf = ecalloc(1, sizeof(__X __Y) -1 +
+ (int)(sect_top - sect) + 1 + (int)(man_top - man) + 1 +1);
+
+ memcpy(buf, __X __Y, sizeof(__X __Y) -1);
+ cp = &buf[sizeof(__X __Y) -1];
+ for (; sect < sect_top; ++sect)
+ *cp++ = *sect;
+ *cp++ = ' ';
+ for (; man <= man_top; ++man)
+ *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(osc8_search_line);
+}
+#endif /* OSC8_SEARCH */
+
/*
* Forward forever, or until a highlighted line appears.
*/
@@ -2039,6 +2202,17 @@ commands(VOID_PARAM)
case A_NOACTION:
break;
+ case A_OSC8_SEARCH:
+#if OSC8_SEARCH
+ start_mca(A_OSC8_SEARCH, "[OSC 8 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 008a3d619d..0415e7f2bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -602,6 +602,13 @@ AH_TOP([
*/
#define LOGFILE (!SECURE)
+/*
+ * OSC8_SEARCH is 1 if you wish to support searching for OSC8 local references
+ * (as can be created via OSC 8 id= parameters).
+ * As well as external OSC 8 man://ual page references.
+ */
+#define OSC8_SEARCH (!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 9fa891969d..baa185c445 100644
--- a/decode.c
+++ b/decode.c
@@ -169,7 +169,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_OSC8_SEARCH
};
static unsigned char edittable[] =
diff --git a/defines.ds b/defines.ds
index 9d30129029..46e5f8a9c4 100644
--- a/defines.ds
+++ b/defines.ds
@@ -90,6 +90,13 @@
*/
#define LOGFILE (!SECURE)
+/*
+ * OSC8_SEARCH is 1 if you wish to support searching for OSC8 local references
+ * (as can be created via OSC 8 id= parameters).
+ * As well as external OSC 8 man://ual page references.
+ */
+#define OSC8_SEARCH (!SECURE)
+
/*
* GNU_OPTIONS is 1 if you wish to support the GNU-style command
* line options --help and --version.
diff --git a/defines.o2 b/defines.o2
index c3a0ed4c6a..908777825d 100644
--- a/defines.o2
+++ b/defines.o2
@@ -83,6 +83,13 @@
*/
#define LOGFILE (!SECURE)
+/*
+ * OSC8_SEARCH is 1 if you wish to support searching for OSC8 local references
+ * (as can be created via OSC 8 id= parameters).
+ * As well as external OSC 8 man://ual page references.
+ */
+#define OSC8_SEARCH (!SECURE)
+
/*
* GNU_OPTIONS is 1 if you wish to support the GNU-style command
* line options --help and --version.
diff --git a/defines.o9 b/defines.o9
index 55fdb85af2..b6415449d5 100644
--- a/defines.o9
+++ b/defines.o9
@@ -82,6 +82,13 @@
*/
#define LOGFILE (!SECURE)
+/*
+ * OSC8_SEARCH is 1 if you wish to support searching for OSC8 local references
+ * (as can be created via OSC 8 id= parameters).
+ * As well as external OSC 8 man://ual page references.
+ */
+#define OSC8_SEARCH (!SECURE)
+
/*
* GNU_OPTIONS is 1 if you wish to support the GNU-style command
* line options --help and --version.
diff --git a/defines.wn b/defines.wn
index 9127ca8a9c..69f412fb87 100644
--- a/defines.wn
+++ b/defines.wn
@@ -83,6 +83,13 @@
*/
#define LOGFILE (!SECURE)
+/*
+ * OSC8_SEARCH is 1 if you wish to support searching for OSC8 local references
+ * (as can be created via OSC 8 id= parameters).
+ * As well as external OSC 8 man://ual page references.
+ */
+#define OSC8_SEARCH (!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 a00a64db49..e8970ac93b 100644
--- a/less.h
+++ b/less.h
@@ -360,6 +360,9 @@ struct wchar_range_table
#define SRCH_FILTER (1 << 13) /* Search is for '&' (filter) command */
#define SRCH_AFTER_TARGET (1 << 14) /* Start search after the target line */
#define SRCH_WRAP (1 << 15) /* Wrap-around search (continue at BOF/EOF) */
+#if OSC8_SEARCH
+# define SRCH_OSC8 (1 << 16) /* OSC 8 id= search */
+#endif
#define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \
(((t) & ~SRCH_FORW) | SRCH_BACK) : \
diff --git a/less.hlp b/less.hlp
index 333a0b576c..41c65f7e8f 100644
--- a/less.hlp
+++ b/less.hlp
@@ -73,6 +73,7 @@
m_<_l_e_t_t_e_r_> Mark the current top line with <letter>.
M_<_l_e_t_t_e_r_> Mark the current bottom line with <letter>.
+ ^A_<_t_e_x_t_> Go to OSC 8 anchor <text>.
'_<_l_e_t_t_e_r_> Go to a previously marked position.
'' Go to the previous position.
^X^X Same as '.
diff --git a/less.nro.VER b/less.nro.VER
index 2f7d956262..bbb6f8cd60 100644
--- a/less.nro.VER
+++ b/less.nro.VER
@@ -105,6 +105,12 @@ and LEFTARROW commands.
Scroll horizontally right to show the end of the longest displayed line.
.IP "ESC-{ or ^LEFTARROW"
Scroll horizontally left back to the first column.
+.IP "^A"
+Enter the
+.I OSC 8
+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.
.IP "r or ^R or ^L"
Repaint the screen.
.IP R
diff --git a/lesskey_parse.c b/lesskey_parse.c
index 3190cd113e..3fc4fc3aa3 100644
--- a/lesskey_parse.c
+++ b/lesskey_parse.c
@@ -55,6 +55,9 @@ static struct lesskey_cmdname cmdnames[] =
{ "index-file", A_INDEX_FILE },
{ "invalid", A_UINVALID },
{ "left-scroll", A_LSHIFT },
+#if OSC8_SEARCH
+ { "osc8-search", A_OSC8_SEARCH },
+#endif
{ "next-file", A_NEXT_FILE },
{ "next-tag", A_NEXT_TAG },
{ "noaction", A_NOACTION },
diff --git a/search.c b/search.c
index 6277fe3c4e..01daec7d7c 100644
--- a/search.c
+++ b/search.c
@@ -1375,6 +1375,27 @@ search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos, pla
continue;
#endif
+ /* Special case OSC 8 searches: we want to match plain line
+ * content, so avoid allocations and text conversions */
+#if OSC8_SEARCH
+ if (search_type & SRCH_OSC8) {
+ line_match = match_pattern(info_compiled(&search_info),
+ search_info.text, line, line_len, &sp, &ep, 0,
+ search_type);
+ if (line_match) {
+ extern char *osc8_search_line;
+
+ osc8_search_line = (char*)ecalloc(1, line_len +1);
+ memcpy(osc8_search_line, line, line_len);
+ osc8_search_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.
--
2.31.1
From ee1627a04640576719d45c6be0807aeb9dbdacec Mon Sep 17 00:00:00 2001
Message-Id: <ee1627a04640576719d45c6be0807aeb9dbdacec.1621901733.git.stef...@sdaoden.eu>
From: Steffen Nurpmeso <[email protected]>
Date: Tue, 25 May 2021 02:07:15 +0200
Subject: [PATCH] src/devices/grotty/tty.cpp: add support for \X'tty: osc8 [id
ID] [uri URI]'
---
src/devices/grotty/tty.cpp | 79 ++++++++++++++++++++++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/src/devices/grotty/tty.cpp b/src/devices/grotty/tty.cpp
index 72f963e3de..550f0ef170 100644
--- a/src/devices/grotty/tty.cpp
+++ b/src/devices/grotty/tty.cpp
@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "driver.h"
#include "device.h"
+#include "cset.h"
#include "ptable.h"
typedef signed char schar;
@@ -56,6 +57,7 @@ static int italic_flag;
static int reverse_flag_option = 0;
static int reverse_flag;
static int old_drawing_scheme = 0;
+static int no_osc8_refs = 0;
static void update_options();
static void usage(FILE *stream);
@@ -80,8 +82,12 @@ static unsigned char bold_underline_mode;
#ifndef IS_EBCDIC_HOST
#define CSI "\033["
+#define OSC8 "\033]8;"
+#define ST "\033\\"
#else
#define CSI "\047["
+#define OSC8 "\047]8;"
+#define ST "\047\\"
#endif
// SGR handling (ISO 6429)
@@ -189,6 +195,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_osc8(char *, const environment *);
public:
tty_printer();
~tty_printer();
@@ -443,6 +450,76 @@ void tty_printer::special(char *arg, const environment *env, char type)
old_drawing_scheme = 0;
update_options();
}
+ else if (strncmp(command, "osc8", p - command) == 0 && !no_osc8_refs)
+ special_osc8(p, env);
+}
+
+void
+tty_printer::special_osc8(char *ap, const environment *env){
+ size_t idl, uril, *lp;
+ char *ap_orig, *idp, *urip, *cp, **bpp;
+
+ ap_orig = ap;
+ idl = uril = 0;
+ idp = urip = NULL;
+
+ for(lp = NULL; *ap != '\0';){
+ for(; *ap == ' ' || *ap == '\n'; ++ap)
+ ;
+ cp = ap;
+ for(; *ap != '\0' && *ap != ' ' && *ap != '\n'; ++ap)
+ ;
+ if(cp == ap){
+ assert(*ap == '\0');
+ break;
+ }
+
+ if(lp != NULL){
+ *bpp = cp;
+ *lp = (size_t)(ap - cp);
+ lp = NULL;
+
+ for(; cp < ap; ++cp)
+ if(!csprint(*cp)){
+ error("invalid X osc8 parameter content, ignoring: %1", ap_orig);
+ goto jleave;
+ }
+ }else if(!strncmp(cp, "id", (size_t)(ap - cp))){
+ lp = &idl;
+ bpp = &idp;
+ }else if(!strncmp(cp, "uri", (size_t)(ap - cp))){
+ lp = &uril;
+ bpp = &urip;
+ }else{
+ error("unknown X osc8 parameter, ignoring: %1", ap_orig);
+ goto jleave;
+ }
+ }
+
+ if(lp != NULL){
+ error("missing argument of X osc8 parameter, ignoring: %1", ap_orig);
+ goto jleave;
+ }
+
+ for(char const *ccp = OSC8; *ccp != '\0'; ++ccp)
+ add_char(*ccp, 0, env->hpos, env->vpos, env->col, env->fill, 0);
+
+ if(idp != NULL){
+ for(char const *ccp = "id="; *ccp != '\0'; ++ccp)
+ add_char(*ccp, 0, env->hpos, env->vpos, env->col, env->fill, 0);
+ while(idl-- != 0)
+ add_char(*idp++, 0, env->hpos, env->vpos, env->col, env->fill, 0);
+ }
+
+ add_char(';', 0, env->hpos, env->vpos, env->col, env->fill, 0);
+
+ while(uril-- != 0)
+ add_char(*urip++, 0, env->hpos, env->vpos, env->col, env->fill, 0);
+
+ for(char const *ccp = ST; *ccp != '\0'; ++ccp)
+ add_char(*ccp, 0, env->hpos, env->vpos, env->col, env->fill, 0);
+
+jleave:;
}
void tty_printer::change_color(const environment * const env)
@@ -836,6 +913,8 @@ int main(int argc, char **argv)
static char stderr_buf[BUFSIZ];
if (getenv("GROFF_NO_SGR"))
old_drawing_scheme = 1;
+ if(getenv("GROFF_NO_OSC8"))
+ no_osc8_refs = 1;
setbuf(stderr, stderr_buf);
setlocale(LC_CTYPE, "");
int c;
--
2.31.1
MDOCMX(7) BSD Miscellaneous Information Manual MDOCMX(7)
[1mNAME[22m]8;id=1;\]8;;\
[1m.Mx [22m— Reference extension for the mdoc semantic markup language
[1mSYNOPSIS[22m]8;id=2;\]8;;\
[1m.Mx -enable [22m[[4moutput-formats[24m]
[1m.Mx -disable[0m
[1m.Mx[0m
[1m.Mx [4m[22mmacro[0m
[1m.Mx [4m[22mmacro[24m [4mkey[0m
[1m.Mx -ix [22m[[4mcategory[24m] [4mkey[0m
[1m.Mx -sx [22m[[4mcategory[24m]
[1m.Mx -toc [22m[[4moutput-formats[24m] [[1m-tree
[4m[22moutput-formats[24m]
[1mTABLE OF CONTENTS[22m]8;id=3;\]8;;\
[4mNAME[34m[24m[]8;;#1\1]8;;\][0m
[4mSYNOPSIS[34m[24m[]8;;#2\2]8;;\][0m
[4mTABLE[24m [4mOF[24m [4mCONTENTS[34m[24m[]8;;#3\3]8;;\][0m
[4mDESCRIPTION[34m[24m[]8;;#4\4]8;;\][0m
[4mCreating[24m [4mreferenceable[24m
[4manchors[34m[24m[]8;;#5\5]8;;\][0m
[4mString[24m [4mcleanup[24m [4mand[24m [4mreference[24m
[4mprevention[34m[24m[]8;;#6\6]8;;\][0m
[4mFreely[24m [4mdefinable[24m [4manchors[24m [4mand[24m
[4mreferences[34m[24m[]8;;#7\7]8;;\][0m
[4mCreating[24m [4mtable[24m [4mof[24m
[4mcontents[34m[24m[]8;;#8\8]8;;\][0m
[4mStrings[24m [4mthat[24m [4maffect[24m
[4mmdocmx[34m[24m[]8;;#9\9]8;;\][0m
[4mIMPLEMENTATION[24m [4mNOTES[34m[24m[]8;;#10\10]8;;\][0m
[4mInternal[24m [4mextended[24m
[4msynopsis[34m[24m[]8;;#11\11]8;;\][0m
[4mENVIRONMENT[34m[24m[]8;;#12\12]8;;\][0m
[4mEXAMPLES[34m[24m[]8;;#13\13]8;;\][0m
[4mCOMPATIBILITY[34m[24m[]8;;#14\14]8;;\][0m
[4mSEE[24m [4mALSO[34m[24m[]8;;#15\15]8;;\][0m
[4mHISTORY[34m[24m[]8;;#16\16]8;;\][0m
[4mAUTHORS[34m[24m[]8;;#17\17]8;;\][0m
[4mCAVEATS[34m[24m[]8;;#18\18]8;;\][0m
[1mDESCRIPTION[22m]8;id=4;\]8;;\
mdocmx(7)[34m[]8;id=43;man://mdocmx.7\43]8;;\] [0mintroduces
referenceable index anchors to the
mdoc(7)[34m[]8;id=44;man://mdoc.7\44]8;;\][0m
semantic markup language that is used for UNIX manual pages by defining a
single new multiplexer command: [1m.Mx[22m. Users can truly enable this
exten‐
sion by setting the environment variable
MDOCMX_ENABLE[34m[]8;;#42\42]8;;\] [0mto a non-empty
value (non-empty is a troff(1)[34m[]8;id=45;man://troff.1\45]8;;\]
[0mrequirement).
The mdocmx(7)[34m[]8;id=46;man://mdocmx.7\46]8;;\] [0mreference
extension augments the standard
mdoc(7)[34m[]8;id=47;man://mdoc.7\47]8;;\][0m
document prologue – [1m.Dd[22m, [1m.Dt [22mand [1m.Os [22m– with the
new command ‘[1m.Mx[0m
[1m-enable[22m’. It can be restricted to specific output formats by
adding
those troff(1)[34m[]8;id=48;man://troff.1\48]8;;\] [0moutput devices
for which expansion is desired as fur‐
ther arguments to ‘[1m-enable[22m’; for convenience all typewriter-like
devices
can be addressed via ‘[4mtty[24m’. It is not an error to specify a
device for
which no special mdocmx(7)[34m[]8;id=49;man://mdocmx.7\49]8;;\]
[0msupport is available, but such requests
are simply ignored.
Because macros driven by single-pass troff implementations cannot create
forward references mdoc(7)[34m[]8;id=50;man://mdoc.7\50]8;;\]
[0mdocuments which use this extension need to
be preprocessed with the
mdocmx(1)[34m[]8;id=51;man://mdocmx.1\51]8;;\] [0mpreprocessor, which is
a regular
part of mdocmx(7)[34m[]8;id=52;man://mdocmx.7\52]8;;\] [0mand
implemented in portable sh(1)[34m[]8;id=53;man://sh.1\53]8;;\] [0mand
awk(1)[34m[]8;id=54;man://awk.1\54]8;;\][0m. 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 dis‐
tribute the resulting document – refer to the
[4mCOMPATIBILITY[34m[24m[]8;;#14\14]8;;\] [0msection
for more on that.
Sometimes it may be desirable to actively suppress any processing of the
mdocmx(7)[34m[]8;id=55;man://mdocmx.7\55]8;;\] [0mreference
extension: this can either be accomplished by
using [1m.Mx [22mwith the ‘[1m-disable[22m’ argument or by defining
the string
‘mx-disable[34m[]8;;#36\36]8;;\][0m’, as in
$ MDOCMX_ENABLE=anyval; export MDOCMX_ENABLE
$ < xy.z mdocmx.sh | troff -Tutf8 -mdoc -a -dmx-disable=1
[1mCreating referenceable anchors[22m]8;id=5;\]8;;\
After the extension was ‘[1m-enable[22m’d in the document prologue the
third
group of [1m.Mx [22musage forms can be used to enqueue index anchor
requests.
These requests form a last–in — first–out stack which will be consumed
(popped) by the later occurrence of a (corresponding)
mdoc(7)[34m[]8;id=56;man://mdoc.7\56]8;;\] [0m[4mmacro[0m
which supports referenceable index entries. The indices are managed with
distinct namespaces for each supported [4mmacro[24m, meaning that, e.g.,
‘[1m.Mx [22mIc
sendmail’ and ‘[1m.Mx [22mVa sendmail’ will create distinct index
anchors.
Using the plain macro [1m.Mx [22mwithout arguments creates a stack entry
for
which both, the name of the [4mmacro[24m as well as the [4mkey[24m
will be taken from
the document content. ‘[1m.Mx [4m[22mmacro[24m’ will create a stack
entry that will be
consumed by the next occurrence of [4mmacro[24m only, then taking the
[4mkey[24m off
the document content, whereas ‘[1m.Mx [4m[22mmacro[24m [4mkey[24m’
creates a stack entry that
also has its [4mkey[24m defined, and which will be consumed once an
exactly
matching macro / key pair is seen in the document only. (The
[4mEXAMPLES[34m[24m[]8;;#13\13]8;;\] [0msection gives a use case
for this form.)
Using the mdocmx(1)[34m[]8;id=57;man://mdocmx.1\57]8;;\]
[0mpreprocessor will also create referenceable
anchors for the mdoc(7)[34m[]8;id=58;man://mdoc.7\58]8;;\]
[0msection header commands [1m.Sh [22mand [1m.Ss [22mautomati‐
cally, so that a mdoc(7)[34m[]8;id=59;man://mdoc.7\59]8;;\] [0mmacro
package which supports the
mdocmx(7)[34m[]8;id=60;man://mdocmx.7\60]8;;\] [0mextension will be
enabled to actually create references
with the [1m.Sx [22mcommand, and, dependent on the output device,
cross-refer‐
ences defined via the command [1m.Xr [22mwill also be backed with
functionality.
The following macros gain support for referenceable anchors via
[1m.Mx[22m:
[1m.Ar[22m]8;id=19;\]8;;\ Command argument.
[1m.Cd[22m]8;id=20;\]8;;\ Configuration declaration.
[1m.Cm[22m]8;id=21;\]8;;\ Command modifier.
[1m.Dv[22m]8;id=22;\]8;;\ Defined variable or preprocessor constant.
[1m.Er[22m]8;id=23;\]8;;\ Error constant.
[1m.Ev[22m]8;id=24;\]8;;\ Environment variable.
[1m.Fl[22m]8;id=25;\]8;;\ Command line option (flag).
[1m.Fn[22m]8;id=26;\]8;;\ Function name.
[1m.Fo[22m]8;id=27;\]8;;\ Function name (in function block syntax).
This is mapped to
[1m.Fn[34m[22m[]8;;#26\26]8;;\][0m, [1m.Fo [22mhas no index
by itself.
[1m.Ic[22m]8;id=28;\]8;;\ Internal or interactive command.
[1m.In[22m]8;id=29;\]8;;\ An ‘include’ file for, e.g., the C
programming language.
[1m.Pa[22m]8;id=30;\]8;;\ File system path.
[1m.Va[22m]8;id=31;\]8;;\ Variable name.
[1m.Vt[22m]8;id=32;\]8;;\ Variable type, type reference.
[1mString cleanup and reference prevention[22m]8;id=6;\]8;;\
Before strings get used for anchor creation or reference look up any sur‐
rounding whitespace will be removed, as well as any preceding ‘\&’, ‘\%’
and postposed ‘\&’, ‘\%’, ‘\/’, and ‘\c’ escape characters. Yet, prefix‐
ing a command with two zero-width glyphs (after possible whitespace), as
in ‘\&\&’, has the special meaning that reference lookup will be actively
prevented for all remaining arguments of the command. For example, in
the hypothetic ‘.Ic if , elif , else , endif’ all four commands would be
linked, but in ‘.Ic \&\&if , elif , else , endif’ none of them; if that
is not desired, a new command needs to be started: ‘.Ic \&\&if , .Ic
elif , else , endif’.
[1mFreely definable anchors and references[22m]8;id=7;\]8;;\
Via the ‘[1m.Mx -ix [4m[22mcategory[24m [4mkey[24m’ and ‘[1m.Mx -ix
[4m[22mkey[24m’ usage forms anchors can
be defined almost anywhere, e.g., ‘[1m.Mx -ix [22msubsubsection "An
interesting
topic"’ defines the anchor ‘An interesting topic’ for the “key”
‘subsubsection’. The form without a specified [4mcategory[24m will use
the
builtin name ]8;id=33;\]8;;\‘ixsx’ instead.
References to anchors that have been created via [1m-ix [22mcan be made
by acti‐
vating the [1m.Sx [22msearch extension via ‘[1m.Mx -sx
[4m[22mcategory[24m’ (or ‘[1m.Mx -sx[22m’ for
the builtin ‘[4mixsx[34m[24m[]8;;#33\33]8;;\][0m’
[4mcategory[24m) followed by a normal local reference
lookup:
.Mx -sx subsubsection
.Sx "An interesting topic"
It should be noted that these usage forms are mostly ment for automated
conversion tools rather than for human manual creators: their use is non-
trivial (which is owed to the implementation of
mdoc(7)[34m[]8;id=61;man://mdoc.7\61]8;;\][0m) and the
resulting visual output should always be verified! As a rule of thumb
anchors should [4malways[24m be created inside some “normal” text so
that they
can be attached to something “physical”.
[1mCreating table of contents[22m]8;id=8;\]8;;\
The final usage form of the
mdocmx(7)[34m[]8;id=62;man://mdocmx.7\62]8;;\] [0mreference extension
allows the
creation of a document table of contents, which is of special interest
when converting a mdoc(7)[34m[]8;id=63;man://mdoc.7\63]8;;\]
[0mdocument 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
troff(1)[34m[]8;id=64;man://troff.1\64]8;;\] [0moutput devices for
which expansion is desired as further arguments to ‘[1m-toc[22m’; for
conve‐
nience all typewriter-like devices can be addressed via ‘[4mtty[24m’.
By default only [1m.Sh [22msection headers are a vivid part of the TOC;
in order
to include [1m.Ss [22msubsections also add a ‘[1m-tree[22m’ argument.
Note that if
‘[1m-tree[22m’ is used in conjunction with output-device restrictions it
will
only affect those devices that appear later on the line.
In the first of the following examples a table of contents will be gener‐
ated 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.
.Mx -toc pdf tty
.Mx -toc tty -tree html pdf
[1mStrings that affect mdocmx[22m]8;id=9;\]8;;\
[1mNote [22mthat due to deficiencies in some implementations of
troff(1)[34m[]8;id=65;man://troff.1\65]8;;\][0m
strings given on the command line (via option ‘[1m-d[22m’) have to be
given an
argument in order to be perceived on the macro level.
mx-debug]8;id=34;\]8;;\ If defined
mdocmx(7)[34m[]8;id=66;man://mdocmx.7\66]8;;\] [0mmacros will offer some
ver‐
bosity. In addition not only references will produce
visual output, but also anchors.
mx-anchor-dump]8;id=35;\]8;;\ If this is set to a filename then
the list of anchors
is dumped to it.
mx-disable]8;id=36;\]8;;\ Has the same effect as ‘[1m.Mx
-disable[22m’.
mx-toc-disable]8;id=37;\]8;;\ Forcefully turn off any table of
contents creation.
mx-toc-emerged]8;id=38;\]8;;\ Normally compact display is used
for the table of
contents, but when this string is set an emerged dis‐
play is used for the first level that lists the head‐
ings.
mx-toc-force]8;id=39;\]8;;\ Defining this string can be used to
enforce the cre‐
ation of a table of contents as specified, even if
the documents ‘[1m-toc[22m’ configuration would not
create
one for the targeted output device. A flat table of
contents will be generated unless the string value is
‘tree’.
mx-toc-name]8;id=40;\]8;;\ 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 “TABLE OF
CONTENTS”. (Note that if the table of contents has
instead been generated by the
mdocmx(1)[34m[]8;id=67;man://mdocmx.1\67]8;;\] [0mpre‐
processor then the resulting document already
includes a definition of this string to ensure com‐
patibility with, at least,
mandoc(1)[34m[]8;id=68;man://mandoc.1\68]8;;\][0m.)
mx-toc-numbered]8;id=41;\]8;;\ If defined the first level of the
table of contents
will be numbered.
[1mIMPLEMENTATION NOTES[22m]8;id=10;\]8;;\
The [1m.Mx [22mrequest 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 do
not support mdocmx(7)[34m[]8;id=69;man://mdocmx.7\69]8;;\][0m, it
also simplified implementation of the pre‐
processor.
[1mInternal extended synopsis[22m]8;id=11;\]8;;\
In addition to those usage forms that have been described above the
[1m.Mx[0m
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
mdocmx(1)[34m[]8;id=70;man://mdocmx.1\70]8;;\] [0mpre‐
processor and are solely ment to communicate the preprocessed state of
the document to the actual consumers.
For one a ‘[1m-preprocessed[22m’ flag is appended to the single
‘[1m-enable[22m’ com‐
mand in the document prologue. And then an additional
‘[1m-anchor-spass[22m’
form is introduced, which takes two or three arguments – the macro (name
of the command) for which this defines an anchor as well as its key, pos‐
sibly followed by a numeric argument that describes the relationship in
between section headings: for [1m.Sh [22mcommands it defines a running
one-based
index count of section headers, for [1m.Ss [22mcommands it instead
specifies the
index of the section header they belong to, therefore creating the possi‐
bility to generate TOCs.
[1mENVIRONMENT[22m]8;id=12;\]8;;\
Only if the environment variable ‘MDOCMX_ENABLE]8;id=42;\]8;;\’ is set
to a non-empty
value will the mdocmx(7)[34m[]8;id=71;man://mdocmx.7\71]8;;\]
[0mmacros generate the necessary information
that the chosen output device of
troff(1)[34m[]8;id=72;man://troff.1\72]8;;\] [0mcan, sufficient support
provided, use to generate table of contents, internal as well as external
references.
[1mEXAMPLES[22m]8;id=13;\]8;;\
A complete, but completely fanciful
mdoc(7)[34m[]8;id=73;man://mdoc.7\73]8;;\] [0mdocument that uses the
mdocmx(7)[34m[]8;id=74;man://mdocmx.7\74]8;;\] [0mextension would
for example be:
.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 \&Ic ,
key
.Ql .Ar .
.Mx
.It Ic .Cm
Anchor for
.Ql \&Ic ,
key
.Ql .Cm .
.Mx
.It Ic .Dv
And an anchor for
.Ql \&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 \&Fn
there will be an anchor with the key
.Ql _atexit .
.It Ic "no anchor here"
.It Ic "final anchor"
Pops the pushed
.Ql \&Ic
/
.Ql final anchor
macro / key pair.
.It Ic ciao
Pops the
.Ql \&Ic
and assigns the key
.Ql Ciao .
.El
[1mCOMPATIBILITY[22m]8;id=14;\]8;;\
Using the mdocmx(7)[34m[]8;id=75;man://mdocmx.7\75]8;;\]
[0mextension in mdoc(7)[34m[]8;id=76;man://mdoc.7\76]8;;\] [0mmanual
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
mdocmx(7)[34m[]8;id=77;man://mdocmx.7\77]8;;\] [0mmay be to preprocess
manuals with mdocmx(1)[34m[]8;id=78;man://mdocmx.1\78]8;;\] [0mon
developer machines and instead distribute
the resulting documents.
[1mSEE ALSO[22m]8;id=15;\]8;;\
awk(1)[34m[]8;id=79;man://awk.1\79]8;;\][0m,
mandoc(1)[34m[]8;id=80;man://mandoc.1\80]8;;\][0m,
mdocmx(1)[34m[]8;id=81;man://mdocmx.1\81]8;;\][0m,
sh(1)[34m[]8;id=82;man://sh.1\82]8;;\][0m,
troff(1)[34m[]8;id=83;man://troff.1\83]8;;\][0m,
mdoc(7)[34m[]8;id=84;man://mdoc.7\84]8;;\][0m
[1mHISTORY[22m]8;id=16;\]8;;\
The [1m.Mx [22menvironment appeared in 2014.
[1mAUTHORS[22m]8;id=17;\]8;;\
Idea and implementation by Steffen Nurpmeso <[[email protected][24m>.
[1mCAVEATS[22m]8;id=18;\]8;;\
Be aware that the content of the ‘[1m-width[22m’ argument to
mdoc(7)[34m[]8;id=85;man://mdoc.7\85]8;;\] [0mlists
etc. is evaluated as if it were normal document content; e.g., in the
following example the ‘Fn _atexit’ will be evaluated and may thus get
used by [1m.Mx[22m:
.Bl -tag -width ".It Fn _atexit"
When developing a manual it may be helpful to increase verbosity of the
mdocmx(1)[34m[]8;id=86;man://mdocmx.1\86]8;;\] [0mpreprocessor on
its standard error I/O channel by using the
‘[1m-v[22m’ command line flag in order to get a notion on what is going
on:
$ MDOCMX_ENABLE=1; export MDOCMX_ENABLE
$ mdocmx.sh -vv < mdocmx.7 2> stderr.txt | \
groff -Tutf8 -mdoc -dmx-toc-force=tree -dmx-debug=1 | \
less -R
$ cat stderr.txt
BSD May 25, 2021 BSD