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