]>
git.cameronkatri.com Git - mandoc.git/blob - tag.c
1 /* $Id: tag.c,v 1.36 2020/04/19 16:36:16 schwarze Exp $ */
3 * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
17 * Functions to tag syntax tree nodes.
18 * For internal use by mandoc(1) validation modules only.
22 #include <sys/types.h>
31 #include "mandoc_aux.h"
32 #include "mandoc_ohash.h"
39 struct roff_node
**nodes
;
46 static void tag_move_href(struct roff_man
*,
47 struct roff_node
*, const char *);
48 static void tag_move_id(struct roff_node
*);
50 static struct ohash tag_data
;
54 * Set up the ohash table to collect nodes
55 * where various marked-up terms are documented.
60 mandoc_ohash_init(&tag_data
, 4, offsetof(struct tag_entry
, s
));
66 struct tag_entry
*entry
;
69 if (tag_data
.info
.free
== NULL
)
71 entry
= ohash_first(&tag_data
, &slot
);
72 while (entry
!= NULL
) {
75 entry
= ohash_next(&tag_data
, &slot
);
77 ohash_delete(&tag_data
);
78 tag_data
.info
.free
= NULL
;
82 * Set a node where a term is defined,
83 * unless it is already defined at a lower priority.
86 tag_put(const char *s
, int prio
, struct roff_node
*n
)
88 struct tag_entry
*entry
;
89 struct roff_node
*nold
;
94 assert(prio
<= TAG_FALLBACK
);
97 if (n
->child
== NULL
|| n
->child
->type
!= ROFFT_TEXT
)
121 * Skip whitespace and escapes and whatever follows,
122 * and if there is any, downgrade the priority.
125 len
= strcspn(s
, " \t\\");
130 if (*se
!= '\0' && prio
< TAG_WEAK
)
133 slot
= ohash_qlookupi(&tag_data
, s
, &se
);
134 entry
= ohash_find(&tag_data
, slot
);
136 /* Build a new entry. */
139 entry
= mandoc_malloc(sizeof(*entry
) + len
+ 1);
140 memcpy(entry
->s
, s
, len
);
141 entry
->s
[len
] = '\0';
143 entry
->maxnodes
= entry
->nnodes
= 0;
144 ohash_insert(&tag_data
, slot
, entry
);
148 * Lower priority numbers take precedence.
149 * If a better entry is already present, ignore the new one.
152 else if (entry
->prio
< prio
)
156 * If the existing entry is worse, clear it.
157 * In addition, a tag with priority TAG_FALLBACK
158 * is only used if the tag occurs exactly once.
161 else if (entry
->prio
> prio
|| prio
== TAG_FALLBACK
) {
162 while (entry
->nnodes
> 0) {
163 nold
= entry
->nodes
[--entry
->nnodes
];
164 nold
->flags
&= ~NODE_ID
;
168 if (prio
== TAG_FALLBACK
) {
169 entry
->prio
= TAG_DELETE
;
174 /* Remember the new node. */
176 if (entry
->maxnodes
== entry
->nnodes
) {
177 entry
->maxnodes
+= 4;
178 entry
->nodes
= mandoc_reallocarray(entry
->nodes
,
179 entry
->maxnodes
, sizeof(*entry
->nodes
));
181 entry
->nodes
[entry
->nnodes
++] = n
;
184 if (n
->child
== NULL
|| n
->child
->string
!= s
|| *se
!= '\0') {
185 assert(n
->tag
== NULL
);
186 n
->tag
= mandoc_strndup(s
, len
);
191 tag_exists(const char *tag
)
193 return ohash_find(&tag_data
, ohash_qlookup(&tag_data
, tag
)) != NULL
;
197 * For in-line elements, move the link target
198 * to the enclosing paragraph when appropriate.
201 tag_move_id(struct roff_node
*n
)
203 struct roff_node
*np
;
207 if (np
->prev
!= NULL
)
209 else if ((np
= np
->parent
) == NULL
)
213 switch (np
->parent
->parent
->norm
->Bl
.type
) {
215 /* Target the ROFFT_BLOCK = <tr>. */
223 /* Target the ROFFT_HEAD = <dt>. */
224 np
= np
->parent
->head
;
227 /* Target the ROFF_BODY = <li>. */
231 case MDOC_Pp
: /* Target the ROFFT_ELEM = <p>. */
232 if (np
->tag
== NULL
) {
233 np
->tag
= mandoc_strdup(n
->tag
== NULL
?
234 n
->child
->string
: n
->tag
);
235 np
->flags
|= NODE_ID
;
236 n
->flags
&= ~NODE_ID
;
246 /* Do not move past major blocks. */
250 * Move past in-line content and partial
251 * blocks, for example .It Xo or .It Bq Er.
259 * When a paragraph is tagged and starts with text,
260 * move the permalink to the first few words.
263 tag_move_href(struct roff_man
*man
, struct roff_node
*n
, const char *tag
)
267 if (n
== NULL
|| n
->type
!= ROFFT_TEXT
||
268 *n
->string
== '\0' || *n
->string
== ' ')
272 while (cp
!= NULL
&& cp
- n
->string
< 5)
273 cp
= strchr(cp
+ 1, ' ');
275 /* If the first text node is longer, split it. */
277 if (cp
!= NULL
&& cp
[1] != '\0') {
279 man
->next
= ROFF_NEXT_SIBLING
;
280 roff_word_alloc(man
, n
->line
,
281 n
->pos
+ (cp
- n
->string
), cp
+ 1);
282 man
->last
->flags
= n
->flags
& ~NODE_LINE
;
286 assert(n
->tag
== NULL
);
287 n
->tag
= mandoc_strdup(tag
);
288 n
->flags
|= NODE_HREF
;
292 * When all tags have been set, decide where to put
293 * the associated permalinks, and maybe move some tags
294 * to the beginning of the respective paragraphs.
297 tag_postprocess(struct roff_man
*man
, struct roff_node
*n
)
299 if (n
->flags
& NODE_ID
) {
302 tag_move_href(man
, n
->next
, n
->tag
);
307 tag_move_href(man
, n
->child
, n
->tag
);
310 /* XXX No permalink for now. */
313 if (n
->type
== ROFFT_ELEM
|| n
->tok
== MDOC_Fo
)
315 if (n
->tok
!= MDOC_Tg
)
316 n
->flags
|= NODE_HREF
;
317 else if ((n
->flags
& NODE_ID
) == 0) {
318 n
->flags
|= NODE_NOPRT
;
325 for (n
= n
->child
; n
!= NULL
; n
= n
->next
)
326 tag_postprocess(man
, n
);