[-- Attachment #1: Type: text/plain, Size: 3594 bytes --] -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 Hi folks, This wraps up an examplenimplementation of `Ix' for mdoc(7) by extending `Xr' to handle indices made with `Ix'. In other words, linking to sections in other manpages. Enclosed is the patch. * BACKGROUND `Ix' is a macro proposal that inserts an anchor at an arbitrary location in an mdoc(7) document. There are already two implicit anchors generated with `Sh' and `Ss'; `Ix' allows for the same. As with `Sh' and `Ss', `Ix' anchors can be linked with the `Sx' macro. In effect, this allows doing things like: .Bl -tag -width Ds .It Fl b Ar moo .Ix -b flag This is a flag... ... .El Then later: The .Sx -b flag allows us to... For completeness, I also add an `Lkx' macro that serves to link to sections in the same document by a different name. Thus, The .Lkx "-b flag" "bee flag" allows us to... I actually don't like the `Lkx' and would prefer `Sxx' or something, but bikeshed bikeshed bikeshed. `Ix' is nice because it fits with the existing `Sx' model and can simply be ignored in non-conformant tools like groff(1). Output modes /not/ allowing for linkage, such as -Tascii, can simply discard `Ix' and render the contents of `Sx' as-is. This is the existing behaviour when using `Sx' to link to `Sh' and so on. * INTER-PAGE LINKING The remaining problem is inter-document linkage. This can be easily accomodated by extending `Xr' to accept 1, 2, 3, or 4 arguments: .Xr NAME [SECTION [LINK [LINKNAME]]] So we can have .Xr foo .Xr foo 1 .Xr foo 1 NAME .Xr foo 1 NAME "name section" By default, links to other sections can be formatted as "foo(1) section NAME", but this needs some more consideration because it might not be a section that's linked. * PROBLEMS All of this works beautifully in -Thtml (and can be extended to -Tpdf and friends). The problem is in -Tascii, which as it stands can't support links. (That's beyond the scope of these patches.) While we can get away with `Sx' doing nothing for sections, using `Sx' in console mode to refer to `Ix' tags is confusing because, well, they don't exist. There is, however, a solution. I don't have a patch for this (yet), but am working on it. Essentially, -Tascii knows the exact line and column of its output. When we invoke an `Ix', we remember the output line and column. Then when we use `Sx', we can refer to the line and column of the origin. For example, Hello .Ix world world. ... ... Link to .Sx world . Right now, this produces "Link to world". Why not make it do "Link to world [line 23, column 45]"? This way, we can have arbitrary links in - -Tascii and quickly jump to the locations in the manpager. This doesn't do much for `Xr', unfortunately. Best, Kristaps -----BEGIN PGP SIGNATURE----- Comment: GPGTools - https://gpgtools.org iQIcBAEBCgAGBQJVAP8VAAoJEMT2SUY9XBES7A8QAIbH4nUAzZwf05As7tKiOW+u YV9kHedKZjgYWPX6MgUwGyl+MRwqdVcT+Xx/LPhJtXsnzBGwd+1CsKsAZFTfi7rr dyB3ZupSf3ZF89X+aRn3MpY8A+7XiKUm/seXZFLtB/u7rMRnEP0TVJF1oWF6V5oW fLAQvuq7Aw5HkPj1nscUSQO6pQx/xpYozgZ5R8E3aBSN7DRFj/6U6784C7AlIia8 IKrnvp4WJVYulDgwADxQE0SgxOfeIT+aDDIqGCy3gqfIeH1xsgiXHsB+IJQkgsLT lxnw3blrPnDoEcYaUu0/u+i57z+Q+REbPYgkNENp7lZaHqopovY/lX2nZCmacyOE yMONC9E7vLvnPVikyNfz2V6FXG3hb76m1HBxtPCcTZnn25uzbUI3Ha+N8pyMEL77 6tV+H7G8/ffkoS9UBXODyXETGqjCLG09L24k+iiIKuamEfaQcyHY46p8Nyo+TpH9 LES3RsquRUIdz96yK4kbLH964x8juU6fdCkZ8cR5iPYb+SdUz6RkmBAChlFHYPjB 3W/sEr+aw3AOsghaNwUznSCeNx9Ag492U0nIwPOeeUaWG9k68/Jd7mbcuy8avPLP 7F1O9j+g7mugmhHCcaIkPlsfbgwhtWmbmbPVGAqDgalzccxPA/0UyOsZ3/lGRy/t hgOknxeuWPvk7B4vlC14 =cGAE -----END PGP SIGNATURE----- [-- Attachment #2: index.patch --] [-- Type: text/plain, Size: 12791 bytes --] ? .DS_Store ? Makefile.local ? cgi.h ? config.h ? config.log ? configure.local ? foo.7 ? foo.7.html ? foo.html ? index.patch ? linking.patch ? machine.diff ? tbl.patch ? tbl2.patch Index: html.c =================================================================== RCS file: /home/cvs/mdocml/mdocml/html.c,v retrieving revision 1.185 diff -u -p -r1.185 html.c --- html.c 21 Jan 2015 20:33:25 -0000 1.185 +++ html.c 12 Mar 2015 02:49:07 -0000 @@ -707,7 +707,8 @@ buffmt_includes(struct html *h, const ch } void -buffmt_man(struct html *h, const char *name, const char *sec) +buffmt_man(struct html *h, const char *name, + const char *sec, const char *index) { const char *p, *pp; @@ -731,6 +732,10 @@ buffmt_man(struct html *h, const char *n } if (pp) bufcat(h, pp); + if (NULL != index) { + bufcat(h, "#index-"); + bufcat_id(h, index); + } } void @@ -752,7 +757,12 @@ bufcat_id(struct html *h, const char *sr { /* Cf. <http://www.w3.org/TR/html4/types.html#h-6.2>. */ - - while ('\0' != *src) - bufcat_fmt(h, "%.2x", *src++); + for ( ; '\0' != *src; src++) { + if (isalnum((unsigned int)*src)) + bufncat(h, src, 1); + else if (' ' == *src) + bufncat(h, "_", 1); + else + bufcat_fmt(h, "%.2x", *src); + } } Index: html.h =================================================================== RCS file: /home/cvs/mdocml/mdocml/html.h,v retrieving revision 1.70 diff -u -p -r1.70 html.h --- html.h 2 Dec 2014 10:08:06 -0000 1.70 +++ html.h 12 Mar 2015 02:49:08 -0000 @@ -171,7 +171,7 @@ void bufcat_style(struct html *, void bufcat_su(struct html *, const char *, const struct roffsu *); void bufinit(struct html *); -void buffmt_man(struct html *, +void buffmt_man(struct html *, const char *, const char *, const char *); void buffmt_includes(struct html *, const char *); Index: mdoc.7 =================================================================== RCS file: /home/cvs/mdocml/mdocml/mdoc.7,v retrieving revision 1.252 diff -u -p -r1.252 mdoc.7 --- mdoc.7 23 Feb 2015 13:31:04 -0000 1.252 +++ mdoc.7 12 Mar 2015 02:49:08 -0000 @@ -440,6 +440,8 @@ in the alphabetical .El .Ss Sections and cross references .Bl -column "Brq, Bro, Brc" description +.It Sx \&Ix Ta index anchor (one line) +.It Sx \&Lkx Ta internal cross reference to an index .It Sx \&Sh Ta section header (one line) .It Sx \&Ss Ta subsection header (one line) .It Sx \&Sx Ta internal cross reference to a section or subsection @@ -510,6 +512,7 @@ in the alphabetical .Bl -column "Brq, Bro, Brc" description .It Sx \&An Ta author name (>0 arguments) .It Sx \&Lk Ta hyperlink: Ar uri Op Ar name +.It Sx \&Lkx Ta index: Ar index Op Ar name .It Sx \&Mt Ta Do mailto Dc hyperlink: Ar address .It Sx \&Cd Ta kernel configuration declaration (>0 arguments) .It Sx \&Ad Ta memory address (>0 arguments) @@ -1854,6 +1857,18 @@ will preserve the semicolon whitespace e .Pp See also .Sx \&Bl . +.Ss \&Ix +Specify an index point with an arbitrary name. +The argument is not printed. +Its syntax is as follows: +.Pp +.Dl Pf \. Ix Ar index +.Pp +An index is an anchor within a document. +It can be accessed with +.Sx \&Lkx +and +.Sx \&Sx . .Ss \&Lb Specify a library. The syntax is as follows: @@ -1905,6 +1920,25 @@ Examples: .Pp See also .Sx \&Mt . +.Ss \&Lkx +Formats an index link. +If invoked without arguments, is the same as +.Sx \&Sx . +Its syntax is as follows: +.Pp +.Dl Pf \. Sx \&Lkx Ar index Op Ar name +.Pp +Examples: +.Bd -literal -offset indent +\&.Ix foobar +Some text here. +\&.Lkx foobar \(dqGo to the above link\(dq +.Ed +.Pp +See also +.Sx \&Ix +and +.Sx \&Sx . .Ss \&Lp Synonym for .Sx \&Pp . @@ -2705,18 +2739,31 @@ Link to another manual .Pq Qq cross-reference . Its syntax is as follows: .Pp -.D1 Pf \. Sx \&Xr Ar name Op section +.D1 Pf \. Sx \&Xr Ar name Op section Op index Op index_name .Pp Cross reference the .Ar name and .Ar section -number of another man page; -omitting the section number is rarely useful. +number of another man page. +.Pq Omitting the section number is rarely useful. +If +.Ar index +is specified, the link is to a position specified with +.Ss \&Ix , +.Ss \&Sh , +or +.Ss \&Ss . +If +.Ar index_name +is used as well, it is printed instead of +.Ar index , +which is used purely as the index location. .Pp Examples: .Dl \&.Xr mandoc 1 .Dl \&.Xr mandoc 1 \&; +.Dl \&.Xr mandoc 1 "SEE ALSO" .Dl \&.Xr mandoc 1 \&Ns s behaviour .Ss \&br Emits a line-break. Index: mdoc.c =================================================================== RCS file: /home/cvs/mdocml/mdocml/mdoc.c,v retrieving revision 1.238 diff -u -p -r1.238 mdoc.c --- mdoc.c 12 Feb 2015 13:00:52 -0000 1.238 +++ mdoc.c 12 Mar 2015 02:49:09 -0000 @@ -61,10 +61,12 @@ const char *const __mdoc_macronames[MDOC "Fo", "Fc", "Oo", "Oc", "Bk", "Ek", "Bt", "Hf", "Fr", "Ud", "Lb", "Lp", - "Lk", "Mt", "Brq", "Bro", + "Lk", "Lkx", + "Mt", "Brq", "Bro", "Brc", "%C", "Es", "En", "Dx", "%Q", "br", "sp", - "%U", "Ta", "ll", "text", + "%U", "Ta", "ll", "Ix", + "text", }; const char *const __mdoc_argnames[MDOC_ARG_MAX] = { Index: mdoc.h =================================================================== RCS file: /home/cvs/mdocml/mdocml/mdoc.h,v retrieving revision 1.136 diff -u -p -r1.136 mdoc.h --- mdoc.h 12 Feb 2015 12:24:33 -0000 1.136 +++ mdoc.h 12 Mar 2015 02:49:09 -0000 @@ -126,6 +126,7 @@ enum mdoct { MDOC_Lb, MDOC_Lp, MDOC_Lk, + MDOC_Lkx, MDOC_Mt, MDOC_Brq, MDOC_Bro, @@ -140,6 +141,7 @@ enum mdoct { MDOC__U, MDOC_Ta, MDOC_ll, + MDOC_Ix, MDOC_MAX }; Index: mdoc_html.c =================================================================== RCS file: /home/cvs/mdocml/mdocml/mdoc_html.c,v retrieving revision 1.226 diff -u -p -r1.226 mdoc_html.c --- mdoc_html.c 3 Mar 2015 21:11:34 -0000 1.226 +++ mdoc_html.c 12 Mar 2015 02:49:10 -0000 @@ -92,6 +92,7 @@ static int mdoc_ic_pre(MDOC_ARGS); static int mdoc_igndelim_pre(MDOC_ARGS); static int mdoc_in_pre(MDOC_ARGS); static int mdoc_it_pre(MDOC_ARGS); +static int mdoc_ix_pre(MDOC_ARGS); static int mdoc_lb_pre(MDOC_ARGS); static int mdoc_li_pre(MDOC_ARGS); static int mdoc_lk_pre(MDOC_ARGS); @@ -231,6 +232,7 @@ static const struct htmlmdoc mdocs[MDOC_ {mdoc_lb_pre, NULL}, /* Lb */ {mdoc_pp_pre, NULL}, /* Lp */ {mdoc_lk_pre, NULL}, /* Lk */ + {mdoc_lk_pre, NULL}, /* Lkx */ {mdoc_mt_pre, NULL}, /* Mt */ {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ {mdoc_quote_pre, mdoc_quote_post}, /* Bro */ @@ -245,6 +247,7 @@ static const struct htmlmdoc mdocs[MDOC_ {mdoc__x_pre, mdoc__x_post}, /* %U */ {NULL, NULL}, /* Ta */ {mdoc_skip_pre, NULL}, /* ll */ + {mdoc_ix_pre, NULL}, /* Ix */ }; static const char * const lists[LIST_MAX] = { @@ -538,6 +541,25 @@ mdoc_root_pre(MDOC_ARGS) } static int +mdoc_ix_pre(MDOC_ARGS) +{ + struct htmlpair tag; + + bufinit(h); + bufcat(h, "index-"); + + for (n = n->child; n && MDOC_TEXT == n->type; ) { + bufcat_id(h, n->string); + if (NULL != (n = n->next)) + bufcat_id(h, " "); + } + + PAIR_ID_INIT(&tag, h->buf); + print_otag(h, TAG_A, 1, &tag); + return(0); +} + +static int mdoc_sh_pre(MDOC_ARGS) { struct htmlpair tag; @@ -556,7 +578,7 @@ mdoc_sh_pre(MDOC_ARGS) } bufinit(h); - bufcat(h, "x"); + bufcat(h, "index-"); for (n = n->child; n && MDOC_TEXT == n->type; ) { bufcat_id(h, n->string); @@ -586,7 +608,7 @@ mdoc_ss_pre(MDOC_ARGS) return(1); bufinit(h); - bufcat(h, "x"); + bufcat(h, "index-"); for (n = n->child; n && MDOC_TEXT == n->type; ) { bufcat_id(h, n->string); @@ -696,33 +718,56 @@ static int mdoc_xr_pre(MDOC_ARGS) { struct htmlpair tag[2]; + const char *name, *sec, *index, *desc; + struct tag *t; - if (NULL == n->child) + if (NULL == (n = n->child)) return(0); + name = n->string; + sec = desc = index = NULL; + if (NULL != n->next) { + sec = n->next->string; + if (NULL != n->next->next) { + index = desc = n->next->next->string; + if (NULL != n->next->next->next) + desc = n->next->next->next->string; + } + } + PAIR_CLASS_INIT(&tag[0], "link-man"); if (h->base_man) { - buffmt_man(h, n->child->string, - n->child->next ? - n->child->next->string : NULL); + buffmt_man(h, name, sec, NULL); PAIR_HREF_INIT(&tag[1], h->buf); - print_otag(h, TAG_A, 2, tag); + t = print_otag(h, TAG_A, 2, tag); } else - print_otag(h, TAG_A, 1, tag); + t = print_otag(h, TAG_A, 1, tag); - n = n->child; - print_text(h, n->string); + print_text(h, name); - if (NULL == (n = n->next)) - return(0); + if (NULL != index) { + h->flags |= HTML_NOSPACE; + print_text(h, "("); + h->flags |= HTML_NOSPACE; + print_text(h, index); + h->flags |= HTML_NOSPACE; + print_text(h, ")"); + } + + if (NULL != desc) { + print_tagq(h, t); + h->flags |= HTML_NOSPACE; + print_text(h, ", section"); + if (h->base_man) { + buffmt_man(h, name, sec, index); + PAIR_HREF_INIT(&tag[1], h->buf); + print_otag(h, TAG_A, 2, tag); + } else + print_otag(h, TAG_A, 1, tag); + print_text(h, desc); + } - h->flags |= HTML_NOSPACE; - print_text(h, "("); - h->flags |= HTML_NOSPACE; - print_text(h, n->string); - h->flags |= HTML_NOSPACE; - print_text(h, ")"); return(0); } @@ -1104,7 +1149,7 @@ mdoc_sx_pre(MDOC_ARGS) struct htmlpair tag[2]; bufinit(h); - bufcat(h, "#x"); + bufcat(h, "#index-"); for (n = n->child; n; ) { bufcat_id(h, n->string); @@ -1592,14 +1637,23 @@ static int mdoc_lk_pre(MDOC_ARGS) { struct htmlpair tag[2]; + enum mdoct type = n->tok; if (NULL == (n = n->child)) return(0); assert(MDOC_TEXT == n->type); - PAIR_CLASS_INIT(&tag[0], "link-ext"); - PAIR_HREF_INIT(&tag[1], n->string); + if (MDOC_Lkx == type) { + PAIR_CLASS_INIT(&tag[0], "link-int"); + bufinit(h); + bufcat(h, "#index-"); + bufcat_id(h, n->string); + PAIR_HREF_INIT(&tag[1], h->buf); + } else { + PAIR_CLASS_INIT(&tag[0], "link-ext"); + PAIR_HREF_INIT(&tag[1], n->string); + } print_otag(h, TAG_A, 2, tag); Index: mdoc_macro.c =================================================================== RCS file: /home/cvs/mdocml/mdocml/mdoc_macro.c,v retrieving revision 1.183 diff -u -p -r1.183 mdoc_macro.c --- mdoc_macro.c 12 Feb 2015 12:24:33 -0000 1.183 +++ mdoc_macro.c 12 Mar 2015 02:49:10 -0000 @@ -180,6 +180,7 @@ const struct mdoc_macro __mdoc_macros[MD { in_line, 0 }, /* Lb */ { in_line_eoln, 0 }, /* Lp */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lk */ + { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lkx */ { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Mt */ { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Brq */ { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | @@ -196,6 +197,7 @@ const struct mdoc_macro __mdoc_macros[MD { in_line_eoln, 0 }, /* %U */ { phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */ { in_line_eoln, MDOC_PROLOGUE }, /* ll */ + { in_line_eoln, MDOC_JOIN }, /* Ix */ }; const struct mdoc_macro * const mdoc_macros = __mdoc_macros; @@ -1288,9 +1290,10 @@ in_line_argn(MACRO_PROT_ARGS) case MDOC_Bx: /* FALLTHROUGH */ case MDOC_Es: - /* FALLTHROUGH */ - case MDOC_Xr: maxargs = 2; + break; + case MDOC_Xr: + maxargs = 4; break; default: maxargs = 1; Index: mdoc_term.c =================================================================== RCS file: /home/cvs/mdocml/mdocml/mdoc_term.c,v retrieving revision 1.313 diff -u -p -r1.313 mdoc_term.c --- mdoc_term.c 6 Mar 2015 15:48:52 -0000 1.313 +++ mdoc_term.c 12 Mar 2015 02:49:11 -0000 @@ -232,6 +232,7 @@ static const struct termact termacts[MDO { NULL, termp_lb_post }, /* Lb */ { termp_sp_pre, NULL }, /* Lp */ { termp_lk_pre, NULL }, /* Lk */ + { termp_lk_pre, NULL }, /* Lkx */ { termp_under_pre, NULL }, /* Mt */ { termp_quote_pre, termp_quote_post }, /* Brq */ { termp_quote_pre, termp_quote_post }, /* Bro */ @@ -246,6 +247,7 @@ static const struct termact termacts[MDO { NULL, termp____post }, /* %U */ { NULL, NULL }, /* Ta */ { termp_ll_pre, NULL }, /* ll */ + { termp_skip_pre, NULL }, /* Ix */ }; @@ -1257,6 +1259,16 @@ termp_xr_pre(DECL_ARGS) p->flags |= TERMP_NOSPACE; term_word(p, ")"); + if (NULL == (n = n->next)) + return(0); + + p->flags |= TERMP_NOSPACE; + term_word(p, " section"); + if (NULL != n->next) + term_word(p, n->next->string); + else + term_word(p, n->string); + return(0); } @@ -2174,6 +2186,9 @@ termp_lk_pre(DECL_ARGS) if (NULL == (link = n->child)) return(0); + if (NULL != link->next) + link = link->next; + if (NULL != (descr = link->next)) { term_fontpush(p, TERMFONT_UNDER); while (NULL != descr) { @@ -2188,7 +2203,6 @@ termp_lk_pre(DECL_ARGS) term_fontpush(p, TERMFONT_BOLD); term_word(p, link->string); term_fontpop(p); - return(0); }