]>
git.cameronkatri.com Git - mandoc.git/blob - mdoc.c
1 /* $Id: mdoc.c,v 1.76 2009/06/10 20:18:43 kristaps Exp $ */
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
36 const char *const __mdoc_macronames
[MDOC_MAX
] = {
37 "\\\"", "Dd", "Dt", "Os",
38 "Sh", "Ss", "Pp", "D1",
39 "Dl", "Bd", "Ed", "Bl",
40 "El", "It", "Ad", "An",
41 "Ar", "Cd", "Cm", "Dv",
42 "Er", "Ev", "Ex", "Fa",
43 "Fd", "Fl", "Fn", "Ft",
44 "Ic", "In", "Li", "Nd",
45 "Nm", "Op", "Ot", "Pa",
46 "Rv", "St", "Va", "Vt",
48 "Xr", "\%A", "\%B", "\%D",
50 "\%I", "\%J", "\%N", "\%O",
52 "\%P", "\%R", "\%T", "\%V",
53 "Ac", "Ao", "Aq", "At",
54 "Bc", "Bf", "Bo", "Bq",
55 "Bsx", "Bx", "Db", "Dc",
56 "Do", "Dq", "Ec", "Ef",
57 "Em", "Eo", "Fx", "Ms",
58 "No", "Ns", "Nx", "Ox",
59 "Pc", "Pf", "Po", "Pq",
60 "Qc", "Ql", "Qo", "Qq",
61 "Re", "Rs", "Sc", "So",
62 "Sq", "Sm", "Sx", "Sy",
63 "Tn", "Ux", "Xc", "Xo",
64 "Fo", "Fc", "Oo", "Oc",
65 "Bk", "Ek", "Bt", "Hf",
66 "Fr", "Ud", "Lb", "Ap",
67 "Lp", "Lk", "Mt", "Brq",
69 "Bro", "Brc", "\%C", "Es",
74 const char *const __mdoc_argnames
[MDOC_ARG_MAX
] = {
75 "split", "nosplit", "ragged",
76 "unfilled", "literal", "file",
77 "offset", "bullet", "dash",
78 "hyphen", "item", "enum",
79 "tag", "diag", "hang",
80 "ohang", "inset", "column",
81 "width", "compact", "std",
82 "filled", "words", "emphasis",
86 const char * const *mdoc_macronames
= __mdoc_macronames
;
87 const char * const *mdoc_argnames
= __mdoc_argnames
;
89 static void mdoc_free1(struct mdoc
*);
90 static int mdoc_alloc1(struct mdoc
*);
91 static struct mdoc_node
*node_alloc(struct mdoc
*, int, int,
93 static int node_append(struct mdoc
*,
95 static int parsetext(struct mdoc
*, int, char *);
96 static int parsemacro(struct mdoc
*, int, char *);
97 static int macrowarn(struct mdoc
*, int, const char *);
98 static int perr(struct mdoc
*, int, int, enum merr
);
100 #define verr(m, t) perr((m), (m)->last->line, (m)->last->pos, (t))
103 * Get the first (root) node of the parse tree.
105 const struct mdoc_node
*
106 mdoc_node(const struct mdoc
*m
)
109 return(MDOC_HALT
& m
->flags
? NULL
: m
->first
);
113 const struct mdoc_meta
*
114 mdoc_meta(const struct mdoc
*m
)
117 return(MDOC_HALT
& m
->flags
? NULL
: &m
->meta
);
122 mdoc_free1(struct mdoc
*mdoc
)
126 mdoc_node_freelist(mdoc
->first
);
127 if (mdoc
->meta
.title
)
128 free(mdoc
->meta
.title
);
132 free(mdoc
->meta
.name
);
134 free(mdoc
->meta
.arch
);
136 free(mdoc
->meta
.vol
);
141 mdoc_alloc1(struct mdoc
*mdoc
)
144 bzero(&mdoc
->meta
, sizeof(struct mdoc_meta
));
146 mdoc
->lastnamed
= mdoc
->lastsec
= 0;
147 mdoc
->last
= calloc(1, sizeof(struct mdoc_node
));
148 if (NULL
== mdoc
->last
)
151 mdoc
->first
= mdoc
->last
;
152 mdoc
->last
->type
= MDOC_ROOT
;
153 mdoc
->next
= MDOC_NEXT_CHILD
;
159 * Free up all resources contributed by a parse: the node tree,
160 * meta-data and so on. Then reallocate the root node for another
164 mdoc_reset(struct mdoc
*mdoc
)
168 return(mdoc_alloc1(mdoc
));
173 * Completely free up all resources.
176 mdoc_free(struct mdoc
*mdoc
)
181 mdoc_hash_free(mdoc
->htab
);
187 mdoc_alloc(void *data
, int pflags
, const struct mdoc_cb
*cb
)
191 if (NULL
== (p
= calloc(1, sizeof(struct mdoc
))))
194 (void)memcpy(&p
->cb
, cb
, sizeof(struct mdoc_cb
));
199 if (NULL
== (p
->htab
= mdoc_hash_alloc())) {
202 } else if (mdoc_alloc1(p
))
211 * Climb back up the parse tree, validating open scopes. Mostly calls
212 * through to macro_end in macro.c.
215 mdoc_endparse(struct mdoc
*m
)
218 if (MDOC_HALT
& m
->flags
)
220 else if (mdoc_macroend(m
))
222 m
->flags
|= MDOC_HALT
;
228 * Main parse routine. Parses a single line -- really just hands off to
229 * the macro or text parser.
232 mdoc_parseln(struct mdoc
*m
, int ln
, char *buf
)
235 /* If in error-mode, then we parse no more. */
237 if (MDOC_HALT
& m
->flags
)
240 return('.' == *buf
? parsemacro(m
, ln
, buf
) :
241 parsetext(m
, ln
, buf
));
246 mdoc_vmsg(struct mdoc
*mdoc
, int ln
, int pos
, const char *fmt
, ...)
251 if (NULL
== mdoc
->cb
.mdoc_msg
)
255 (void)vsnprintf(buf
, sizeof(buf
) - 1, fmt
, ap
);
257 (*mdoc
->cb
.mdoc_msg
)(mdoc
->data
, ln
, pos
, buf
);
262 mdoc_verr(struct mdoc
*mdoc
, int ln
, int pos
,
263 const char *fmt
, ...)
268 if (NULL
== mdoc
->cb
.mdoc_err
)
272 (void)vsnprintf(buf
, sizeof(buf
) - 1, fmt
, ap
);
274 return((*mdoc
->cb
.mdoc_err
)(mdoc
->data
, ln
, pos
, buf
));
279 mdoc_vwarn(struct mdoc
*mdoc
, int ln
, int pos
,
280 enum mdoc_warn type
, const char *fmt
, ...)
285 if (NULL
== mdoc
->cb
.mdoc_warn
)
289 (void)vsnprintf(buf
, sizeof(buf
) - 1, fmt
, ap
);
291 return((*mdoc
->cb
.mdoc_warn
)(mdoc
->data
, ln
, pos
, type
, buf
));
296 mdoc_macro(struct mdoc
*m
, int tok
,
297 int ln
, int pp
, int *pos
, char *buf
)
300 /* FIXME - these should happen during validation. */
302 if (MDOC_PROLOGUE
& mdoc_macros
[tok
].flags
&&
303 SEC_PROLOGUE
!= m
->lastnamed
)
304 return(perr(m
, ln
, pp
, EPROLBODY
));
306 if ( ! (MDOC_PROLOGUE
& mdoc_macros
[tok
].flags
) &&
307 SEC_PROLOGUE
== m
->lastnamed
)
308 return(perr(m
, ln
, pp
, EBODYPROL
));
310 if (1 != pp
&& ! (MDOC_CALLABLE
& mdoc_macros
[tok
].flags
))
311 return(perr(m
, ln
, pp
, ENOCALL
));
313 return((*mdoc_macros
[tok
].fp
)(m
, tok
, ln
, pp
, pos
, buf
));
318 perr(struct mdoc
*m
, int line
, int pos
, enum merr type
)
328 p
= "macro disallowed in document body";
331 p
= "macro disallowed in document prologue";
334 p
= "memory exhausted";
337 p
= "text disallowed in document prologue";
340 p
= "blank lines disallowed in non-literal contexts";
343 p
= "whitespace disallowed after delimiter";
347 return(mdoc_perr(m
, line
, pos
, p
));
352 node_append(struct mdoc
*mdoc
, struct mdoc_node
*p
)
357 assert(MDOC_ROOT
!= p
->type
);
359 switch (mdoc
->next
) {
360 case (MDOC_NEXT_SIBLING
):
361 mdoc
->last
->next
= p
;
362 p
->prev
= mdoc
->last
;
363 p
->parent
= mdoc
->last
->parent
;
365 case (MDOC_NEXT_CHILD
):
366 mdoc
->last
->child
= p
;
367 p
->parent
= mdoc
->last
;
374 if ( ! mdoc_valid_pre(mdoc
, p
))
376 if ( ! mdoc_action_pre(mdoc
, p
))
381 assert(MDOC_BLOCK
== p
->parent
->type
);
385 assert(MDOC_BLOCK
== p
->parent
->type
);
389 assert(MDOC_BLOCK
== p
->parent
->type
);
400 if ( ! mdoc_valid_post(mdoc
))
402 if ( ! mdoc_action_post(mdoc
))
413 static struct mdoc_node
*
414 node_alloc(struct mdoc
*mdoc
, int line
,
415 int pos
, int tok
, enum mdoc_type type
)
419 if (NULL
== (p
= calloc(1, sizeof(struct mdoc_node
)))) {
420 (void)verr(mdoc
, EMALLOC
);
424 p
->sec
= mdoc
->lastsec
;
428 if (MDOC_TEXT
!= (p
->type
= type
))
436 mdoc_tail_alloc(struct mdoc
*mdoc
, int line
, int pos
, int tok
)
440 p
= node_alloc(mdoc
, line
, pos
, tok
, MDOC_TAIL
);
443 return(node_append(mdoc
, p
));
448 mdoc_head_alloc(struct mdoc
*mdoc
, int line
, int pos
, int tok
)
455 p
= node_alloc(mdoc
, line
, pos
, tok
, MDOC_HEAD
);
458 return(node_append(mdoc
, p
));
463 mdoc_body_alloc(struct mdoc
*mdoc
, int line
, int pos
, int tok
)
467 p
= node_alloc(mdoc
, line
, pos
, tok
, MDOC_BODY
);
470 return(node_append(mdoc
, p
));
475 mdoc_block_alloc(struct mdoc
*mdoc
, int line
, int pos
,
476 int tok
, struct mdoc_arg
*args
)
480 p
= node_alloc(mdoc
, line
, pos
, tok
, MDOC_BLOCK
);
483 if ((p
->args
= args
))
485 return(node_append(mdoc
, p
));
490 mdoc_elem_alloc(struct mdoc
*mdoc
, int line
, int pos
,
491 int tok
, struct mdoc_arg
*args
)
495 p
= node_alloc(mdoc
, line
, pos
, tok
, MDOC_ELEM
);
498 if ((p
->args
= args
))
500 return(node_append(mdoc
, p
));
505 mdoc_word_alloc(struct mdoc
*mdoc
,
506 int line
, int pos
, const char *word
)
510 p
= node_alloc(mdoc
, line
, pos
, -1, MDOC_TEXT
);
513 if (NULL
== (p
->string
= strdup(word
))) {
514 (void)verr(mdoc
, EMALLOC
);
517 return(node_append(mdoc
, p
));
522 mdoc_node_free(struct mdoc_node
*p
)
528 mdoc_argv_free(p
->args
);
534 mdoc_node_freelist(struct mdoc_node
*p
)
538 mdoc_node_freelist(p
->child
);
540 mdoc_node_freelist(p
->next
);
547 * Parse free-form text, that is, a line that does not begin with the
551 parsetext(struct mdoc
*m
, int line
, char *buf
)
554 if (SEC_PROLOGUE
== m
->lastnamed
)
555 return(perr(m
, line
, 0, ETEXTPROL
));
557 if (0 == buf
[0] && ! (MDOC_LITERAL
& m
->flags
))
558 return(perr(m
, line
, 0, ENOBLANK
));
560 if ( ! mdoc_word_alloc(m
, line
, 0, buf
))
563 m
->next
= MDOC_NEXT_SIBLING
;
569 macrowarn(struct mdoc
*m
, int ln
, const char *buf
)
571 if ( ! (MDOC_IGN_MACRO
& m
->pflags
))
572 return(mdoc_perr(m
, ln
, 1,
573 "unknown macro: %s%s",
574 buf
, strlen(buf
) > 3 ? "..." : ""));
575 return(mdoc_pwarn(m
, ln
, 1, WARN_SYNTAX
,
576 "unknown macro: %s%s",
577 buf
, strlen(buf
) > 3 ? "..." : ""));
583 * Parse a macro line, that is, a line beginning with the control
587 parsemacro(struct mdoc
*m
, int ln
, char *buf
)
592 /* Comments and empties are quickly ignored. */
599 while (buf
[i
] && ' ' == buf
[i
])
603 return(perr(m
, ln
, 1, ESPACE
));
606 if (buf
[1] && '\\' == buf
[1])
607 if (buf
[2] && '\"' == buf
[2])
610 /* Copy the first word into a nil-terminated buffer. */
612 for (i
= 1; i
< 5; i
++) {
613 if (0 == (mac
[i
- 1] = buf
[i
]))
615 else if (' ' == buf
[i
])
621 if (i
== 5 || i
<= 2) {
622 if ( ! macrowarn(m
, ln
, mac
))
627 if (MDOC_MAX
== (c
= mdoc_hash_find(m
->htab
, mac
))) {
628 if ( ! macrowarn(m
, ln
, mac
))
633 /* The macro is sane. Jump to the next word. */
635 while (buf
[i
] && ' ' == buf
[i
])
638 /* Begin recursive parse sequence. */
640 if ( ! mdoc_macro(m
, c
, ln
, 1, &i
, buf
))
645 err
: /* Error out. */
647 m
->flags
|= MDOC_HALT
;