Annotation of src/usr.bin/mandoc/tag.c, Revision 1.31
1.31 ! schwarze 1: /* $OpenBSD: tag.c,v 1.30 2020/03/21 00:17:01 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: */
20: #include <sys/types.h>
21:
1.27 schwarze 22: #include <assert.h>
1.20 schwarze 23: #include <limits.h>
1.1 schwarze 24: #include <stddef.h>
25: #include <stdlib.h>
26: #include <string.h>
27:
28: #include "mandoc_aux.h"
1.10 schwarze 29: #include "mandoc_ohash.h"
1.28 schwarze 30: #include "roff.h"
1.1 schwarze 31: #include "tag.h"
32:
33: struct tag_entry {
1.28 schwarze 34: struct roff_node **nodes;
35: size_t maxnodes;
36: size_t nnodes;
1.4 schwarze 37: int prio;
1.1 schwarze 38: char s[];
39: };
40:
41: static struct ohash tag_data;
42:
43:
44: /*
1.28 schwarze 45: * Set up the ohash table to collect nodes
46: * where various marked-up terms are documented.
1.1 schwarze 47: */
1.28 schwarze 48: void
49: tag_alloc(void)
1.1 schwarze 50: {
1.28 schwarze 51: mandoc_ohash_init(&tag_data, 4, offsetof(struct tag_entry, s));
52: }
1.1 schwarze 53:
1.28 schwarze 54: void
55: tag_free(void)
56: {
57: struct tag_entry *entry;
58: unsigned int slot;
1.12 schwarze 59:
1.29 schwarze 60: if (tag_data.info.free == NULL)
61: return;
1.28 schwarze 62: entry = ohash_first(&tag_data, &slot);
63: while (entry != NULL) {
64: free(entry->nodes);
65: free(entry);
66: entry = ohash_next(&tag_data, &slot);
1.22 schwarze 67: }
1.28 schwarze 68: ohash_delete(&tag_data);
1.29 schwarze 69: tag_data.info.free = NULL;
1.1 schwarze 70: }
71:
72: /*
1.28 schwarze 73: * Set a node where a term is defined,
1.20 schwarze 74: * unless it is already defined at a lower priority.
1.1 schwarze 75: */
76: void
1.28 schwarze 77: tag_put(const char *s, int prio, struct roff_node *n)
1.1 schwarze 78: {
79: struct tag_entry *entry;
1.20 schwarze 80: const char *se;
1.5 schwarze 81: size_t len;
1.1 schwarze 82: unsigned int slot;
83:
1.27 schwarze 84: assert(prio <= TAG_FALLBACK);
1.20 schwarze 85:
1.28 schwarze 86: if (s == NULL) {
87: if (n->child == NULL || n->child->type != ROFFT_TEXT)
88: return;
89: s = n->child->string;
1.30 schwarze 90: switch (s[0]) {
91: case '-':
92: s++;
93: break;
94: case '\\':
95: switch (s[1]) {
96: case '&':
97: case '-':
98: case 'e':
99: s += 2;
100: break;
101: default:
102: break;
103: }
104: break;
105: default:
106: break;
107: }
1.28 schwarze 108: }
1.20 schwarze 109:
110: /*
1.24 schwarze 111: * Skip whitespace and escapes and whatever follows,
1.20 schwarze 112: * and if there is any, downgrade the priority.
113: */
114:
1.24 schwarze 115: len = strcspn(s, " \t\\");
1.20 schwarze 116: if (len == 0)
1.1 schwarze 117: return;
1.14 schwarze 118:
1.20 schwarze 119: se = s + len;
1.27 schwarze 120: if (*se != '\0' && prio < TAG_WEAK)
121: prio = TAG_WEAK;
1.20 schwarze 122:
123: slot = ohash_qlookupi(&tag_data, s, &se);
1.1 schwarze 124: entry = ohash_find(&tag_data, slot);
1.14 schwarze 125:
1.28 schwarze 126: /* Build a new entry. */
127:
1.1 schwarze 128: if (entry == NULL) {
1.20 schwarze 129: entry = mandoc_malloc(sizeof(*entry) + len + 1);
1.1 schwarze 130: memcpy(entry->s, s, len);
1.20 schwarze 131: entry->s[len] = '\0';
1.28 schwarze 132: entry->nodes = NULL;
133: entry->maxnodes = entry->nnodes = 0;
1.1 schwarze 134: ohash_insert(&tag_data, slot, entry);
1.28 schwarze 135: }
1.14 schwarze 136:
1.28 schwarze 137: /*
138: * Lower priority numbers take precedence.
139: * If a better entry is already present, ignore the new one.
140: */
141:
142: else if (entry->prio < prio)
143: return;
144:
145: /*
146: * If the existing entry is worse, clear it.
147: * In addition, a tag with priority TAG_FALLBACK
148: * is only used if the tag occurs exactly once.
149: */
1.14 schwarze 150:
1.28 schwarze 151: else if (entry->prio > prio || prio == TAG_FALLBACK) {
152: while (entry->nnodes > 0)
153: entry->nodes[--entry->nnodes]->flags &= ~NODE_ID;
1.16 schwarze 154:
1.27 schwarze 155: if (prio == TAG_FALLBACK) {
1.28 schwarze 156: entry->prio = TAG_DELETE;
1.16 schwarze 157: return;
158: }
1.14 schwarze 159: }
160:
1.28 schwarze 161: /* Remember the new node. */
1.14 schwarze 162:
1.28 schwarze 163: if (entry->maxnodes == entry->nnodes) {
164: entry->maxnodes += 4;
165: entry->nodes = mandoc_reallocarray(entry->nodes,
166: entry->maxnodes, sizeof(*entry->nodes));
1.14 schwarze 167: }
1.28 schwarze 168: entry->nodes[entry->nnodes++] = n;
1.4 schwarze 169: entry->prio = prio;
1.28 schwarze 170: n->flags |= NODE_ID;
171: if (n->child == NULL || n->child->string != s || *se != '\0') {
172: assert(n->string == NULL);
173: n->string = mandoc_strndup(s, len);
174: }
1.1 schwarze 175: }
176:
1.31 ! schwarze 177: int
! 178: tag_exists(const char *tag)
1.1 schwarze 179: {
1.31 ! schwarze 180: return ohash_find(&tag_data, ohash_qlookup(&tag_data, tag)) != NULL;
1.1 schwarze 181: }