]> git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
48785716e0f9bbe69fd4ce8a89c21b8a556688b9
[mandoc.git] / mdoc.c
1 /* $Id: mdoc.c,v 1.52 2009/03/06 14:13:47 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 and validate.c.
33 */
34
35 static struct mdoc_arg *argdup(size_t, const struct mdoc_arg *);
36 static void argfree(size_t, struct mdoc_arg *);
37 static void argcpy(struct mdoc_arg *,
38 const struct mdoc_arg *);
39
40 static struct mdoc_node *mdoc_node_alloc(const struct mdoc *);
41 static int mdoc_node_append(struct mdoc *,
42 struct mdoc_node *);
43 static void mdoc_elem_free(struct mdoc_elem *);
44 static void mdoc_text_free(struct mdoc_text *);
45
46
47 const char *const __mdoc_macronames[MDOC_MAX] = {
48 "\\\"", "Dd", "Dt", "Os",
49 "Sh", "Ss", "Pp", "D1",
50 "Dl", "Bd", "Ed", "Bl",
51 "El", "It", "Ad", "An",
52 "Ar", "Cd", "Cm", "Dv",
53 "Er", "Ev", "Ex", "Fa",
54 "Fd", "Fl", "Fn", "Ft",
55 "Ic", "In", "Li", "Nd",
56 "Nm", "Op", "Ot", "Pa",
57 "Rv", "St", "Va", "Vt",
58 /* LINTED */
59 "Xr", "\%A", "\%B", "\%D",
60 /* LINTED */
61 "\%I", "\%J", "\%N", "\%O",
62 /* LINTED */
63 "\%P", "\%R", "\%T", "\%V",
64 "Ac", "Ao", "Aq", "At",
65 "Bc", "Bf", "Bo", "Bq",
66 "Bsx", "Bx", "Db", "Dc",
67 "Do", "Dq", "Ec", "Ef",
68 "Em", "Eo", "Fx", "Ms",
69 "No", "Ns", "Nx", "Ox",
70 "Pc", "Pf", "Po", "Pq",
71 "Qc", "Ql", "Qo", "Qq",
72 "Re", "Rs", "Sc", "So",
73 "Sq", "Sm", "Sx", "Sy",
74 "Tn", "Ux", "Xc", "Xo",
75 "Fo", "Fc", "Oo", "Oc",
76 "Bk", "Ek", "Bt", "Hf",
77 "Fr", "Ud", "Lb",
78 };
79
80 const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
81 "split", "nosplit", "ragged",
82 "unfilled", "literal", "file",
83 "offset", "bullet", "dash",
84 "hyphen", "item", "enum",
85 "tag", "diag", "hang",
86 "ohang", "inset", "column",
87 "width", "compact", "std",
88 "filled", "words", "emphasis",
89 "symbolic"
90 };
91
92 const char * const *mdoc_macronames = __mdoc_macronames;
93 const char * const *mdoc_argnames = __mdoc_argnames;
94
95
96 const struct mdoc_node *
97 mdoc_node(const struct mdoc *mdoc)
98 {
99
100 return(mdoc->first);
101 }
102
103
104 const struct mdoc_meta *
105 mdoc_meta(const struct mdoc *mdoc)
106 {
107
108 return(&mdoc->meta);
109 }
110
111
112 void
113 mdoc_free(struct mdoc *mdoc)
114 {
115
116 if (mdoc->first)
117 mdoc_node_freelist(mdoc->first);
118 if (mdoc->htab)
119 mdoc_tokhash_free(mdoc->htab);
120 if (mdoc->meta.title)
121 free(mdoc->meta.title);
122 if (mdoc->meta.os)
123 free(mdoc->meta.os);
124 if (mdoc->meta.name)
125 free(mdoc->meta.name);
126 if (mdoc->meta.arch)
127 free(mdoc->meta.arch);
128 if (mdoc->meta.vol)
129 free(mdoc->meta.vol);
130
131 free(mdoc);
132 }
133
134
135 struct mdoc *
136 mdoc_alloc(void *data, const struct mdoc_cb *cb)
137 {
138 struct mdoc *p;
139
140 p = xcalloc(1, sizeof(struct mdoc));
141
142 p->data = data;
143 if (cb)
144 (void)memcpy(&p->cb, cb, sizeof(struct mdoc_cb));
145
146 p->last = xcalloc(1, sizeof(struct mdoc_node));
147 p->last->type = MDOC_ROOT;
148 p->first = p->last;
149
150 p->next = MDOC_NEXT_CHILD;
151 p->htab = mdoc_tokhash_alloc();
152
153 return(p);
154 }
155
156
157 int
158 mdoc_endparse(struct mdoc *mdoc)
159 {
160
161 if (MDOC_HALT & mdoc->flags)
162 return(0);
163 if (NULL == mdoc->first)
164 return(1);
165
166 assert(mdoc->last);
167 if ( ! macro_end(mdoc)) {
168 mdoc->flags |= MDOC_HALT;
169 return(0);
170 }
171 return(1);
172 }
173
174
175 /*
176 * Main line-parsing routine. If the line is a macro-line (started with
177 * a '.' control character), then pass along to the parser, which parses
178 * subsequent macros until the end of line. If normal text, simply
179 * append the entire line to the chain.
180 */
181 int
182 mdoc_parseln(struct mdoc *mdoc, int line, char *buf)
183 {
184 int c, i;
185 char tmp[5];
186
187 if (MDOC_HALT & mdoc->flags)
188 return(0);
189
190 mdoc->linetok = 0;
191
192 if ('.' != *buf) {
193 /*
194 * Free-form text. Not allowed in the prologue.
195 */
196 if (SEC_PROLOGUE == mdoc->lastnamed)
197 return(mdoc_perr(mdoc, line, 0,
198 "no text in prologue"));
199
200 if ( ! mdoc_word_alloc(mdoc, line, 0, buf))
201 return(0);
202 mdoc->next = MDOC_NEXT_SIBLING;
203 return(1);
204 }
205
206 /*
207 * Control-character detected. Begin the parsing sequence.
208 */
209
210 if (buf[1] && '\\' == buf[1])
211 if (buf[2] && '\"' == buf[2])
212 return(1);
213
214 i = 1;
215 while (buf[i] && ! isspace((u_char)buf[i]) &&
216 i < (int)sizeof(tmp))
217 i++;
218
219 if (i == (int)sizeof(tmp)) {
220 mdoc->flags |= MDOC_HALT;
221 return(mdoc_perr(mdoc, line, 1, "unknown macro"));
222 } else if (i <= 2) {
223 mdoc->flags |= MDOC_HALT;
224 return(mdoc_perr(mdoc, line, 1, "unknown macro"));
225 }
226
227 i--;
228
229 (void)memcpy(tmp, buf + 1, (size_t)i);
230 tmp[i++] = 0;
231
232 if (MDOC_MAX == (c = mdoc_find(mdoc, tmp))) {
233 mdoc->flags |= MDOC_HALT;
234 return(mdoc_perr(mdoc, line, 1, "unknown macro"));
235 }
236
237 while (buf[i] && isspace((u_char)buf[i]))
238 i++;
239
240 if ( ! mdoc_macro(mdoc, c, line, 1, &i, buf)) {
241 mdoc->flags |= MDOC_HALT;
242 return(0);
243 }
244
245 return(1);
246 }
247
248
249 void
250 mdoc_vmsg(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...)
251 {
252 char buf[256];
253 va_list ap;
254
255 if (NULL == mdoc->cb.mdoc_msg)
256 return;
257
258 va_start(ap, fmt);
259 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
260 va_end(ap);
261 (*mdoc->cb.mdoc_msg)(mdoc->data, ln, pos, buf);
262 }
263
264
265 int
266 mdoc_verr(struct mdoc *mdoc, int ln, int pos,
267 const char *fmt, ...)
268 {
269 char buf[256];
270 va_list ap;
271
272 if (NULL == mdoc->cb.mdoc_err)
273 return(0);
274
275 va_start(ap, fmt);
276 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
277 va_end(ap);
278 return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf));
279 }
280
281
282 int
283 mdoc_vwarn(struct mdoc *mdoc, int ln, int pos,
284 enum mdoc_warn type, const char *fmt, ...)
285 {
286 char buf[256];
287 va_list ap;
288
289 if (NULL == mdoc->cb.mdoc_warn)
290 return(0);
291
292 va_start(ap, fmt);
293 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
294 va_end(ap);
295 return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, type, buf));
296 }
297
298
299 int
300 mdoc_macro(struct mdoc *mdoc, int tok,
301 int ln, int ppos, int *pos, char *buf)
302 {
303
304 assert(mdoc_macros[tok].fp);
305
306 if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
307 SEC_PROLOGUE != mdoc->lastnamed)
308 return(mdoc_perr(mdoc, ln, ppos, "macro disallowed in document body"));
309 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
310 SEC_PROLOGUE == mdoc->lastnamed)
311 return(mdoc_perr(mdoc, ln, ppos, "macro disallowed in document prologue"));
312 if (1 != ppos && ! (MDOC_CALLABLE & mdoc_macros[tok].flags))
313 return(mdoc_perr(mdoc, ln, ppos, "macro not callable"));
314 return((*mdoc_macros[tok].fp)(mdoc, tok, ln, ppos, pos, buf));
315 }
316
317
318 static int
319 mdoc_node_append(struct mdoc *mdoc, struct mdoc_node *p)
320 {
321
322 assert(mdoc->last);
323 assert(mdoc->first);
324 assert(MDOC_ROOT != p->type);
325
326 /* See if we exceed the suggest line-max. */
327
328 switch (p->type) {
329 case (MDOC_TEXT):
330 /* FALLTHROUGH */
331 case (MDOC_ELEM):
332 /* FALLTHROUGH */
333 case (MDOC_BLOCK):
334 mdoc->linetok++;
335 break;
336 default:
337 break;
338 }
339
340 /* This sort-of works (re-opening of text macros...). */
341 if (mdoc->linetok > MDOC_LINEARG_SOFTMAX)
342 if ( ! mdoc_nwarn(mdoc, p, WARN_COMPAT,
343 "suggested %d tokens per line exceeded (has %d)",
344 MDOC_LINEARG_SOFTMAX, mdoc->linetok))
345 return(0);
346
347 switch (mdoc->next) {
348 case (MDOC_NEXT_SIBLING):
349 mdoc->last->next = p;
350 p->prev = mdoc->last;
351 p->parent = mdoc->last->parent;
352 break;
353 case (MDOC_NEXT_CHILD):
354 mdoc->last->child = p;
355 p->parent = mdoc->last;
356 break;
357 default:
358 abort();
359 /* NOTREACHED */
360 }
361
362 if ( ! mdoc_valid_pre(mdoc, p))
363 return(0);
364
365 switch (p->type) {
366 case (MDOC_HEAD):
367 assert(MDOC_BLOCK == p->parent->type);
368 p->parent->data.block.head = p;
369 break;
370 case (MDOC_TAIL):
371 assert(MDOC_BLOCK == p->parent->type);
372 p->parent->data.block.tail = p;
373 break;
374 case (MDOC_BODY):
375 assert(MDOC_BLOCK == p->parent->type);
376 p->parent->data.block.body = p;
377 break;
378 default:
379 break;
380 }
381
382 mdoc->last = p;
383 return(1);
384 }
385
386
387 static struct mdoc_node *
388 mdoc_node_alloc(const struct mdoc *mdoc)
389 {
390 struct mdoc_node *p;
391
392 p = xcalloc(1, sizeof(struct mdoc_node));
393 p->sec = mdoc->lastsec;
394
395 return(p);
396 }
397
398
399 int
400 mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, int tok)
401 {
402 struct mdoc_node *p;
403
404 assert(mdoc->first);
405 assert(mdoc->last);
406
407 p = mdoc_node_alloc(mdoc);
408
409 p->line = line;
410 p->pos = pos;
411 p->type = MDOC_TAIL;
412 p->tok = tok;
413
414 return(mdoc_node_append(mdoc, p));
415 }
416
417
418 int
419 mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, int tok)
420 {
421 struct mdoc_node *p;
422
423 assert(mdoc->first);
424 assert(mdoc->last);
425
426 p = mdoc_node_alloc(mdoc);
427
428 p->line = line;
429 p->pos = pos;
430 p->type = MDOC_HEAD;
431 p->tok = tok;
432
433 return(mdoc_node_append(mdoc, p));
434 }
435
436
437 int
438 mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, int tok)
439 {
440 struct mdoc_node *p;
441
442 assert(mdoc->first);
443 assert(mdoc->last);
444
445 p = mdoc_node_alloc(mdoc);
446
447 p->line = line;
448 p->pos = pos;
449 p->type = MDOC_BODY;
450 p->tok = tok;
451
452 return(mdoc_node_append(mdoc, p));
453 }
454
455
456 int
457 mdoc_root_alloc(struct mdoc *mdoc)
458 {
459 struct mdoc_node *p;
460
461 p = mdoc_node_alloc(mdoc);
462
463 p->type = MDOC_ROOT;
464
465 return(mdoc_node_append(mdoc, p));
466 }
467
468
469 int
470 mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
471 int tok, size_t argsz, const struct mdoc_arg *args)
472 {
473 struct mdoc_node *p;
474
475 p = mdoc_node_alloc(mdoc);
476
477 p->pos = pos;
478 p->line = line;
479 p->type = MDOC_BLOCK;
480 p->tok = tok;
481 p->data.block.argc = argsz;
482 p->data.block.argv = argdup(argsz, args);
483
484 return(mdoc_node_append(mdoc, p));
485 }
486
487
488 int
489 mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
490 int tok, size_t argsz, const struct mdoc_arg *args)
491 {
492 struct mdoc_node *p;
493
494 p = mdoc_node_alloc(mdoc);
495
496 p->line = line;
497 p->pos = pos;
498 p->type = MDOC_ELEM;
499 p->tok = tok;
500 p->data.elem.argc = argsz;
501 p->data.elem.argv = argdup(argsz, args);
502
503 return(mdoc_node_append(mdoc, p));
504 }
505
506
507 int
508 mdoc_word_alloc(struct mdoc *mdoc,
509 int line, int pos, const char *word)
510 {
511 struct mdoc_node *p;
512
513 p = mdoc_node_alloc(mdoc);
514
515 p->line = line;
516 p->pos = pos;
517 p->type = MDOC_TEXT;
518 p->data.text.string = xstrdup(word);
519
520 return(mdoc_node_append(mdoc, p));
521 }
522
523
524 static void
525 argfree(size_t sz, struct mdoc_arg *p)
526 {
527 int i, j;
528
529 if (0 == sz)
530 return;
531
532 assert(p);
533 /* LINTED */
534 for (i = 0; i < (int)sz; i++)
535 if (p[i].sz > 0) {
536 assert(p[i].value);
537 /* LINTED */
538 for (j = 0; j < (int)p[i].sz; j++)
539 free(p[i].value[j]);
540 free(p[i].value);
541 }
542 free(p);
543 }
544
545
546 static void
547 mdoc_elem_free(struct mdoc_elem *p)
548 {
549
550 argfree(p->argc, p->argv);
551 }
552
553
554 static void
555 mdoc_block_free(struct mdoc_block *p)
556 {
557
558 argfree(p->argc, p->argv);
559 }
560
561
562 static void
563 mdoc_text_free(struct mdoc_text *p)
564 {
565
566 if (p->string)
567 free(p->string);
568 }
569
570
571 void
572 mdoc_node_free(struct mdoc_node *p)
573 {
574
575 switch (p->type) {
576 case (MDOC_TEXT):
577 mdoc_text_free(&p->data.text);
578 break;
579 case (MDOC_ELEM):
580 mdoc_elem_free(&p->data.elem);
581 break;
582 case (MDOC_BLOCK):
583 mdoc_block_free(&p->data.block);
584 break;
585 default:
586 break;
587 }
588
589 free(p);
590 }
591
592
593 void
594 mdoc_node_freelist(struct mdoc_node *p)
595 {
596
597 if (p->child)
598 mdoc_node_freelist(p->child);
599 if (p->next)
600 mdoc_node_freelist(p->next);
601
602 mdoc_node_free(p);
603 }
604
605
606 int
607 mdoc_find(const struct mdoc *mdoc, const char *key)
608 {
609
610 return(mdoc_tokhash_find(mdoc->htab, key));
611 }
612
613
614 static void
615 argcpy(struct mdoc_arg *dst, const struct mdoc_arg *src)
616 {
617 int i;
618
619 dst->line = src->line;
620 dst->pos = src->pos;
621 dst->arg = src->arg;
622 if (0 == (dst->sz = src->sz))
623 return;
624 dst->value = xcalloc(dst->sz, sizeof(char *));
625 for (i = 0; i < (int)dst->sz; i++)
626 dst->value[i] = xstrdup(src->value[i]);
627 }
628
629
630 static struct mdoc_arg *
631 argdup(size_t argsz, const struct mdoc_arg *args)
632 {
633 struct mdoc_arg *pp;
634 int i;
635
636 if (0 == argsz)
637 return(NULL);
638
639 pp = xcalloc((size_t)argsz, sizeof(struct mdoc_arg));
640 for (i = 0; i < (int)argsz; i++)
641 argcpy(&pp[i], &args[i]);
642
643 return(pp);
644 }
645