=================================================================== RCS file: /cvs/mandoc/tag.c,v retrieving revision 1.33 retrieving revision 1.37 diff -u -p -r1.33 -r1.37 --- mandoc/tag.c 2020/04/07 22:56:02 1.33 +++ mandoc/tag.c 2022/04/26 11:38:38 1.37 @@ -1,6 +1,7 @@ -/* $Id: tag.c,v 1.33 2020/04/07 22:56:02 schwarze Exp $ */ +/* $Id: tag.c,v 1.37 2022/04/26 11:38:38 schwarze Exp $ */ /* - * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze + * Copyright (c) 2015, 2016, 2018, 2019, 2020, 2022 + * Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -32,6 +33,7 @@ #include "mandoc_ohash.h" #include "roff.h" #include "mdoc.h" +#include "roff_int.h" #include "tag.h" struct tag_entry { @@ -42,6 +44,8 @@ struct tag_entry { char s[]; }; +static void tag_move_href(struct roff_man *, + struct roff_node *, const char *); static void tag_move_id(struct roff_node *); static struct ohash tag_data; @@ -77,18 +81,31 @@ tag_free(void) /* * Set a node where a term is defined, - * unless it is already defined at a lower priority. + * unless the term is already defined at a lower priority. */ void tag_put(const char *s, int prio, struct roff_node *n) { struct tag_entry *entry; + struct roff_node *nold; const char *se; size_t len; unsigned int slot; assert(prio <= TAG_FALLBACK); + /* + * If the node is already tagged, the existing tag is + * explicit and we are now about to add an implicit tag. + * Don't do that; just skip implicit tagging if the author + * specified an explicit tag. + */ + + if (n->flags & NODE_ID) + return; + + /* Determine the implicit tag. */ + if (s == NULL) { if (n->child == NULL || n->child->type != ROFFT_TEXT) return; @@ -146,7 +163,7 @@ tag_put(const char *s, int prio, struct roff_node *n) */ else if (entry->prio < prio) - return; + return; /* * If the existing entry is worse, clear it. @@ -155,9 +172,12 @@ tag_put(const char *s, int prio, struct roff_node *n) */ else if (entry->prio > prio || prio == TAG_FALLBACK) { - while (entry->nnodes > 0) - entry->nodes[--entry->nnodes]->flags &= ~NODE_ID; - + while (entry->nnodes > 0) { + nold = entry->nodes[--entry->nnodes]; + nold->flags &= ~NODE_ID; + free(nold->tag); + nold->tag = NULL; + } if (prio == TAG_FALLBACK) { entry->prio = TAG_DELETE; return; @@ -175,8 +195,8 @@ tag_put(const char *s, int prio, struct roff_node *n) entry->prio = prio; n->flags |= NODE_ID; if (n->child == NULL || n->child->string != s || *se != '\0') { - assert(n->string == NULL); - n->string = mandoc_strndup(s, len); + assert(n->tag == NULL); + n->tag = mandoc_strndup(s, len); } } @@ -222,9 +242,9 @@ tag_move_id(struct roff_node *n) } /* FALLTHROUGH */ case MDOC_Pp: /* Target the ROFFT_ELEM =

. */ - if (np->string == NULL) { - np->string = mandoc_strdup(n->string == NULL ? - n->child->string : n->string); + if (np->tag == NULL) { + np->tag = mandoc_strdup(n->tag == NULL ? + n->child->string : n->tag); np->flags |= NODE_ID; n->flags &= ~NODE_ID; } @@ -249,18 +269,57 @@ tag_move_id(struct roff_node *n) } /* + * When a paragraph is tagged and starts with text, + * move the permalink to the first few words. + */ +static void +tag_move_href(struct roff_man *man, struct roff_node *n, const char *tag) +{ + char *cp; + + if (n == NULL || n->type != ROFFT_TEXT || + *n->string == '\0' || *n->string == ' ') + return; + + cp = n->string; + while (cp != NULL && cp - n->string < 5) + cp = strchr(cp + 1, ' '); + + /* If the first text node is longer, split it. */ + + if (cp != NULL && cp[1] != '\0') { + man->last = n; + man->next = ROFF_NEXT_SIBLING; + roff_word_alloc(man, n->line, + n->pos + (cp - n->string), cp + 1); + man->last->flags = n->flags & ~NODE_LINE; + *cp = '\0'; + } + + assert(n->tag == NULL); + n->tag = mandoc_strdup(tag); + n->flags |= NODE_HREF; +} + +/* * When all tags have been set, decide where to put * the associated permalinks, and maybe move some tags * to the beginning of the respective paragraphs. */ void -tag_postprocess(struct roff_node *n) +tag_postprocess(struct roff_man *man, struct roff_node *n) { if (n->flags & NODE_ID) { switch (n->tok) { + case MDOC_Pp: + tag_move_href(man, n->next, n->tag); + break; case MDOC_Bd: + case MDOC_D1: + case MDOC_Dl: + tag_move_href(man, n->child, n->tag); + break; case MDOC_Bl: - case MDOC_Pp: /* XXX No permalink for now. */ break; default: @@ -268,11 +327,14 @@ tag_postprocess(struct roff_node *n) tag_move_id(n); if (n->tok != MDOC_Tg) n->flags |= NODE_HREF; - else if ((n->flags & NODE_ID) == 0) + else if ((n->flags & NODE_ID) == 0) { n->flags |= NODE_NOPRT; + free(n->tag); + n->tag = NULL; + } break; } } for (n = n->child; n != NULL; n = n->next) - tag_postprocess(n); + tag_postprocess(man, n); }