]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
More correct validation.
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.37 2009/01/19 17:51:33 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
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 "private.h"
28
29 const char *const __mdoc_macronames[MDOC_MAX] = {
30 "\\\"", "Dd", "Dt", "Os",
31 "Sh", "Ss", "Pp", "D1",
32 "Dl", "Bd", "Ed", "Bl",
33 "El", "It", "Ad", "An",
34 "Ar", "Cd", "Cm", "Dv",
35 "Er", "Ev", "Ex", "Fa",
36 "Fd", "Fl", "Fn", "Ft",
37 "Ic", "In", "Li", "Nd",
38 "Nm", "Op", "Ot", "Pa",
39 "Rv", "St", "Va", "Vt",
40 /* LINTED */
41 "Xr", "\%A", "\%B", "\%D",
42 /* LINTED */
43 "\%I", "\%J", "\%N", "\%O",
44 /* LINTED */
45 "\%P", "\%R", "\%T", "\%V",
46 "Ac", "Ao", "Aq", "At",
47 "Bc", "Bf", "Bo", "Bq",
48 "Bsx", "Bx", "Db", "Dc",
49 "Do", "Dq", "Ec", "Ef",
50 "Em", "Eo", "Fx", "Ms",
51 "No", "Ns", "Nx", "Ox",
52 "Pc", "Pf", "Po", "Pq",
53 "Qc", "Ql", "Qo", "Qq",
54 "Re", "Rs", "Sc", "So",
55 "Sq", "Sm", "Sx", "Sy",
56 "Tn", "Ux", "Xc", "Xo",
57 "Fo", "Fc", "Oo", "Oc",
58 "Bk", "Ek", "Bt", "Hf",
59 "Fr", "Ud",
60 };
61
62 const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
63 "split", "nosplit", "ragged",
64 "unfilled", "literal", "file",
65 "offset", "bullet", "dash",
66 "hyphen", "item", "enum",
67 "tag", "diag", "hang",
68 "ohang", "inset", "column",
69 "width", "compact", "std",
70 "p1003.1-88", "p1003.1-90", "p1003.1-96",
71 "p1003.1-2001", "p1003.1-2004", "p1003.1",
72 "p1003.1b", "p1003.1b-93", "p1003.1c-95",
73 "p1003.1g-2000", "p1003.2-92", "p1387.2-95",
74 "p1003.2", "p1387.2", "isoC-90",
75 "isoC-amd1", "isoC-tcor1", "isoC-tcor2",
76 "isoC-99", "ansiC", "ansiC-89",
77 "ansiC-99", "ieee754", "iso8802-3",
78 "xpg3", "xpg4", "xpg4.2",
79 "xpg4.3", "xbd5", "xcu5",
80 "xsh5", "xns5", "xns5.2d2.0",
81 "xcurses4.2", "susv2", "susv3",
82 "svid4", "filled", "words",
83 "emphasis", "symbolic",
84 };
85
86 const struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
87 { NULL, 0 }, /* \" */
88 { macro_constant, MDOC_PROLOGUE | MDOC_NOKEEP }, /* Dd */
89 { macro_constant, MDOC_PROLOGUE | MDOC_NOKEEP }, /* Dt */
90 { macro_constant, MDOC_PROLOGUE | MDOC_NOKEEP }, /* Os */
91 { macro_scoped, 0 }, /* Sh */
92 { macro_scoped, 0 }, /* Ss */
93 { macro_text, 0 }, /* Pp */
94 { macro_scoped_line, MDOC_PARSED }, /* D1 */
95 { macro_scoped_line, MDOC_PARSED }, /* Dl */
96 { macro_scoped, MDOC_EXPLICIT }, /* Bd */
97 { macro_scoped_close, MDOC_EXPLICIT }, /* Ed */
98 { macro_scoped, MDOC_EXPLICIT }, /* Bl */
99 { macro_scoped_close, MDOC_EXPLICIT }, /* El */
100 { macro_scoped, MDOC_PARSED | MDOC_TABSEP}, /* It */
101 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
102 { macro_text, MDOC_PARSED }, /* An */
103 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
104 { macro_constant, MDOC_QUOTABLE }, /* Cd */
105 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
106 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
107 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
108 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
109 { macro_constant, 0 }, /* Ex */
110 { macro_text, MDOC_CALLABLE | MDOC_QUOTABLE | MDOC_PARSED }, /* Fa */
111 { macro_constant, 0 }, /* Fd */
112 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
113 { macro_text, MDOC_CALLABLE | MDOC_QUOTABLE | MDOC_PARSED }, /* Fn */
114 { macro_text, MDOC_PARSED | MDOC_QUOTABLE }, /* Ft */
115 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
116 { macro_constant, 0 }, /* In */
117 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
118 { macro_constant, 0 }, /* Nd */
119 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
120 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
121 { macro_obsolete, 0 }, /* Ot */
122 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
123 { macro_constant, 0 }, /* Rv */
124 /* XXX - .St supposed to be (but isn't) callable. */
125 { macro_constant_delimited, MDOC_PARSED }, /* St */
126 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
127 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
128 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
129 { macro_constant, MDOC_QUOTABLE }, /* %A */
130 { macro_constant, MDOC_QUOTABLE }, /* %B */
131 { macro_constant, MDOC_QUOTABLE }, /* %D */
132 { macro_constant, MDOC_QUOTABLE }, /* %I */
133 { macro_constant, MDOC_QUOTABLE }, /* %J */
134 { macro_constant, MDOC_QUOTABLE }, /* %N */
135 { macro_constant, MDOC_QUOTABLE }, /* %O */
136 { macro_constant, MDOC_QUOTABLE }, /* %P */
137 { macro_constant, MDOC_QUOTABLE }, /* %R */
138 { macro_constant, MDOC_QUOTABLE }, /* %T */
139 { macro_constant, MDOC_QUOTABLE }, /* %V */
140 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */
141 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */
142 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */
143 { macro_constant_delimited, 0 }, /* At */
144 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */
145 { macro_scoped, MDOC_EXPLICIT }, /* Bf */
146 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */
147 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */
148 { macro_constant_delimited, MDOC_PARSED }, /* Bsx */
149 { macro_constant_delimited, MDOC_PARSED }, /* Bx */
150 { macro_constant, 0 }, /* Db */
151 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */
152 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */
153 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */
154 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */
155 { macro_scoped_close, MDOC_EXPLICIT }, /* Ef */
156 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Em */
157 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
158 { macro_constant_delimited, MDOC_PARSED }, /* Fx */
159 { macro_text, MDOC_PARSED }, /* Ms */
160 { macro_constant_delimited, MDOC_CALLABLE | MDOC_PARSED }, /* No */
161 { macro_constant_delimited, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */
162 { macro_constant_delimited, MDOC_PARSED }, /* Nx */
163 { macro_constant_delimited, MDOC_PARSED }, /* Ox */
164 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
165 { macro_constant_delimited, MDOC_PARSED }, /* Pf */
166 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
167 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
168 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
169 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */
170 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */
171 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */
172 { macro_scoped_close, MDOC_EXPLICIT }, /* Re */
173 { macro_scoped, MDOC_EXPLICIT }, /* Rs */
174 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */
175 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */
176 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */
177 { macro_constant, 0 }, /* Sm */
178 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */
179 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */
180 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
181 { macro_constant_delimited, MDOC_PARSED }, /* Ux */
182 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
183 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
184 /* XXX - .Fo supposed to be (but isn't) callable. */
185 { macro_scoped, MDOC_EXPLICIT | MDOC_PARSED }, /* Fo */
186 /* XXX - .Fc supposed to be (but isn't) callable. */
187 { macro_scoped_close, MDOC_EXPLICIT | MDOC_PARSED }, /* Fc */
188 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */
189 { macro_scoped_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */
190 { macro_scoped, MDOC_EXPLICIT }, /* Bk */
191 { macro_scoped_close, MDOC_EXPLICIT }, /* Ek */
192 { macro_constant, 0 }, /* Bt */
193 { macro_constant, 0 }, /* Hf */
194 { macro_obsolete, 0 }, /* Fr */
195 { macro_constant, 0 }, /* Ud */
196 };
197
198 const char * const *mdoc_macronames = __mdoc_macronames;
199 const char * const *mdoc_argnames = __mdoc_argnames;
200 const struct mdoc_macro * const mdoc_macros = __mdoc_macros;
201
202
203 static struct mdoc_arg *argdup(size_t, const struct mdoc_arg *);
204 static void argfree(size_t, struct mdoc_arg *);
205 static void argcpy(struct mdoc_arg *,
206 const struct mdoc_arg *);
207
208 static int mdoc_node_append(struct mdoc *,
209 struct mdoc_node *);
210 static void mdoc_elem_free(struct mdoc_elem *);
211 static void mdoc_text_free(struct mdoc_text *);
212
213
214 const struct mdoc_node *
215 mdoc_node(struct mdoc *mdoc)
216 {
217
218 return(mdoc->first);
219 }
220
221
222 const struct mdoc_meta *
223 mdoc_meta(struct mdoc *mdoc)
224 {
225
226 return(&mdoc->meta);
227 }
228
229
230 void
231 mdoc_meta_free(struct mdoc *mdoc)
232 {
233
234 if (mdoc->meta.title)
235 free(mdoc->meta.title);
236 if (mdoc->meta.os)
237 free(mdoc->meta.os);
238 if (mdoc->meta.name)
239 free(mdoc->meta.name);
240 }
241
242
243 void
244 mdoc_free(struct mdoc *mdoc)
245 {
246
247 if (mdoc->first)
248 mdoc_node_freelist(mdoc->first);
249 if (mdoc->htab)
250 mdoc_tokhash_free(mdoc->htab);
251
252 free(mdoc);
253 }
254
255
256 struct mdoc *
257 mdoc_alloc(void *data, const struct mdoc_cb *cb)
258 {
259 struct mdoc *p;
260
261 p = xcalloc(1, sizeof(struct mdoc));
262
263 p->data = data;
264 if (cb)
265 (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
266
267 p->last = xcalloc(1, sizeof(struct mdoc_node));
268 p->last->type = MDOC_ROOT;
269 p->first = p->last;
270
271 p->next = MDOC_NEXT_CHILD;
272 p->htab = mdoc_tokhash_alloc();
273
274 return(p);
275 }
276
277
278 int
279 mdoc_endparse(struct mdoc *mdoc)
280 {
281
282 if (MDOC_HALT & mdoc->flags)
283 return(0);
284 if (NULL == mdoc->first)
285 return(1);
286
287 assert(mdoc->last);
288 if ( ! macro_end(mdoc)) {
289 mdoc->flags |= MDOC_HALT;
290 return(0);
291 }
292 return(1);
293 }
294
295
296 int
297 mdoc_parseln(struct mdoc *mdoc, int line, char *buf)
298 {
299 int c, i;
300 char tmp[5];
301
302 if (MDOC_HALT & mdoc->flags)
303 return(0);
304
305 if ('.' != *buf) {
306 if (SEC_PROLOGUE != mdoc->sec_lastn) {
307 if ( ! mdoc_word_alloc(mdoc, line, 0, buf))
308 return(0);
309 mdoc->next = MDOC_NEXT_SIBLING;
310 return(1);
311 }
312 return(mdoc_perr(mdoc, line, 0, "text disallowed"));
313 }
314
315 if (buf[1] && '\\' == buf[1])
316 if (buf[2] && '\"' == buf[2])
317 return(1);
318
319 i = 1;
320 while (buf[i] && ! isspace(buf[i]) && i < (int)sizeof(tmp))
321 i++;
322
323 if (i == (int)sizeof(tmp)) {
324 mdoc->flags |= MDOC_HALT;
325 return(mdoc_perr(mdoc, line, 1, "unknown macro"));
326 } else if (i <= 2) {
327 mdoc->flags |= MDOC_HALT;
328 return(mdoc_perr(mdoc, line, 1, "unknown macro"));
329 }
330
331 i--;
332
333 (void)memcpy(tmp, buf + 1, (size_t)i);
334 tmp[i++] = 0;
335
336 if (MDOC_MAX == (c = mdoc_find(mdoc, tmp))) {
337 mdoc->flags |= MDOC_HALT;
338 return(mdoc_perr(mdoc, line, 1, "unknown macro"));
339 }
340
341 while (buf[i] && isspace(buf[i]))
342 i++;
343
344 if ( ! mdoc_macro(mdoc, c, line, 1, &i, buf)) {
345 mdoc->flags |= MDOC_HALT;
346 return(0);
347 }
348 return(1);
349 }
350
351
352 void
353 mdoc_vmsg(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...)
354 {
355 char buf[256];
356 va_list ap;
357
358 if (NULL == mdoc->cb.mdoc_msg)
359 return;
360
361 va_start(ap, fmt);
362 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
363 va_end(ap);
364 (*mdoc->cb.mdoc_msg)(mdoc->data, ln, pos, buf);
365 }
366
367
368 int
369 mdoc_verr(struct mdoc *mdoc, int ln, int pos,
370 const char *fmt, ...)
371 {
372 char buf[256];
373 va_list ap;
374
375 if (NULL == mdoc->cb.mdoc_err)
376 return(0);
377
378 va_start(ap, fmt);
379 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
380 va_end(ap);
381 return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf));
382 }
383
384
385 int
386 mdoc_vwarn(struct mdoc *mdoc, int ln, int pos,
387 enum mdoc_warn type, const char *fmt, ...)
388 {
389 char buf[256];
390 va_list ap;
391
392 if (NULL == mdoc->cb.mdoc_warn)
393 return(0);
394
395 va_start(ap, fmt);
396 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
397 va_end(ap);
398 return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, type, buf));
399 }
400
401
402 int
403 mdoc_macro(struct mdoc *mdoc, int tok,
404 int ln, int ppos, int *pos, char *buf)
405 {
406
407 assert(mdoc_macros[tok].fp);
408
409 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
410 SEC_PROLOGUE == mdoc->sec_lastn)
411 return(mdoc_perr(mdoc, ln, ppos, "macro disallowed in document prologue"));
412 if (1 != ppos && ! (MDOC_CALLABLE & mdoc_macros[tok].flags))
413 return(mdoc_perr(mdoc, ln, ppos, "macro not callable"));
414 return((*mdoc_macros[tok].fp)(mdoc, tok, ln, ppos, pos, buf));
415 }
416
417
418 static int
419 mdoc_node_append(struct mdoc *mdoc, struct mdoc_node *p)
420 {
421 const char *nn, *nt, *on, *ot, *act;
422
423 assert(mdoc->last);
424 assert(mdoc->first);
425 assert(MDOC_ROOT != p->type);
426
427 if (MDOC_TEXT == mdoc->last->type)
428 on = "<text>";
429 else if (MDOC_ROOT == mdoc->last->type)
430 on = "<root>";
431 else
432 on = mdoc_macronames[mdoc->last->tok];
433
434 if (MDOC_TEXT == p->type)
435 nn = "<text>";
436 else if (MDOC_ROOT == p->type)
437 nn = "<root>";
438 else
439 nn = mdoc_macronames[p->tok];
440
441 ot = mdoc_type2a(mdoc->last->type);
442 nt = mdoc_type2a(p->type);
443
444 switch (mdoc->next) {
445 case (MDOC_NEXT_SIBLING):
446 mdoc->last->next = p;
447 p->prev = mdoc->last;
448 p->parent = mdoc->last->parent;
449 act = "sibling";
450 break;
451 case (MDOC_NEXT_CHILD):
452 mdoc->last->child = p;
453 p->parent = mdoc->last;
454 act = "child";
455 break;
456 default:
457 abort();
458 /* NOTREACHED */
459 }
460
461 if ( ! mdoc_valid_pre(mdoc, p))
462 return(0);
463
464 switch (p->type) {
465 case (MDOC_HEAD):
466 assert(MDOC_BLOCK == p->parent->type);
467 p->parent->data.block.head = p;
468 break;
469 case (MDOC_TAIL):
470 assert(MDOC_BLOCK == p->parent->type);
471 p->parent->data.block.tail = p;
472 break;
473 case (MDOC_BODY):
474 assert(MDOC_BLOCK == p->parent->type);
475 p->parent->data.block.body = p;
476 break;
477 default:
478 break;
479 }
480
481 mdoc->last = p;
482 mdoc_msg(mdoc, "parse: %s `%s' %s of %s `%s'",
483 nt, nn, act, ot, on);
484 return(1);
485 }
486
487
488 int
489 mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, int tok)
490 {
491 struct mdoc_node *p;
492
493 assert(mdoc->first);
494 assert(mdoc->last);
495
496 p = xcalloc(1, sizeof(struct mdoc_node));
497
498 p->line = line;
499 p->pos = pos;
500 p->type = MDOC_TAIL;
501 p->tok = tok;
502
503 return(mdoc_node_append(mdoc, p));
504 }
505
506
507 int
508 mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, int tok)
509 {
510 struct mdoc_node *p;
511
512 assert(mdoc->first);
513 assert(mdoc->last);
514
515 p = xcalloc(1, sizeof(struct mdoc_node));
516
517 p->line = line;
518 p->pos = pos;
519 p->type = MDOC_HEAD;
520 p->tok = tok;
521
522 return(mdoc_node_append(mdoc, p));
523 }
524
525
526 int
527 mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, int tok)
528 {
529 struct mdoc_node *p;
530
531 assert(mdoc->first);
532 assert(mdoc->last);
533
534 p = xcalloc(1, sizeof(struct mdoc_node));
535
536 p->line = line;
537 p->pos = pos;
538 p->type = MDOC_BODY;
539 p->tok = tok;
540
541 return(mdoc_node_append(mdoc, p));
542 }
543
544
545 int
546 mdoc_root_alloc(struct mdoc *mdoc)
547 {
548 struct mdoc_node *p;
549
550 p = xcalloc(1, sizeof(struct mdoc_node));
551
552 p->type = MDOC_ROOT;
553
554 return(mdoc_node_append(mdoc, p));
555 }
556
557
558 int
559 mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
560 int tok, size_t argsz, const struct mdoc_arg *args)
561 {
562 struct mdoc_node *p;
563
564 p = xcalloc(1, sizeof(struct mdoc_node));
565
566 p->pos = pos;
567 p->line = line;
568 p->type = MDOC_BLOCK;
569 p->tok = tok;
570 p->data.block.argc = argsz;
571 p->data.block.argv = argdup(argsz, args);
572
573 return(mdoc_node_append(mdoc, p));
574 }
575
576
577 int
578 mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
579 int tok, size_t argsz, const struct mdoc_arg *args)
580 {
581 struct mdoc_node *p;
582
583 p = xcalloc(1, sizeof(struct mdoc_node));
584
585 p->line = line;
586 p->pos = pos;
587 p->type = MDOC_ELEM;
588 p->tok = tok;
589 p->data.elem.argc = argsz;
590 p->data.elem.argv = argdup(argsz, args);
591
592 return(mdoc_node_append(mdoc, p));
593 }
594
595
596 int
597 mdoc_word_alloc(struct mdoc *mdoc,
598 int line, int pos, const char *word)
599 {
600 struct mdoc_node *p;
601
602 p = xcalloc(1, sizeof(struct mdoc_node));
603 p->line = line;
604 p->pos = pos;
605 p->type = MDOC_TEXT;
606 p->data.text.string = xstrdup(word);
607
608 return(mdoc_node_append(mdoc, p));
609 }
610
611
612 static void
613 argfree(size_t sz, struct mdoc_arg *p)
614 {
615 int i, j;
616
617 if (0 == sz)
618 return;
619
620 assert(p);
621 /* LINTED */
622 for (i = 0; i < (int)sz; i++)
623 if (p[i].sz > 0) {
624 assert(p[i].value);
625 /* LINTED */
626 for (j = 0; j < (int)p[i].sz; j++)
627 free(p[i].value[j]);
628 free(p[i].value);
629 }
630 free(p);
631 }
632
633
634 static void
635 mdoc_elem_free(struct mdoc_elem *p)
636 {
637
638 argfree(p->argc, p->argv);
639 }
640
641
642 static void
643 mdoc_block_free(struct mdoc_block *p)
644 {
645
646 argfree(p->argc, p->argv);
647 }
648
649
650 static void
651 mdoc_text_free(struct mdoc_text *p)
652 {
653
654 if (p->string)
655 free(p->string);
656 }
657
658
659 void
660 mdoc_node_free(struct mdoc_node *p)
661 {
662
663 switch (p->type) {
664 case (MDOC_TEXT):
665 mdoc_text_free(&p->data.text);
666 break;
667 case (MDOC_ELEM):
668 mdoc_elem_free(&p->data.elem);
669 break;
670 case (MDOC_BLOCK):
671 mdoc_block_free(&p->data.block);
672 break;
673 default:
674 break;
675 }
676
677 free(p);
678 }
679
680
681 void
682 mdoc_node_freelist(struct mdoc_node *p)
683 {
684
685 if (p->child)
686 mdoc_node_freelist(p->child);
687 if (p->next)
688 mdoc_node_freelist(p->next);
689
690 mdoc_node_free(p);
691 }
692
693
694 int
695 mdoc_find(const struct mdoc *mdoc, const char *key)
696 {
697
698 return(mdoc_tokhash_find(mdoc->htab, key));
699 }
700
701
702 static void
703 argcpy(struct mdoc_arg *dst, const struct mdoc_arg *src)
704 {
705 int i;
706
707 dst->line = src->line;
708 dst->pos = src->pos;
709 dst->arg = src->arg;
710 if (0 == (dst->sz = src->sz))
711 return;
712 dst->value = xcalloc(dst->sz, sizeof(char *));
713 for (i = 0; i < (int)dst->sz; i++)
714 dst->value[i] = xstrdup(src->value[i]);
715 }
716
717
718 static struct mdoc_arg *
719 argdup(size_t argsz, const struct mdoc_arg *args)
720 {
721 struct mdoc_arg *pp;
722 int i;
723
724 if (0 == argsz)
725 return(NULL);
726
727 pp = xcalloc((size_t)argsz, sizeof(struct mdoc_arg));
728 for (i = 0; i < (int)argsz; i++)
729 argcpy(&pp[i], &args[i]);
730
731 return(pp);
732 }
733
734
735 /* FIXME: deprecate. */
736 char *
737 mdoc_node2a(struct mdoc_node *node)
738 {
739 static char buf[64];
740
741 assert(node);
742
743 buf[0] = 0;
744 (void)xstrlcat(buf, mdoc_type2a(node->type), 64);
745 if (MDOC_ROOT == node->type)
746 return(buf);
747 (void)xstrlcat(buf, " `", 64);
748 if (MDOC_TEXT == node->type)
749 (void)xstrlcat(buf, node->data.text.string, 64);
750 else
751 (void)xstrlcat(buf, mdoc_macronames[node->tok], 64);
752 (void)xstrlcat(buf, "'", 64);
753
754 return(buf);
755 }
756
757