]>
git.cameronkatri.com Git - mandoc.git/blob - tag.c
1 /* $Id: tag.c,v 1.37 2022/04/26 11:38:38 schwarze Exp $ */
3 * Copyright (c) 2015, 2016, 2018, 2019, 2020, 2022
4 * Ingo Schwarze <schwarze@openbsd.org>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * Functions to tag syntax tree nodes.
19 * For internal use by mandoc(1) validation modules only.
23 #include <sys/types.h>
32 #include "mandoc_aux.h"
33 #include "mandoc_ohash.h"
40 struct roff_node
**nodes
;
47 static void tag_move_href(struct roff_man
*,
48 struct roff_node
*, const char *);
49 static void tag_move_id(struct roff_node
*);
51 static struct ohash tag_data
;
55 * Set up the ohash table to collect nodes
56 * where various marked-up terms are documented.
61 mandoc_ohash_init(&tag_data
, 4, offsetof(struct tag_entry
, s
));
67 struct tag_entry
*entry
;
70 if (tag_data
.info
.free
== NULL
)
72 entry
= ohash_first(&tag_data
, &slot
);
73 while (entry
!= NULL
) {
76 entry
= ohash_next(&tag_data
, &slot
);
78 ohash_delete(&tag_data
);
79 tag_data
.info
.free
= NULL
;
83 * Set a node where a term is defined,
84 * unless the term is already defined at a lower priority.
87 tag_put(const char *s
, int prio
, struct roff_node
*n
)
89 struct tag_entry
*entry
;
90 struct roff_node
*nold
;
95 assert(prio
<= TAG_FALLBACK
);
98 * If the node is already tagged, the existing tag is
99 * explicit and we are now about to add an implicit tag.
100 * Don't do that; just skip implicit tagging if the author
101 * specified an explicit tag.
104 if (n
->flags
& NODE_ID
)
107 /* Determine the implicit tag. */
110 if (n
->child
== NULL
|| n
->child
->type
!= ROFFT_TEXT
)
112 s
= n
->child
->string
;
134 * Skip whitespace and escapes and whatever follows,
135 * and if there is any, downgrade the priority.
138 len
= strcspn(s
, " \t\\");
143 if (*se
!= '\0' && prio
< TAG_WEAK
)
146 slot
= ohash_qlookupi(&tag_data
, s
, &se
);
147 entry
= ohash_find(&tag_data
, slot
);
149 /* Build a new entry. */
152 entry
= mandoc_malloc(sizeof(*entry
) + len
+ 1);
153 memcpy(entry
->s
, s
, len
);
154 entry
->s
[len
] = '\0';
156 entry
->maxnodes
= entry
->nnodes
= 0;
157 ohash_insert(&tag_data
, slot
, entry
);
161 * Lower priority numbers take precedence.
162 * If a better entry is already present, ignore the new one.
165 else if (entry
->prio
< prio
)
169 * If the existing entry is worse, clear it.
170 * In addition, a tag with priority TAG_FALLBACK
171 * is only used if the tag occurs exactly once.
174 else if (entry
->prio
> prio
|| prio
== TAG_FALLBACK
) {
175 while (entry
->nnodes
> 0) {
176 nold
= entry
->nodes
[--entry
->nnodes
];
177 nold
->flags
&= ~NODE_ID
;
181 if (prio
== TAG_FALLBACK
) {
182 entry
->prio
= TAG_DELETE
;
187 /* Remember the new node. */
189 if (entry
->maxnodes
== entry
->nnodes
) {
190 entry
->maxnodes
+= 4;
191 entry
->nodes
= mandoc_reallocarray(entry
->nodes
,
192 entry
->maxnodes
, sizeof(*entry
->nodes
));
194 entry
->nodes
[entry
->nnodes
++] = n
;
197 if (n
->child
== NULL
|| n
->child
->string
!= s
|| *se
!= '\0') {
198 assert(n
->tag
== NULL
);
199 n
->tag
= mandoc_strndup(s
, len
);
204 tag_exists(const char *tag
)
206 return ohash_find(&tag_data
, ohash_qlookup(&tag_data
, tag
)) != NULL
;
210 * For in-line elements, move the link target
211 * to the enclosing paragraph when appropriate.
214 tag_move_id(struct roff_node
*n
)
216 struct roff_node
*np
;
220 if (np
->prev
!= NULL
)
222 else if ((np
= np
->parent
) == NULL
)
226 switch (np
->parent
->parent
->norm
->Bl
.type
) {
228 /* Target the ROFFT_BLOCK = <tr>. */
236 /* Target the ROFFT_HEAD = <dt>. */
237 np
= np
->parent
->head
;
240 /* Target the ROFF_BODY = <li>. */
244 case MDOC_Pp
: /* Target the ROFFT_ELEM = <p>. */
245 if (np
->tag
== NULL
) {
246 np
->tag
= mandoc_strdup(n
->tag
== NULL
?
247 n
->child
->string
: n
->tag
);
248 np
->flags
|= NODE_ID
;
249 n
->flags
&= ~NODE_ID
;
259 /* Do not move past major blocks. */
263 * Move past in-line content and partial
264 * blocks, for example .It Xo or .It Bq Er.
272 * When a paragraph is tagged and starts with text,
273 * move the permalink to the first few words.
276 tag_move_href(struct roff_man
*man
, struct roff_node
*n
, const char *tag
)
280 if (n
== NULL
|| n
->type
!= ROFFT_TEXT
||
281 *n
->string
== '\0' || *n
->string
== ' ')
285 while (cp
!= NULL
&& cp
- n
->string
< 5)
286 cp
= strchr(cp
+ 1, ' ');
288 /* If the first text node is longer, split it. */
290 if (cp
!= NULL
&& cp
[1] != '\0') {
292 man
->next
= ROFF_NEXT_SIBLING
;
293 roff_word_alloc(man
, n
->line
,
294 n
->pos
+ (cp
- n
->string
), cp
+ 1);
295 man
->last
->flags
= n
->flags
& ~NODE_LINE
;
299 assert(n
->tag
== NULL
);
300 n
->tag
= mandoc_strdup(tag
);
301 n
->flags
|= NODE_HREF
;
305 * When all tags have been set, decide where to put
306 * the associated permalinks, and maybe move some tags
307 * to the beginning of the respective paragraphs.
310 tag_postprocess(struct roff_man
*man
, struct roff_node
*n
)
312 if (n
->flags
& NODE_ID
) {
315 tag_move_href(man
, n
->next
, n
->tag
);
320 tag_move_href(man
, n
->child
, n
->tag
);
323 /* XXX No permalink for now. */
326 if (n
->type
== ROFFT_ELEM
|| n
->tok
== MDOC_Fo
)
328 if (n
->tok
!= MDOC_Tg
)
329 n
->flags
|= NODE_HREF
;
330 else if ((n
->flags
& NODE_ID
) == 0) {
331 n
->flags
|= NODE_NOPRT
;
338 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
339 tag_postprocess(man
, n
);