]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
*** empty log message ***
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.27 2009/01/12 12:52:21 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 }, /* Dd */
89 { macro_constant, MDOC_PROLOGUE }, /* Dt */
90 { macro_constant, MDOC_PROLOGUE }, /* 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_close_explicit, 0 }, /* Ed */
98 { macro_scoped, MDOC_EXPLICIT }, /* Bl */
99 { macro_close_explicit, 0 }, /* El */
100 { macro_scoped, MDOC_NESTED | MDOC_PARSED }, /* It */
101 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
102 { macro_constant, 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 }, /* 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_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Ac */
141 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* Ao */
142 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */
143 { macro_constant, 0 }, /* At */
144 { macro_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Bc */
145 { macro_scoped, MDOC_EXPLICIT }, /* Bf */
146 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* 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_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Dc */
152 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* Do */
153 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */
154 { macro_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Ec */
155 { macro_close_explicit, 0 }, /* Ef */
156 { macro_text, MDOC_CALLABLE | MDOC_PARSED }, /* Em */
157 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* 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_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
165 { macro_constant, MDOC_PARSED }, /* Pf */
166 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* Po */
167 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
168 { macro_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
169 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */
170 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* Qo */
171 { macro_scoped_line, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */
172 { macro_close_explicit, 0 }, /* Re */
173 { macro_scoped, MDOC_EXPLICIT }, /* Rs */
174 { macro_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Sc */
175 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* 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_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
183 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* 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_close_explicit, MDOC_PARSED }, /* Fc */
188 { macro_constant_scoped, MDOC_CALLABLE | MDOC_PARSED }, /* Oo */
189 { macro_close_explicit, MDOC_CALLABLE | MDOC_PARSED }, /* Oc */
190 { macro_scoped, MDOC_EXPLICIT }, /* Bk */
191 { macro_close_explicit, 0 }, /* 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 void mdoc_node_freelist(struct mdoc_node *);
209 static int mdoc_node_append(struct mdoc *,
210 struct mdoc_node *);
211 static void mdoc_elem_free(struct mdoc_elem *);
212 static void mdoc_text_free(struct mdoc_text *);
213
214
215 const struct mdoc_node *
216 mdoc_result(struct mdoc *mdoc)
217 {
218
219 return(mdoc->first);
220 }
221
222
223 void
224 mdoc_free(struct mdoc *mdoc)
225 {
226
227 if (mdoc->first)
228 mdoc_node_freelist(mdoc->first);
229 if (mdoc->htab)
230 mdoc_tokhash_free(mdoc->htab);
231
232 free(mdoc);
233 }
234
235
236 struct mdoc *
237 mdoc_alloc(void *data, const struct mdoc_cb *cb)
238 {
239 struct mdoc *p;
240
241 p = xcalloc(1, sizeof(struct mdoc));
242
243 p->data = data;
244 (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
245
246 p->last = xcalloc(1, sizeof(struct mdoc_node));
247 p->last->type = MDOC_ROOT;
248 p->first = p->last;
249
250 p->next = MDOC_NEXT_CHILD;
251 p->htab = mdoc_tokhash_alloc();
252
253 return(p);
254 }
255
256
257 int
258 mdoc_endparse(struct mdoc *mdoc)
259 {
260
261 if (MDOC_HALT & mdoc->flags)
262 return(0);
263 if (NULL == mdoc->first)
264 return(1);
265
266 assert(mdoc->last);
267 if ( ! macro_end(mdoc)) {
268 mdoc->flags |= MDOC_HALT;
269 return(0);
270 }
271 return(1);
272 }
273
274
275 int
276 mdoc_parseln(struct mdoc *mdoc, int line, char *buf)
277 {
278 int c, i;
279 char tmp[5];
280
281 if (MDOC_HALT & mdoc->flags)
282 return(0);
283
284 if ('.' != *buf) {
285 if (SEC_PROLOGUE != mdoc->sec_lastn) {
286 if ( ! mdoc_word_alloc(mdoc, line, 0, buf))
287 return(0);
288 mdoc->next = MDOC_NEXT_SIBLING;
289 return(1);
290 }
291 return(mdoc_perr(mdoc, line, 0, ERR_SYNTAX_NOTEXT));
292 }
293
294 if (buf[1] && '\\' == buf[1])
295 if (buf[2] && '\"' == buf[2])
296 return(1);
297
298 i = 1;
299 while (buf[i] && ! isspace(buf[i]) && i < (int)sizeof(tmp))
300 i++;
301
302 if (i == (int)sizeof(tmp)) {
303 mdoc->flags |= MDOC_HALT;
304 return(mdoc_perr(mdoc, line, 1, ERR_MACRO_NOTSUP));
305 } else if (i <= 2) {
306 mdoc->flags |= MDOC_HALT;
307 return(mdoc_perr(mdoc, line, 1, ERR_MACRO_NOTSUP));
308 }
309
310 i--;
311
312 (void)memcpy(tmp, buf + 1, (size_t)i);
313 tmp[i++] = 0;
314
315 if (MDOC_MAX == (c = mdoc_find(mdoc, tmp))) {
316 mdoc->flags |= MDOC_HALT;
317 return(mdoc_perr(mdoc, line, 1, ERR_MACRO_NOTSUP));
318 }
319
320 while (buf[i] && isspace(buf[i]))
321 i++;
322
323 if ( ! mdoc_macro(mdoc, c, line, 1, &i, buf)) {
324 mdoc->flags |= MDOC_HALT;
325 return(0);
326 }
327 return(1);
328 }
329
330
331 void
332 mdoc_msg(struct mdoc *mdoc, const char *fmt, ...)
333 {
334 struct mdoc_node *n;
335 va_list ap;
336 char buf[256];
337
338 if (NULL == mdoc->cb.mdoc_msg)
339 return;
340
341 n = mdoc->last;
342 assert(n);
343
344 va_start(ap, fmt);
345 (void)vsnprintf(buf, sizeof(buf), fmt, ap);
346 va_end(ap);
347
348 (*mdoc->cb.mdoc_msg)(mdoc->data, n->line, n->pos, buf);
349 }
350
351
352 int
353 mdoc_perr(struct mdoc *mdoc,
354 int line, int pos, enum mdoc_err type)
355 {
356
357 if (NULL == mdoc->cb.mdoc_err)
358 return(0);
359 return((*mdoc->cb.mdoc_err)(mdoc->data, line, pos, type));
360 }
361
362
363 int
364 mdoc_pwarn(struct mdoc *mdoc,
365 int line, int pos, enum mdoc_warn type)
366 {
367
368 if (NULL == mdoc->cb.mdoc_warn)
369 return(0);
370 return((*mdoc->cb.mdoc_warn)(mdoc->data, line, pos, type));
371 }
372
373
374 int
375 mdoc_macro(struct mdoc *mdoc, int tok,
376 int ln, int ppos, int *pos, char *buf)
377 {
378
379 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
380 SEC_PROLOGUE == mdoc->sec_lastn)
381 return(mdoc_perr(mdoc, ln, ppos, ERR_SEC_PROLOGUE));
382
383 if (NULL == (mdoc_macros[tok].fp))
384 return(mdoc_perr(mdoc, ln, ppos, ERR_MACRO_NOTSUP));
385
386 if (1 != ppos && ! (MDOC_CALLABLE & mdoc_macros[tok].flags))
387 return(mdoc_perr(mdoc, ln, ppos, ERR_MACRO_NOTCALL));
388
389 return((*mdoc_macros[tok].fp)(mdoc, tok, ln, ppos, pos, buf));
390 }
391
392
393 static int
394 mdoc_node_append(struct mdoc *mdoc, struct mdoc_node *p)
395 {
396 const char *on, *ot, *act;
397
398 assert(mdoc->last);
399 assert(mdoc->first);
400 assert(MDOC_ROOT != p->type);
401
402 if (MDOC_TEXT == mdoc->last->type)
403 on = "<text>";
404 else if (MDOC_ROOT == mdoc->last->type)
405 on = "<root>";
406 else
407 on = mdoc_macronames[mdoc->last->tok];
408
409 /* FIXME: put this into util.c. */
410 switch (mdoc->last->type) {
411 case (MDOC_TEXT):
412 ot = "text";
413 break;
414 case (MDOC_BODY):
415 ot = "body";
416 break;
417 case (MDOC_ELEM):
418 ot = "elem";
419 break;
420 case (MDOC_HEAD):
421 ot = "head";
422 break;
423 case (MDOC_TAIL):
424 ot = "tail";
425 break;
426 case (MDOC_BLOCK):
427 ot = "block";
428 break;
429 case (MDOC_ROOT):
430 ot = "root";
431 break;
432 default:
433 abort();
434 /* NOTREACHED */
435 }
436
437 switch (mdoc->next) {
438 case (MDOC_NEXT_SIBLING):
439 mdoc->last->next = p;
440 p->prev = mdoc->last;
441 p->parent = mdoc->last->parent;
442 act = "sibling";
443 break;
444 case (MDOC_NEXT_CHILD):
445 mdoc->last->child = p;
446 p->parent = mdoc->last;
447 act = "child";
448 break;
449 default:
450 abort();
451 /* NOTREACHED */
452 }
453
454 if ( ! mdoc_valid_pre(mdoc, p))
455 return(0);
456 if ( ! mdoc_action_pre(mdoc, p))
457 return(0);
458
459 switch (p->type) {
460 case (MDOC_HEAD):
461 assert(MDOC_BLOCK == p->parent->type);
462 p->parent->data.block.head = p;
463 break;
464 case (MDOC_TAIL):
465 assert(MDOC_BLOCK == p->parent->type);
466 p->parent->data.block.tail = p;
467 break;
468 case (MDOC_BODY):
469 assert(MDOC_BLOCK == p->parent->type);
470 p->parent->data.block.body = p;
471 break;
472 default:
473 break;
474 }
475
476 mdoc->last = p;
477 mdoc_msg(mdoc, "parse: %s of %s `%s'", act, ot, on);
478 return(1);
479 }
480
481
482 int
483 mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, int tok)
484 {
485 struct mdoc_node *p;
486
487 assert(mdoc->first);
488 assert(mdoc->last);
489
490 p = xcalloc(1, sizeof(struct mdoc_node));
491
492 p->line = line;
493 p->pos = pos;
494 p->type = MDOC_TAIL;
495 p->tok = tok;
496
497 return(mdoc_node_append(mdoc, p));
498 }
499
500
501 int
502 mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, int tok)
503 {
504 struct mdoc_node *p;
505
506 assert(mdoc->first);
507 assert(mdoc->last);
508
509 p = xcalloc(1, sizeof(struct mdoc_node));
510
511 p->line = line;
512 p->pos = pos;
513 p->type = MDOC_HEAD;
514 p->tok = tok;
515
516 return(mdoc_node_append(mdoc, p));
517 }
518
519
520 int
521 mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, int tok)
522 {
523 struct mdoc_node *p;
524
525 assert(mdoc->first);
526 assert(mdoc->last);
527
528 p = xcalloc(1, sizeof(struct mdoc_node));
529
530 p->line = line;
531 p->pos = pos;
532 p->type = MDOC_BODY;
533 p->tok = tok;
534
535 return(mdoc_node_append(mdoc, p));
536 }
537
538
539 int
540 mdoc_root_alloc(struct mdoc *mdoc)
541 {
542 struct mdoc_node *p;
543
544 p = xcalloc(1, sizeof(struct mdoc_node));
545
546 p->type = MDOC_ROOT;
547
548 return(mdoc_node_append(mdoc, p));
549 }
550
551
552 int
553 mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
554 int tok, size_t argsz, const struct mdoc_arg *args)
555 {
556 struct mdoc_node *p;
557
558 p = xcalloc(1, sizeof(struct mdoc_node));
559
560 p->pos = pos;
561 p->line = line;
562 p->type = MDOC_BLOCK;
563 p->tok = tok;
564 p->data.block.argc = argsz;
565 p->data.block.argv = argdup(argsz, args);
566
567 return(mdoc_node_append(mdoc, p));
568 }
569
570
571 int
572 mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
573 int tok, size_t argsz, const struct mdoc_arg *args)
574 {
575 struct mdoc_node *p;
576
577 p = xcalloc(1, sizeof(struct mdoc_node));
578
579 p->line = line;
580 p->pos = pos;
581 p->type = MDOC_ELEM;
582 p->tok = tok;
583 p->data.elem.argc = argsz;
584 p->data.elem.argv = argdup(argsz, args);
585
586 return(mdoc_node_append(mdoc, p));
587 }
588
589
590 int
591 mdoc_word_alloc(struct mdoc *mdoc,
592 int line, int pos, const char *word)
593 {
594 struct mdoc_node *p;
595
596 p = xcalloc(1, sizeof(struct mdoc_node));
597 p->line = line;
598 p->pos = pos;
599 p->type = MDOC_TEXT;
600 p->data.text.string = xstrdup(word);
601
602 return(mdoc_node_append(mdoc, p));
603 }
604
605
606 static void
607 argfree(size_t sz, struct mdoc_arg *p)
608 {
609 int i, j;
610
611 if (0 == sz)
612 return;
613
614 assert(p);
615 /* LINTED */
616 for (i = 0; i < (int)sz; i++)
617 if (p[i].sz > 0) {
618 assert(p[i].value);
619 /* LINTED */
620 for (j = 0; j < (int)p[i].sz; j++)
621 free(p[i].value[j]);
622 free(p[i].value);
623 }
624 free(p);
625 }
626
627
628 static void
629 mdoc_elem_free(struct mdoc_elem *p)
630 {
631
632 argfree(p->argc, p->argv);
633 }
634
635
636 static void
637 mdoc_block_free(struct mdoc_block *p)
638 {
639
640 argfree(p->argc, p->argv);
641 }
642
643
644 static void
645 mdoc_text_free(struct mdoc_text *p)
646 {
647
648 if (p->string)
649 free(p->string);
650 }
651
652
653 void
654 mdoc_node_free(struct mdoc_node *p)
655 {
656
657 switch (p->type) {
658 case (MDOC_TEXT):
659 mdoc_text_free(&p->data.text);
660 break;
661 case (MDOC_ELEM):
662 mdoc_elem_free(&p->data.elem);
663 break;
664 case (MDOC_BLOCK):
665 mdoc_block_free(&p->data.block);
666 break;
667 default:
668 break;
669 }
670
671 free(p);
672 }
673
674
675 static void
676 mdoc_node_freelist(struct mdoc_node *p)
677 {
678
679 if (p->child)
680 mdoc_node_freelist(p->child);
681 if (p->next)
682 mdoc_node_freelist(p->next);
683
684 mdoc_node_free(p);
685 }
686
687
688 int
689 mdoc_find(const struct mdoc *mdoc, const char *key)
690 {
691
692 return(mdoc_tokhash_find(mdoc->htab, key));
693 }
694
695
696 static void
697 argcpy(struct mdoc_arg *dst, const struct mdoc_arg *src)
698 {
699 int i;
700
701 dst->line = src->line;
702 dst->pos = src->pos;
703 dst->arg = src->arg;
704 if (0 == (dst->sz = src->sz))
705 return;
706 dst->value = xcalloc(dst->sz, sizeof(char *));
707 for (i = 0; i < (int)dst->sz; i++)
708 dst->value[i] = xstrdup(src->value[i]);
709 }
710
711
712 static struct mdoc_arg *
713 argdup(size_t argsz, const struct mdoc_arg *args)
714 {
715 struct mdoc_arg *pp;
716 int i;
717
718 if (0 == argsz)
719 return(NULL);
720
721 pp = xcalloc((size_t)argsz, sizeof(struct mdoc_arg));
722 for (i = 0; i < (int)argsz; i++)
723 argcpy(&pp[i], &args[i]);
724
725 return(pp);
726 }
727