Annotation of mandoc/tag.c, Revision 1.34
1.34 ! schwarze 1: /* $Id: tag.c,v 1.33 2020/04/07 22:56:02 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.34 ! schwarze 86: struct roff_node *nold;
1.20 schwarze 87: const char *se;
1.5 schwarze 88: size_t len;
1.1 schwarze 89: unsigned int slot;
90:
1.27 schwarze 91: assert(prio <= TAG_FALLBACK);
1.20 schwarze 92:
1.28 schwarze 93: if (s == NULL) {
94: if (n->child == NULL || n->child->type != ROFFT_TEXT)
95: return;
96: s = n->child->string;
1.30 schwarze 97: switch (s[0]) {
98: case '-':
99: s++;
100: break;
101: case '\\':
102: switch (s[1]) {
103: case '&':
104: case '-':
105: case 'e':
106: s += 2;
107: break;
108: default:
109: break;
110: }
111: break;
112: default:
113: break;
114: }
1.28 schwarze 115: }
1.20 schwarze 116:
117: /*
1.24 schwarze 118: * Skip whitespace and escapes and whatever follows,
1.20 schwarze 119: * and if there is any, downgrade the priority.
120: */
121:
1.24 schwarze 122: len = strcspn(s, " \t\\");
1.20 schwarze 123: if (len == 0)
1.1 schwarze 124: return;
1.14 schwarze 125:
1.20 schwarze 126: se = s + len;
1.27 schwarze 127: if (*se != '\0' && prio < TAG_WEAK)
128: prio = TAG_WEAK;
1.20 schwarze 129:
130: slot = ohash_qlookupi(&tag_data, s, &se);
1.1 schwarze 131: entry = ohash_find(&tag_data, slot);
1.14 schwarze 132:
1.28 schwarze 133: /* Build a new entry. */
134:
1.1 schwarze 135: if (entry == NULL) {
1.20 schwarze 136: entry = mandoc_malloc(sizeof(*entry) + len + 1);
1.1 schwarze 137: memcpy(entry->s, s, len);
1.20 schwarze 138: entry->s[len] = '\0';
1.28 schwarze 139: entry->nodes = NULL;
140: entry->maxnodes = entry->nnodes = 0;
1.1 schwarze 141: ohash_insert(&tag_data, slot, entry);
1.28 schwarze 142: }
1.14 schwarze 143:
1.28 schwarze 144: /*
145: * Lower priority numbers take precedence.
146: * If a better entry is already present, ignore the new one.
147: */
148:
149: else if (entry->prio < prio)
150: return;
151:
152: /*
153: * If the existing entry is worse, clear it.
154: * In addition, a tag with priority TAG_FALLBACK
155: * is only used if the tag occurs exactly once.
156: */
1.14 schwarze 157:
1.28 schwarze 158: else if (entry->prio > prio || prio == TAG_FALLBACK) {
1.34 ! schwarze 159: while (entry->nnodes > 0) {
! 160: nold = entry->nodes[--entry->nnodes];
! 161: nold->flags &= ~NODE_ID;
! 162: free(nold->tag);
! 163: nold->tag = NULL;
! 164: }
1.27 schwarze 165: if (prio == TAG_FALLBACK) {
1.28 schwarze 166: entry->prio = TAG_DELETE;
1.16 schwarze 167: return;
168: }
1.14 schwarze 169: }
170:
1.28 schwarze 171: /* Remember the new node. */
1.14 schwarze 172:
1.28 schwarze 173: if (entry->maxnodes == entry->nnodes) {
174: entry->maxnodes += 4;
175: entry->nodes = mandoc_reallocarray(entry->nodes,
176: entry->maxnodes, sizeof(*entry->nodes));
1.14 schwarze 177: }
1.28 schwarze 178: entry->nodes[entry->nnodes++] = n;
1.4 schwarze 179: entry->prio = prio;
1.28 schwarze 180: n->flags |= NODE_ID;
181: if (n->child == NULL || n->child->string != s || *se != '\0') {
1.34 ! schwarze 182: assert(n->tag == NULL);
! 183: n->tag = mandoc_strndup(s, len);
1.28 schwarze 184: }
1.1 schwarze 185: }
186:
1.31 schwarze 187: int
188: tag_exists(const char *tag)
1.1 schwarze 189: {
1.31 schwarze 190: return ohash_find(&tag_data, ohash_qlookup(&tag_data, tag)) != NULL;
1.33 schwarze 191: }
192:
193: /*
194: * For in-line elements, move the link target
195: * to the enclosing paragraph when appropriate.
196: */
197: static void
198: tag_move_id(struct roff_node *n)
199: {
200: struct roff_node *np;
201:
202: np = n;
203: for (;;) {
204: if (np->prev != NULL)
205: np = np->prev;
206: else if ((np = np->parent) == NULL)
207: return;
208: switch (np->tok) {
209: case MDOC_It:
210: switch (np->parent->parent->norm->Bl.type) {
211: case LIST_column:
212: /* Target the ROFFT_BLOCK = <tr>. */
213: np = np->parent;
214: break;
215: case LIST_diag:
216: case LIST_hang:
217: case LIST_inset:
218: case LIST_ohang:
219: case LIST_tag:
220: /* Target the ROFFT_HEAD = <dt>. */
221: np = np->parent->head;
222: break;
223: default:
224: /* Target the ROFF_BODY = <li>. */
225: break;
226: }
227: /* FALLTHROUGH */
228: case MDOC_Pp: /* Target the ROFFT_ELEM = <p>. */
1.34 ! schwarze 229: if (np->tag == NULL) {
! 230: np->tag = mandoc_strdup(n->tag == NULL ?
! 231: n->child->string : n->tag);
1.33 schwarze 232: np->flags |= NODE_ID;
233: n->flags &= ~NODE_ID;
234: }
235: return;
236: case MDOC_Sh:
237: case MDOC_Ss:
238: case MDOC_Bd:
239: case MDOC_Bl:
240: case MDOC_D1:
241: case MDOC_Dl:
242: case MDOC_Rs:
243: /* Do not move past major blocks. */
244: return;
245: default:
246: /*
247: * Move past in-line content and partial
248: * blocks, for example .It Xo or .It Bq Er.
249: */
250: break;
251: }
252: }
253: }
254:
255: /*
256: * When all tags have been set, decide where to put
257: * the associated permalinks, and maybe move some tags
258: * to the beginning of the respective paragraphs.
259: */
260: void
261: tag_postprocess(struct roff_node *n)
262: {
263: if (n->flags & NODE_ID) {
264: switch (n->tok) {
265: case MDOC_Bd:
266: case MDOC_Bl:
267: case MDOC_Pp:
268: /* XXX No permalink for now. */
269: break;
270: default:
271: if (n->type == ROFFT_ELEM || n->tok == MDOC_Fo)
272: tag_move_id(n);
273: if (n->tok != MDOC_Tg)
274: n->flags |= NODE_HREF;
1.34 ! schwarze 275: else if ((n->flags & NODE_ID) == 0) {
1.33 schwarze 276: n->flags |= NODE_NOPRT;
1.34 ! schwarze 277: free(n->tag);
! 278: n->tag = NULL;
! 279: }
1.33 schwarze 280: break;
281: }
282: }
283: for (n = n->child; n != NULL; n = n->next)
284: tag_postprocess(n);
1.1 schwarze 285: }
CVSweb