Annotation of mandoc/tag.c, Revision 1.33
1.33 ! schwarze 1: /* $Id: tag.c,v 1.32 2020/04/03 10:30:09 schwarze Exp $ */
1.1 schwarze 2: /*
1.26 schwarze 3: * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org>
1.1 schwarze 4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1.28 schwarze 16: *
17: * Functions to tag syntax tree nodes.
18: * For internal use by mandoc(1) validation modules only.
1.1 schwarze 19: */
1.7 schwarze 20: #include "config.h"
21:
1.1 schwarze 22: #include <sys/types.h>
23:
1.27 schwarze 24: #include <assert.h>
1.20 schwarze 25: #include <limits.h>
1.1 schwarze 26: #include <stddef.h>
1.32 schwarze 27: #include <stdint.h>
1.1 schwarze 28: #include <stdlib.h>
29: #include <string.h>
30:
31: #include "mandoc_aux.h"
1.10 schwarze 32: #include "mandoc_ohash.h"
1.28 schwarze 33: #include "roff.h"
1.33 ! schwarze 34: #include "mdoc.h"
1.1 schwarze 35: #include "tag.h"
36:
37: struct tag_entry {
1.28 schwarze 38: struct roff_node **nodes;
39: size_t maxnodes;
40: size_t nnodes;
1.4 schwarze 41: int prio;
1.1 schwarze 42: char s[];
43: };
44:
1.33 ! schwarze 45: static void tag_move_id(struct roff_node *);
! 46:
1.1 schwarze 47: static struct ohash tag_data;
48:
49:
50: /*
1.28 schwarze 51: * Set up the ohash table to collect nodes
52: * where various marked-up terms are documented.
1.1 schwarze 53: */
1.28 schwarze 54: void
55: tag_alloc(void)
1.1 schwarze 56: {
1.28 schwarze 57: mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
58: }
1.1 schwarze 59:
1.28 schwarze 60: void
61: tag_free(void)
62: {
63: struct tag_entry *entry;
64: unsigned int slot;
1.12 schwarze 65:
1.29 schwarze 66: if (tag_data.info.free == NULL)
67: return;
1.28 schwarze 68: entry = ohash_first(&tag_data, &slot);
69: while (entry != NULL) {
70: free(entry->nodes);
71: free(entry);
72: entry = ohash_next(&tag_data, &slot);
1.22 schwarze 73: }
1.28 schwarze 74: ohash_delete(&tag_data);
1.29 schwarze 75: tag_data.info.free = NULL;
1.1 schwarze 76: }
77:
78: /*
1.28 schwarze 79: * Set a node where a term is defined,
1.20 schwarze 80: * unless it is already defined at a lower priority.
1.1 schwarze 81: */
82: void
1.28 schwarze 83: tag_put(const char *s, int prio, struct roff_node *n)
1.1 schwarze 84: {
85: struct tag_entry *entry;
1.20 schwarze 86: const char *se;
1.5 schwarze 87: size_t len;
1.1 schwarze 88: unsigned int slot;
89:
1.27 schwarze 90: assert(prio <= TAG_FALLBACK);
1.20 schwarze 91:
1.28 schwarze 92: if (s == NULL) {
93: if (n->child == NULL || n->child->type != ROFFT_TEXT)
94: return;
95: s = n->child->string;
1.30 schwarze 96: switch (s[0]) {
97: case '-':
98: s++;
99: break;
100: case '\\':
101: switch (s[1]) {
102: case '&':
103: case '-':
104: case 'e':
105: s += 2;
106: break;
107: default:
108: break;
109: }
110: break;
111: default:
112: break;
113: }
1.28 schwarze 114: }
1.20 schwarze 115:
116: /*
1.24 schwarze 117: * Skip whitespace and escapes and whatever follows,
1.20 schwarze 118: * and if there is any, downgrade the priority.
119: */
120:
1.24 schwarze 121: len = strcspn(s, " \t\\");
1.20 schwarze 122: if (len == 0)
1.1 schwarze 123: return;
1.14 schwarze 124:
1.20 schwarze 125: se = s + len;
1.27 schwarze 126: if (*se != '\0' && prio < TAG_WEAK)
127: prio = TAG_WEAK;
1.20 schwarze 128:
129: slot = ohash_qlookupi(&tag_data, s, &se);
1.1 schwarze 130: entry = ohash_find(&tag_data, slot);
1.14 schwarze 131:
1.28 schwarze 132: /* Build a new entry. */
133:
1.1 schwarze 134: if (entry == NULL) {
1.20 schwarze 135: entry = mandoc_malloc(sizeof(*entry) + len + 1);
1.1 schwarze 136: memcpy(entry->s, s, len);
1.20 schwarze 137: entry->s[len] = '\0';
1.28 schwarze 138: entry->nodes = NULL;
139: entry->maxnodes = entry->nnodes = 0;
1.1 schwarze 140: ohash_insert(&tag_data, slot, entry);
1.28 schwarze 141: }
1.14 schwarze 142:
1.28 schwarze 143: /*
144: * Lower priority numbers take precedence.
145: * If a better entry is already present, ignore the new one.
146: */
147:
148: else if (entry->prio < prio)
149: return;
150:
151: /*
152: * If the existing entry is worse, clear it.
153: * In addition, a tag with priority TAG_FALLBACK
154: * is only used if the tag occurs exactly once.
155: */
1.14 schwarze 156:
1.28 schwarze 157: else if (entry->prio > prio || prio == TAG_FALLBACK) {
158: while (entry->nnodes > 0)
159: entry->nodes[--entry->nnodes]->flags &= ~NODE_ID;
1.16 schwarze 160:
1.27 schwarze 161: if (prio == TAG_FALLBACK) {
1.28 schwarze 162: entry->prio = TAG_DELETE;
1.16 schwarze 163: return;
164: }
1.14 schwarze 165: }
166:
1.28 schwarze 167: /* Remember the new node. */
1.14 schwarze 168:
1.28 schwarze 169: if (entry->maxnodes == entry->nnodes) {
170: entry->maxnodes += 4;
171: entry->nodes = mandoc_reallocarray(entry->nodes,
172: entry->maxnodes, sizeof(*entry->nodes));
1.14 schwarze 173: }
1.28 schwarze 174: entry->nodes[entry->nnodes++] = n;
1.4 schwarze 175: entry->prio = prio;
1.28 schwarze 176: n->flags |= NODE_ID;
177: if (n->child == NULL || n->child->string != s || *se != '\0') {
178: assert(n->string == NULL);
179: n->string = mandoc_strndup(s, len);
180: }
1.1 schwarze 181: }
182:
1.31 schwarze 183: int
184: tag_exists(const char *tag)
1.1 schwarze 185: {
1.31 schwarze 186: return ohash_find(&tag_data, ohash_qlookup(&tag_data, tag)) != NULL;
1.33 ! schwarze 187: }
! 188:
! 189: /*
! 190: * For in-line elements, move the link target
! 191: * to the enclosing paragraph when appropriate.
! 192: */
! 193: static void
! 194: tag_move_id(struct roff_node *n)
! 195: {
! 196: struct roff_node *np;
! 197:
! 198: np = n;
! 199: for (;;) {
! 200: if (np->prev != NULL)
! 201: np = np->prev;
! 202: else if ((np = np->parent) == NULL)
! 203: return;
! 204: switch (np->tok) {
! 205: case MDOC_It:
! 206: switch (np->parent->parent->norm->Bl.type) {
! 207: case LIST_column:
! 208: /* Target the ROFFT_BLOCK = <tr>. */
! 209: np = np->parent;
! 210: break;
! 211: case LIST_diag:
! 212: case LIST_hang:
! 213: case LIST_inset:
! 214: case LIST_ohang:
! 215: case LIST_tag:
! 216: /* Target the ROFFT_HEAD = <dt>. */
! 217: np = np->parent->head;
! 218: break;
! 219: default:
! 220: /* Target the ROFF_BODY = <li>. */
! 221: break;
! 222: }
! 223: /* FALLTHROUGH */
! 224: case MDOC_Pp: /* Target the ROFFT_ELEM = <p>. */
! 225: if (np->string == NULL) {
! 226: np->string = mandoc_strdup(n->string == NULL ?
! 227: n->child->string : n->string);
! 228: np->flags |= NODE_ID;
! 229: n->flags &= ~NODE_ID;
! 230: }
! 231: return;
! 232: case MDOC_Sh:
! 233: case MDOC_Ss:
! 234: case MDOC_Bd:
! 235: case MDOC_Bl:
! 236: case MDOC_D1:
! 237: case MDOC_Dl:
! 238: case MDOC_Rs:
! 239: /* Do not move past major blocks. */
! 240: return;
! 241: default:
! 242: /*
! 243: * Move past in-line content and partial
! 244: * blocks, for example .It Xo or .It Bq Er.
! 245: */
! 246: break;
! 247: }
! 248: }
! 249: }
! 250:
! 251: /*
! 252: * When all tags have been set, decide where to put
! 253: * the associated permalinks, and maybe move some tags
! 254: * to the beginning of the respective paragraphs.
! 255: */
! 256: void
! 257: tag_postprocess(struct roff_node *n)
! 258: {
! 259: if (n->flags & NODE_ID) {
! 260: switch (n->tok) {
! 261: case MDOC_Bd:
! 262: case MDOC_Bl:
! 263: case MDOC_Pp:
! 264: /* XXX No permalink for now. */
! 265: break;
! 266: default:
! 267: if (n->type == ROFFT_ELEM || n->tok == MDOC_Fo)
! 268: tag_move_id(n);
! 269: if (n->tok != MDOC_Tg)
! 270: n->flags |= NODE_HREF;
! 271: else if ((n->flags & NODE_ID) == 0)
! 272: n->flags |= NODE_NOPRT;
! 273: break;
! 274: }
! 275: }
! 276: for (n = n->child; n != NULL; n = n->next)
! 277: tag_postprocess(n);
1.1 schwarze 278: }
CVSweb