]> git.cameronkatri.com Git - mandoc.git/blob - man.c
Added man validator, renamed mdoc validator.
[mandoc.git] / man.c
1 /* $Id: man.c,v 1.4 2009/03/25 15:17:49 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <assert.h>
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdarg.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "libman.h"
28
29 const char *const __man_macronames[MAN_MAX] = {
30 "\\\"", "TH", "SH", "SS",
31 "TP", "LP", "PP", "P",
32 "IP", "HP", "SM", "SB",
33 "BI", "IB", "BR", "RB",
34 "R", "B", "I", "IR"
35 };
36
37 const char * const *man_macronames = __man_macronames;
38
39 static struct man_node *man_node_alloc(int, int, enum man_type);
40 static int man_node_append(struct man *,
41 struct man_node *);
42 static int man_ptext(struct man *, int, char *);
43 static int man_pmacro(struct man *, int, char *);
44 static void man_free1(struct man *);
45 static void man_alloc1(struct man *);
46
47
48 const struct man_node *
49 man_node(const struct man *m)
50 {
51
52 return(MAN_HALT & m->flags ? NULL : m->first);
53 }
54
55
56 const struct man_meta *
57 man_meta(const struct man *m)
58 {
59
60 return(MAN_HALT & m->flags ? NULL : &m->meta);
61 }
62
63
64 void
65 man_reset(struct man *man)
66 {
67
68 man_free1(man);
69 man_alloc1(man);
70 }
71
72
73 void
74 man_free(struct man *man)
75 {
76
77 man_free1(man);
78
79 if (man->htab)
80 man_hash_free(man->htab);
81 free(man);
82 }
83
84
85 struct man *
86 man_alloc(void *data, const struct man_cb *cb)
87 {
88 struct man *p;
89
90 p = calloc(1, sizeof(struct man));
91 if (NULL == p)
92 err(1, "calloc");
93
94 man_alloc1(p);
95
96 if (cb)
97 (void)memcpy(&p->cb, cb, sizeof(struct man_cb));
98
99 p->htab = man_hash_alloc();
100 p->data = data;
101 return(p);
102 }
103
104
105 int
106 man_endparse(struct man *m)
107 {
108
109 if (MAN_HALT & m->flags)
110 return(0);
111 else if (man_macroend(m))
112 return(1);
113 m->flags |= MAN_HALT;
114 return(0);
115 }
116
117
118 int
119 man_parseln(struct man *m, int ln, char *buf)
120 {
121
122 return('.' == *buf ?
123 man_pmacro(m, ln, buf) :
124 man_ptext(m, ln, buf));
125 }
126
127
128 static void
129 man_free1(struct man *man)
130 {
131
132 if (man->first)
133 man_node_freelist(man->first);
134 if (man->meta.title)
135 free(man->meta.title);
136 if (man->meta.os)
137 free(man->meta.os);
138 if (man->meta.vol)
139 free(man->meta.vol);
140 }
141
142
143 static void
144 man_alloc1(struct man *m)
145 {
146
147 bzero(&m->meta, sizeof(struct man_meta));
148 m->flags = 0;
149 m->last = calloc(1, sizeof(struct man_node));
150 if (NULL == m->last)
151 err(1, "calloc");
152 m->first = m->last;
153 m->last->type = MAN_ROOT;
154 m->next = MAN_NEXT_CHILD;
155 }
156
157
158 static int
159 man_node_append(struct man *man, struct man_node *p)
160 {
161
162 assert(man->last);
163 assert(man->first);
164 assert(MAN_ROOT != p->type);
165
166 switch (man->next) {
167 case (MAN_NEXT_SIBLING):
168 man->last->next = p;
169 p->prev = man->last;
170 p->parent = man->last->parent;
171 break;
172 case (MAN_NEXT_CHILD):
173 man->last->child = p;
174 p->parent = man->last;
175 break;
176 default:
177 abort();
178 /* NOTREACHED */
179 }
180
181 #if 0
182 if ( ! man_action_pre(man, p))
183 return(0);
184 #endif
185
186 man->last = p;
187
188 switch (p->type) {
189 case (MAN_TEXT):
190 if ( ! man_valid_post(man))
191 return(0);
192 #if 0
193 if ( ! man_action_post(man))
194 return(0);
195 #endif
196 break;
197 default:
198 break;
199 }
200
201 return(1);
202 }
203
204
205 static struct man_node *
206 man_node_alloc(int line, int pos, enum man_type type)
207 {
208 struct man_node *p;
209
210 if (NULL == (p = calloc(1, sizeof(struct man_node))))
211 err(1, "malloc");
212 p->line = line;
213 p->pos = pos;
214 p->type = type;
215
216 return(p);
217 }
218
219
220 int
221 man_elem_alloc(struct man *man, int line, int pos, int tok)
222 {
223 struct man_node *p;
224
225 p = man_node_alloc(line, pos, MAN_ELEM);
226 p->tok = tok;
227
228 return(man_node_append(man, p));
229 }
230
231
232 int
233 man_word_alloc(struct man *man,
234 int line, int pos, const char *word)
235 {
236 struct man_node *p;
237
238 p = man_node_alloc(line, pos, MAN_TEXT);
239 if (NULL == (p->string = strdup(word)))
240 err(1, "strdup");
241
242 return(man_node_append(man, p));
243 }
244
245
246 void
247 man_node_free(struct man_node *p)
248 {
249
250 if (p->string)
251 free(p->string);
252 free(p);
253 }
254
255
256 void
257 man_node_freelist(struct man_node *p)
258 {
259
260 if (p->child)
261 man_node_freelist(p->child);
262 if (p->next)
263 man_node_freelist(p->next);
264
265 man_node_free(p);
266 }
267
268
269 static int
270 man_ptext(struct man *m, int line, char *buf)
271 {
272
273 if ( ! man_word_alloc(m, line, 0, buf))
274 return(0);
275 m->next = MAN_NEXT_SIBLING;
276 return(1);
277 }
278
279
280 int
281 man_pmacro(struct man *m, int ln, char *buf)
282 {
283 int i, c;
284 char mac[5];
285
286 /* Comments and empties are quickly ignored. */
287
288 if (0 == buf[1])
289 return(1);
290
291 if (' ' == buf[1]) {
292 i = 2;
293 while (buf[i] && ' ' == buf[i])
294 i++;
295 if (0 == buf[i])
296 return(1);
297 warnx("invalid syntax");
298 return(0);
299 }
300
301 if (buf[1] && '\\' == buf[1])
302 if (buf[2] && '\"' == buf[2])
303 return(1);
304
305 /* Copy the first word into a nil-terminated buffer. */
306
307 for (i = 1; i < 5; i++) {
308 if (0 == (mac[i - 1] = buf[i]))
309 break;
310 else if (' ' == buf[i])
311 break;
312 }
313
314 mac[i - 1] = 0;
315
316 if (i == 5 || i <= 1) {
317 warnx("unknown macro: %s", mac);
318 goto err;
319 }
320
321 if (MAN_MAX == (c = man_hash_find(m->htab, mac))) {
322 warnx("unknown macro: %s", mac);
323 goto err;
324 }
325
326 /* The macro is sane. Jump to the next word. */
327
328 while (buf[i] && ' ' == buf[i])
329 i++;
330
331 /* Begin recursive parse sequence. */
332
333 if ( ! man_macro(m, c, ln, 1, &i, buf))
334 goto err;
335
336 return(1);
337
338 err: /* Error out. */
339
340 m->flags |= MAN_HALT;
341 return(0);
342 }
343
344
345 int
346 man_verr(struct man *man, int ln, int pos, const char *fmt, ...)
347 {
348 char buf[256];
349 va_list ap;
350
351 if (NULL == man->cb.man_err)
352 return(0);
353
354 va_start(ap, fmt);
355 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
356 va_end(ap);
357 return((*man->cb.man_err)(man->data, ln, pos, buf));
358 }
359
360
361 int
362 man_vwarn(struct man *man, int ln, int pos, const char *fmt, ...)
363 {
364 char buf[256];
365 va_list ap;
366
367 if (NULL == man->cb.man_warn)
368 return(0);
369
370 va_start(ap, fmt);
371 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
372 va_end(ap);
373 return((*man->cb.man_warn)(man->data, ln, pos, buf));
374 }
375
376