1*0a6a1f1dSLionel Sambuc /* Id: mdoc.c,v 1.207 2013/12/31 23:23:11 schwarze Exp */
2d65f6f70SBen Gras /*
392395e9cSLionel Sambuc * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*0a6a1f1dSLionel Sambuc * Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
5d65f6f70SBen Gras *
6d65f6f70SBen Gras * Permission to use, copy, modify, and distribute this software for any
7d65f6f70SBen Gras * purpose with or without fee is hereby granted, provided that the above
8d65f6f70SBen Gras * copyright notice and this permission notice appear in all copies.
9d65f6f70SBen Gras *
10d65f6f70SBen Gras * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d65f6f70SBen Gras * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d65f6f70SBen Gras * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d65f6f70SBen Gras * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d65f6f70SBen Gras * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d65f6f70SBen Gras * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d65f6f70SBen Gras * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d65f6f70SBen Gras */
18d65f6f70SBen Gras #ifdef HAVE_CONFIG_H
19d65f6f70SBen Gras #include "config.h"
20d65f6f70SBen Gras #endif
21d65f6f70SBen Gras
22d65f6f70SBen Gras #include <sys/types.h>
23d65f6f70SBen Gras
24d65f6f70SBen Gras #include <assert.h>
25d65f6f70SBen Gras #include <stdarg.h>
26d65f6f70SBen Gras #include <stdio.h>
27d65f6f70SBen Gras #include <stdlib.h>
28d65f6f70SBen Gras #include <string.h>
29d65f6f70SBen Gras #include <time.h>
30d65f6f70SBen Gras
3192395e9cSLionel Sambuc #include "mdoc.h"
32d65f6f70SBen Gras #include "mandoc.h"
33d65f6f70SBen Gras #include "libmdoc.h"
34d65f6f70SBen Gras #include "libmandoc.h"
35d65f6f70SBen Gras
36d65f6f70SBen Gras const char *const __mdoc_macronames[MDOC_MAX] = {
37d65f6f70SBen Gras "Ap", "Dd", "Dt", "Os",
38d65f6f70SBen Gras "Sh", "Ss", "Pp", "D1",
39d65f6f70SBen Gras "Dl", "Bd", "Ed", "Bl",
40d65f6f70SBen Gras "El", "It", "Ad", "An",
41d65f6f70SBen Gras "Ar", "Cd", "Cm", "Dv",
42d65f6f70SBen Gras "Er", "Ev", "Ex", "Fa",
43d65f6f70SBen Gras "Fd", "Fl", "Fn", "Ft",
44d65f6f70SBen Gras "Ic", "In", "Li", "Nd",
45d65f6f70SBen Gras "Nm", "Op", "Ot", "Pa",
46d65f6f70SBen Gras "Rv", "St", "Va", "Vt",
47d65f6f70SBen Gras /* LINTED */
48d65f6f70SBen Gras "Xr", "%A", "%B", "%D",
49d65f6f70SBen Gras /* LINTED */
50d65f6f70SBen Gras "%I", "%J", "%N", "%O",
51d65f6f70SBen Gras /* LINTED */
52d65f6f70SBen Gras "%P", "%R", "%T", "%V",
53d65f6f70SBen Gras "Ac", "Ao", "Aq", "At",
54d65f6f70SBen Gras "Bc", "Bf", "Bo", "Bq",
55d65f6f70SBen Gras "Bsx", "Bx", "Db", "Dc",
56d65f6f70SBen Gras "Do", "Dq", "Ec", "Ef",
57d65f6f70SBen Gras "Em", "Eo", "Fx", "Ms",
58d65f6f70SBen Gras "No", "Ns", "Nx", "Ox",
59d65f6f70SBen Gras "Pc", "Pf", "Po", "Pq",
60d65f6f70SBen Gras "Qc", "Ql", "Qo", "Qq",
61d65f6f70SBen Gras "Re", "Rs", "Sc", "So",
62d65f6f70SBen Gras "Sq", "Sm", "Sx", "Sy",
63d65f6f70SBen Gras "Tn", "Ux", "Xc", "Xo",
64d65f6f70SBen Gras "Fo", "Fc", "Oo", "Oc",
65d65f6f70SBen Gras "Bk", "Ek", "Bt", "Hf",
66d65f6f70SBen Gras "Fr", "Ud", "Lb", "Lp",
67d65f6f70SBen Gras "Lk", "Mt", "Brq", "Bro",
68d65f6f70SBen Gras /* LINTED */
69d65f6f70SBen Gras "Brc", "%C", "Es", "En",
70d65f6f70SBen Gras /* LINTED */
71d65f6f70SBen Gras "Dx", "%Q", "br", "sp",
72d65f6f70SBen Gras /* LINTED */
73d65f6f70SBen Gras "%U", "Ta"
74d65f6f70SBen Gras };
75d65f6f70SBen Gras
76d65f6f70SBen Gras const char *const __mdoc_argnames[MDOC_ARG_MAX] = {
77d65f6f70SBen Gras "split", "nosplit", "ragged",
78d65f6f70SBen Gras "unfilled", "literal", "file",
79d65f6f70SBen Gras "offset", "bullet", "dash",
80d65f6f70SBen Gras "hyphen", "item", "enum",
81d65f6f70SBen Gras "tag", "diag", "hang",
82d65f6f70SBen Gras "ohang", "inset", "column",
83d65f6f70SBen Gras "width", "compact", "std",
84d65f6f70SBen Gras "filled", "words", "emphasis",
85d65f6f70SBen Gras "symbolic", "nested", "centered"
86d65f6f70SBen Gras };
87d65f6f70SBen Gras
88d65f6f70SBen Gras const char * const *mdoc_macronames = __mdoc_macronames;
89d65f6f70SBen Gras const char * const *mdoc_argnames = __mdoc_argnames;
90d65f6f70SBen Gras
91d65f6f70SBen Gras static void mdoc_node_free(struct mdoc_node *);
92d65f6f70SBen Gras static void mdoc_node_unlink(struct mdoc *,
93d65f6f70SBen Gras struct mdoc_node *);
94d65f6f70SBen Gras static void mdoc_free1(struct mdoc *);
95d65f6f70SBen Gras static void mdoc_alloc1(struct mdoc *);
96d65f6f70SBen Gras static struct mdoc_node *node_alloc(struct mdoc *, int, int,
97d65f6f70SBen Gras enum mdoct, enum mdoc_type);
98d65f6f70SBen Gras static int node_append(struct mdoc *,
99d65f6f70SBen Gras struct mdoc_node *);
10092395e9cSLionel Sambuc #if 0
10192395e9cSLionel Sambuc static int mdoc_preptext(struct mdoc *, int, char *, int);
10292395e9cSLionel Sambuc #endif
103d65f6f70SBen Gras static int mdoc_ptext(struct mdoc *, int, char *, int);
104d65f6f70SBen Gras static int mdoc_pmacro(struct mdoc *, int, char *, int);
105d65f6f70SBen Gras
106d65f6f70SBen Gras const struct mdoc_node *
mdoc_node(const struct mdoc * mdoc)107*0a6a1f1dSLionel Sambuc mdoc_node(const struct mdoc *mdoc)
108d65f6f70SBen Gras {
109d65f6f70SBen Gras
110*0a6a1f1dSLionel Sambuc assert( ! (MDOC_HALT & mdoc->flags));
111*0a6a1f1dSLionel Sambuc return(mdoc->first);
112d65f6f70SBen Gras }
113d65f6f70SBen Gras
114d65f6f70SBen Gras
115d65f6f70SBen Gras const struct mdoc_meta *
mdoc_meta(const struct mdoc * mdoc)116*0a6a1f1dSLionel Sambuc mdoc_meta(const struct mdoc *mdoc)
117d65f6f70SBen Gras {
118d65f6f70SBen Gras
119*0a6a1f1dSLionel Sambuc assert( ! (MDOC_HALT & mdoc->flags));
120*0a6a1f1dSLionel Sambuc return(&mdoc->meta);
121d65f6f70SBen Gras }
122d65f6f70SBen Gras
123d65f6f70SBen Gras
124d65f6f70SBen Gras /*
125d65f6f70SBen Gras * Frees volatile resources (parse tree, meta-data, fields).
126d65f6f70SBen Gras */
127d65f6f70SBen Gras static void
mdoc_free1(struct mdoc * mdoc)128d65f6f70SBen Gras mdoc_free1(struct mdoc *mdoc)
129d65f6f70SBen Gras {
130d65f6f70SBen Gras
131d65f6f70SBen Gras if (mdoc->first)
132d65f6f70SBen Gras mdoc_node_delete(mdoc, mdoc->first);
133d65f6f70SBen Gras if (mdoc->meta.title)
134d65f6f70SBen Gras free(mdoc->meta.title);
135d65f6f70SBen Gras if (mdoc->meta.os)
136d65f6f70SBen Gras free(mdoc->meta.os);
137d65f6f70SBen Gras if (mdoc->meta.name)
138d65f6f70SBen Gras free(mdoc->meta.name);
139d65f6f70SBen Gras if (mdoc->meta.arch)
140d65f6f70SBen Gras free(mdoc->meta.arch);
141d65f6f70SBen Gras if (mdoc->meta.vol)
142d65f6f70SBen Gras free(mdoc->meta.vol);
143d65f6f70SBen Gras if (mdoc->meta.msec)
144d65f6f70SBen Gras free(mdoc->meta.msec);
14592395e9cSLionel Sambuc if (mdoc->meta.date)
14692395e9cSLionel Sambuc free(mdoc->meta.date);
147d65f6f70SBen Gras }
148d65f6f70SBen Gras
149d65f6f70SBen Gras
150d65f6f70SBen Gras /*
151d65f6f70SBen Gras * Allocate all volatile resources (parse tree, meta-data, fields).
152d65f6f70SBen Gras */
153d65f6f70SBen Gras static void
mdoc_alloc1(struct mdoc * mdoc)154d65f6f70SBen Gras mdoc_alloc1(struct mdoc *mdoc)
155d65f6f70SBen Gras {
156d65f6f70SBen Gras
157d65f6f70SBen Gras memset(&mdoc->meta, 0, sizeof(struct mdoc_meta));
158d65f6f70SBen Gras mdoc->flags = 0;
159d65f6f70SBen Gras mdoc->lastnamed = mdoc->lastsec = SEC_NONE;
160d65f6f70SBen Gras mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node));
161d65f6f70SBen Gras mdoc->first = mdoc->last;
162d65f6f70SBen Gras mdoc->last->type = MDOC_ROOT;
16392395e9cSLionel Sambuc mdoc->last->tok = MDOC_MAX;
164d65f6f70SBen Gras mdoc->next = MDOC_NEXT_CHILD;
165d65f6f70SBen Gras }
166d65f6f70SBen Gras
167d65f6f70SBen Gras
168d65f6f70SBen Gras /*
169d65f6f70SBen Gras * Free up volatile resources (see mdoc_free1()) then re-initialises the
170d65f6f70SBen Gras * data with mdoc_alloc1(). After invocation, parse data has been reset
171d65f6f70SBen Gras * and the parser is ready for re-invocation on a new tree; however,
172d65f6f70SBen Gras * cross-parse non-volatile data is kept intact.
173d65f6f70SBen Gras */
174d65f6f70SBen Gras void
mdoc_reset(struct mdoc * mdoc)175d65f6f70SBen Gras mdoc_reset(struct mdoc *mdoc)
176d65f6f70SBen Gras {
177d65f6f70SBen Gras
178d65f6f70SBen Gras mdoc_free1(mdoc);
179d65f6f70SBen Gras mdoc_alloc1(mdoc);
180d65f6f70SBen Gras }
181d65f6f70SBen Gras
182d65f6f70SBen Gras
183d65f6f70SBen Gras /*
184d65f6f70SBen Gras * Completely free up all volatile and non-volatile parse resources.
185d65f6f70SBen Gras * After invocation, the pointer is no longer usable.
186d65f6f70SBen Gras */
187d65f6f70SBen Gras void
mdoc_free(struct mdoc * mdoc)188d65f6f70SBen Gras mdoc_free(struct mdoc *mdoc)
189d65f6f70SBen Gras {
190d65f6f70SBen Gras
191d65f6f70SBen Gras mdoc_free1(mdoc);
192d65f6f70SBen Gras free(mdoc);
193d65f6f70SBen Gras }
194d65f6f70SBen Gras
195d65f6f70SBen Gras
196d65f6f70SBen Gras /*
197d65f6f70SBen Gras * Allocate volatile and non-volatile parse resources.
198d65f6f70SBen Gras */
199d65f6f70SBen Gras struct mdoc *
mdoc_alloc(struct roff * roff,struct mparse * parse,char * defos)200*0a6a1f1dSLionel Sambuc mdoc_alloc(struct roff *roff, struct mparse *parse, char *defos)
201d65f6f70SBen Gras {
202d65f6f70SBen Gras struct mdoc *p;
203d65f6f70SBen Gras
204d65f6f70SBen Gras p = mandoc_calloc(1, sizeof(struct mdoc));
205d65f6f70SBen Gras
20692395e9cSLionel Sambuc p->parse = parse;
207*0a6a1f1dSLionel Sambuc p->defos = defos;
20892395e9cSLionel Sambuc p->roff = roff;
209d65f6f70SBen Gras
210d65f6f70SBen Gras mdoc_hash_init();
211d65f6f70SBen Gras mdoc_alloc1(p);
212d65f6f70SBen Gras return(p);
213d65f6f70SBen Gras }
214d65f6f70SBen Gras
215d65f6f70SBen Gras
216d65f6f70SBen Gras /*
217d65f6f70SBen Gras * Climb back up the parse tree, validating open scopes. Mostly calls
218d65f6f70SBen Gras * through to macro_end() in macro.c.
219d65f6f70SBen Gras */
220d65f6f70SBen Gras int
mdoc_endparse(struct mdoc * mdoc)221*0a6a1f1dSLionel Sambuc mdoc_endparse(struct mdoc *mdoc)
222d65f6f70SBen Gras {
223d65f6f70SBen Gras
224*0a6a1f1dSLionel Sambuc assert( ! (MDOC_HALT & mdoc->flags));
225*0a6a1f1dSLionel Sambuc if (mdoc_macroend(mdoc))
226d65f6f70SBen Gras return(1);
227*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_HALT;
228d65f6f70SBen Gras return(0);
229d65f6f70SBen Gras }
230d65f6f70SBen Gras
231d65f6f70SBen Gras int
mdoc_addeqn(struct mdoc * mdoc,const struct eqn * ep)232*0a6a1f1dSLionel Sambuc mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep)
233d65f6f70SBen Gras {
23492395e9cSLionel Sambuc struct mdoc_node *n;
235d65f6f70SBen Gras
236*0a6a1f1dSLionel Sambuc assert( ! (MDOC_HALT & mdoc->flags));
237d65f6f70SBen Gras
238d65f6f70SBen Gras /* No text before an initial macro. */
239d65f6f70SBen Gras
240*0a6a1f1dSLionel Sambuc if (SEC_NONE == mdoc->lastnamed) {
241*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, ep->ln, ep->pos, MANDOCERR_NOTEXT);
242d65f6f70SBen Gras return(1);
243d65f6f70SBen Gras }
244d65f6f70SBen Gras
245*0a6a1f1dSLionel Sambuc n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
24692395e9cSLionel Sambuc n->eqn = ep;
24792395e9cSLionel Sambuc
248*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, n))
24992395e9cSLionel Sambuc return(0);
25092395e9cSLionel Sambuc
251*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_SIBLING;
25292395e9cSLionel Sambuc return(1);
25392395e9cSLionel Sambuc }
25492395e9cSLionel Sambuc
25592395e9cSLionel Sambuc int
mdoc_addspan(struct mdoc * mdoc,const struct tbl_span * sp)256*0a6a1f1dSLionel Sambuc mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp)
25792395e9cSLionel Sambuc {
25892395e9cSLionel Sambuc struct mdoc_node *n;
25992395e9cSLionel Sambuc
260*0a6a1f1dSLionel Sambuc assert( ! (MDOC_HALT & mdoc->flags));
26192395e9cSLionel Sambuc
26292395e9cSLionel Sambuc /* No text before an initial macro. */
26392395e9cSLionel Sambuc
264*0a6a1f1dSLionel Sambuc if (SEC_NONE == mdoc->lastnamed) {
265*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, sp->line, 0, MANDOCERR_NOTEXT);
26692395e9cSLionel Sambuc return(1);
26792395e9cSLionel Sambuc }
26892395e9cSLionel Sambuc
269*0a6a1f1dSLionel Sambuc n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL);
27092395e9cSLionel Sambuc n->span = sp;
27192395e9cSLionel Sambuc
272*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, n))
27392395e9cSLionel Sambuc return(0);
27492395e9cSLionel Sambuc
275*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_SIBLING;
27692395e9cSLionel Sambuc return(1);
277d65f6f70SBen Gras }
278d65f6f70SBen Gras
279d65f6f70SBen Gras
280d65f6f70SBen Gras /*
281d65f6f70SBen Gras * Main parse routine. Parses a single line -- really just hands off to
282d65f6f70SBen Gras * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
283d65f6f70SBen Gras */
284d65f6f70SBen Gras int
mdoc_parseln(struct mdoc * mdoc,int ln,char * buf,int offs)285*0a6a1f1dSLionel Sambuc mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs)
286d65f6f70SBen Gras {
287d65f6f70SBen Gras
288*0a6a1f1dSLionel Sambuc assert( ! (MDOC_HALT & mdoc->flags));
289d65f6f70SBen Gras
290*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_NEWLINE;
291d65f6f70SBen Gras
292d65f6f70SBen Gras /*
293d65f6f70SBen Gras * Let the roff nS register switch SYNOPSIS mode early,
294d65f6f70SBen Gras * such that the parser knows at all times
295d65f6f70SBen Gras * whether this mode is on or off.
296d65f6f70SBen Gras * Note that this mode is also switched by the Sh macro.
297d65f6f70SBen Gras */
298*0a6a1f1dSLionel Sambuc if (roff_getreg(mdoc->roff, "nS"))
299*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_SYNOPSIS;
300d65f6f70SBen Gras else
301*0a6a1f1dSLionel Sambuc mdoc->flags &= ~MDOC_SYNOPSIS;
302d65f6f70SBen Gras
303*0a6a1f1dSLionel Sambuc return(roff_getcontrol(mdoc->roff, buf, &offs) ?
304*0a6a1f1dSLionel Sambuc mdoc_pmacro(mdoc, ln, buf, offs) :
305*0a6a1f1dSLionel Sambuc mdoc_ptext(mdoc, ln, buf, offs));
306d65f6f70SBen Gras }
307d65f6f70SBen Gras
308d65f6f70SBen Gras int
mdoc_macro(MACRO_PROT_ARGS)309d65f6f70SBen Gras mdoc_macro(MACRO_PROT_ARGS)
310d65f6f70SBen Gras {
311d65f6f70SBen Gras assert(tok < MDOC_MAX);
312d65f6f70SBen Gras
313d65f6f70SBen Gras /* If we're in the body, deny prologue calls. */
314d65f6f70SBen Gras
315d65f6f70SBen Gras if (MDOC_PROLOGUE & mdoc_macros[tok].flags &&
316*0a6a1f1dSLionel Sambuc MDOC_PBODY & mdoc->flags) {
317*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADBODY);
318d65f6f70SBen Gras return(1);
319d65f6f70SBen Gras }
320d65f6f70SBen Gras
321d65f6f70SBen Gras /* If we're in the prologue, deny "body" macros. */
322d65f6f70SBen Gras
323d65f6f70SBen Gras if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) &&
324*0a6a1f1dSLionel Sambuc ! (MDOC_PBODY & mdoc->flags)) {
325*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADPROLOG);
326*0a6a1f1dSLionel Sambuc if (NULL == mdoc->meta.msec)
327*0a6a1f1dSLionel Sambuc mdoc->meta.msec = mandoc_strdup("1");
328*0a6a1f1dSLionel Sambuc if (NULL == mdoc->meta.title)
329*0a6a1f1dSLionel Sambuc mdoc->meta.title = mandoc_strdup("UNKNOWN");
330*0a6a1f1dSLionel Sambuc if (NULL == mdoc->meta.vol)
331*0a6a1f1dSLionel Sambuc mdoc->meta.vol = mandoc_strdup("LOCAL");
332*0a6a1f1dSLionel Sambuc if (NULL == mdoc->meta.os)
333*0a6a1f1dSLionel Sambuc mdoc->meta.os = mandoc_strdup("LOCAL");
334*0a6a1f1dSLionel Sambuc if (NULL == mdoc->meta.date)
335*0a6a1f1dSLionel Sambuc mdoc->meta.date = mandoc_normdate
336*0a6a1f1dSLionel Sambuc (mdoc->parse, NULL, line, ppos);
337*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_PBODY;
338d65f6f70SBen Gras }
339d65f6f70SBen Gras
340*0a6a1f1dSLionel Sambuc return((*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf));
341d65f6f70SBen Gras }
342d65f6f70SBen Gras
343d65f6f70SBen Gras
344d65f6f70SBen Gras static int
node_append(struct mdoc * mdoc,struct mdoc_node * p)345d65f6f70SBen Gras node_append(struct mdoc *mdoc, struct mdoc_node *p)
346d65f6f70SBen Gras {
347d65f6f70SBen Gras
348d65f6f70SBen Gras assert(mdoc->last);
349d65f6f70SBen Gras assert(mdoc->first);
350d65f6f70SBen Gras assert(MDOC_ROOT != p->type);
351d65f6f70SBen Gras
352d65f6f70SBen Gras switch (mdoc->next) {
353d65f6f70SBen Gras case (MDOC_NEXT_SIBLING):
354d65f6f70SBen Gras mdoc->last->next = p;
355d65f6f70SBen Gras p->prev = mdoc->last;
356d65f6f70SBen Gras p->parent = mdoc->last->parent;
357d65f6f70SBen Gras break;
358d65f6f70SBen Gras case (MDOC_NEXT_CHILD):
359d65f6f70SBen Gras mdoc->last->child = p;
360d65f6f70SBen Gras p->parent = mdoc->last;
361d65f6f70SBen Gras break;
362d65f6f70SBen Gras default:
363d65f6f70SBen Gras abort();
364d65f6f70SBen Gras /* NOTREACHED */
365d65f6f70SBen Gras }
366d65f6f70SBen Gras
367d65f6f70SBen Gras p->parent->nchild++;
368d65f6f70SBen Gras
369d65f6f70SBen Gras /*
370d65f6f70SBen Gras * Copy over the normalised-data pointer of our parent. Not
371d65f6f70SBen Gras * everybody has one, but copying a null pointer is fine.
372d65f6f70SBen Gras */
373d65f6f70SBen Gras
374d65f6f70SBen Gras switch (p->type) {
375d65f6f70SBen Gras case (MDOC_BODY):
376*0a6a1f1dSLionel Sambuc if (ENDBODY_NOT != p->end)
377*0a6a1f1dSLionel Sambuc break;
378d65f6f70SBen Gras /* FALLTHROUGH */
379d65f6f70SBen Gras case (MDOC_TAIL):
380d65f6f70SBen Gras /* FALLTHROUGH */
381d65f6f70SBen Gras case (MDOC_HEAD):
382d65f6f70SBen Gras p->norm = p->parent->norm;
383d65f6f70SBen Gras break;
384d65f6f70SBen Gras default:
385d65f6f70SBen Gras break;
386d65f6f70SBen Gras }
387d65f6f70SBen Gras
388d65f6f70SBen Gras if ( ! mdoc_valid_pre(mdoc, p))
389d65f6f70SBen Gras return(0);
390d65f6f70SBen Gras
391d65f6f70SBen Gras switch (p->type) {
392d65f6f70SBen Gras case (MDOC_HEAD):
393d65f6f70SBen Gras assert(MDOC_BLOCK == p->parent->type);
394d65f6f70SBen Gras p->parent->head = p;
395d65f6f70SBen Gras break;
396d65f6f70SBen Gras case (MDOC_TAIL):
397d65f6f70SBen Gras assert(MDOC_BLOCK == p->parent->type);
398d65f6f70SBen Gras p->parent->tail = p;
399d65f6f70SBen Gras break;
400d65f6f70SBen Gras case (MDOC_BODY):
401d65f6f70SBen Gras if (p->end)
402d65f6f70SBen Gras break;
403d65f6f70SBen Gras assert(MDOC_BLOCK == p->parent->type);
404d65f6f70SBen Gras p->parent->body = p;
405d65f6f70SBen Gras break;
406d65f6f70SBen Gras default:
407d65f6f70SBen Gras break;
408d65f6f70SBen Gras }
409d65f6f70SBen Gras
410d65f6f70SBen Gras mdoc->last = p;
411d65f6f70SBen Gras
412d65f6f70SBen Gras switch (p->type) {
413d65f6f70SBen Gras case (MDOC_TBL):
414d65f6f70SBen Gras /* FALLTHROUGH */
415d65f6f70SBen Gras case (MDOC_TEXT):
416d65f6f70SBen Gras if ( ! mdoc_valid_post(mdoc))
417d65f6f70SBen Gras return(0);
418d65f6f70SBen Gras break;
419d65f6f70SBen Gras default:
420d65f6f70SBen Gras break;
421d65f6f70SBen Gras }
422d65f6f70SBen Gras
423d65f6f70SBen Gras return(1);
424d65f6f70SBen Gras }
425d65f6f70SBen Gras
426d65f6f70SBen Gras
427d65f6f70SBen Gras static struct mdoc_node *
node_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,enum mdoc_type type)428*0a6a1f1dSLionel Sambuc node_alloc(struct mdoc *mdoc, int line, int pos,
429d65f6f70SBen Gras enum mdoct tok, enum mdoc_type type)
430d65f6f70SBen Gras {
431d65f6f70SBen Gras struct mdoc_node *p;
432d65f6f70SBen Gras
433d65f6f70SBen Gras p = mandoc_calloc(1, sizeof(struct mdoc_node));
434*0a6a1f1dSLionel Sambuc p->sec = mdoc->lastsec;
435d65f6f70SBen Gras p->line = line;
436d65f6f70SBen Gras p->pos = pos;
437*0a6a1f1dSLionel Sambuc p->lastline = line;
438d65f6f70SBen Gras p->tok = tok;
439d65f6f70SBen Gras p->type = type;
440d65f6f70SBen Gras
441d65f6f70SBen Gras /* Flag analysis. */
442d65f6f70SBen Gras
443*0a6a1f1dSLionel Sambuc if (MDOC_SYNOPSIS & mdoc->flags)
444d65f6f70SBen Gras p->flags |= MDOC_SYNPRETTY;
445d65f6f70SBen Gras else
446d65f6f70SBen Gras p->flags &= ~MDOC_SYNPRETTY;
447*0a6a1f1dSLionel Sambuc if (MDOC_NEWLINE & mdoc->flags)
448d65f6f70SBen Gras p->flags |= MDOC_LINE;
449*0a6a1f1dSLionel Sambuc mdoc->flags &= ~MDOC_NEWLINE;
450d65f6f70SBen Gras
451d65f6f70SBen Gras return(p);
452d65f6f70SBen Gras }
453d65f6f70SBen Gras
454d65f6f70SBen Gras
455d65f6f70SBen Gras int
mdoc_tail_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok)456*0a6a1f1dSLionel Sambuc mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
457d65f6f70SBen Gras {
458d65f6f70SBen Gras struct mdoc_node *p;
459d65f6f70SBen Gras
460*0a6a1f1dSLionel Sambuc p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL);
461*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, p))
462d65f6f70SBen Gras return(0);
463*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_CHILD;
464d65f6f70SBen Gras return(1);
465d65f6f70SBen Gras }
466d65f6f70SBen Gras
467d65f6f70SBen Gras
468d65f6f70SBen Gras int
mdoc_head_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok)469*0a6a1f1dSLionel Sambuc mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
470d65f6f70SBen Gras {
471d65f6f70SBen Gras struct mdoc_node *p;
472d65f6f70SBen Gras
473*0a6a1f1dSLionel Sambuc assert(mdoc->first);
474*0a6a1f1dSLionel Sambuc assert(mdoc->last);
475d65f6f70SBen Gras
476*0a6a1f1dSLionel Sambuc p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD);
477*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, p))
478d65f6f70SBen Gras return(0);
479*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_CHILD;
480d65f6f70SBen Gras return(1);
481d65f6f70SBen Gras }
482d65f6f70SBen Gras
483d65f6f70SBen Gras
484d65f6f70SBen Gras int
mdoc_body_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok)485*0a6a1f1dSLionel Sambuc mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
486d65f6f70SBen Gras {
487d65f6f70SBen Gras struct mdoc_node *p;
488d65f6f70SBen Gras
489*0a6a1f1dSLionel Sambuc p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
490*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, p))
491d65f6f70SBen Gras return(0);
492*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_CHILD;
493d65f6f70SBen Gras return(1);
494d65f6f70SBen Gras }
495d65f6f70SBen Gras
496d65f6f70SBen Gras
497d65f6f70SBen Gras int
mdoc_endbody_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,struct mdoc_node * body,enum mdoc_endbody end)498*0a6a1f1dSLionel Sambuc mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok,
499d65f6f70SBen Gras struct mdoc_node *body, enum mdoc_endbody end)
500d65f6f70SBen Gras {
501d65f6f70SBen Gras struct mdoc_node *p;
502d65f6f70SBen Gras
503*0a6a1f1dSLionel Sambuc p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
504d65f6f70SBen Gras p->pending = body;
505*0a6a1f1dSLionel Sambuc p->norm = body->norm;
506d65f6f70SBen Gras p->end = end;
507*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, p))
508d65f6f70SBen Gras return(0);
509*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_SIBLING;
510d65f6f70SBen Gras return(1);
511d65f6f70SBen Gras }
512d65f6f70SBen Gras
513d65f6f70SBen Gras
514d65f6f70SBen Gras int
mdoc_block_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,struct mdoc_arg * args)515*0a6a1f1dSLionel Sambuc mdoc_block_alloc(struct mdoc *mdoc, int line, int pos,
516d65f6f70SBen Gras enum mdoct tok, struct mdoc_arg *args)
517d65f6f70SBen Gras {
518d65f6f70SBen Gras struct mdoc_node *p;
519d65f6f70SBen Gras
520*0a6a1f1dSLionel Sambuc p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK);
521d65f6f70SBen Gras p->args = args;
522d65f6f70SBen Gras if (p->args)
523d65f6f70SBen Gras (args->refcnt)++;
524d65f6f70SBen Gras
525d65f6f70SBen Gras switch (tok) {
526d65f6f70SBen Gras case (MDOC_Bd):
527d65f6f70SBen Gras /* FALLTHROUGH */
528d65f6f70SBen Gras case (MDOC_Bf):
529d65f6f70SBen Gras /* FALLTHROUGH */
530d65f6f70SBen Gras case (MDOC_Bl):
531d65f6f70SBen Gras /* FALLTHROUGH */
532d65f6f70SBen Gras case (MDOC_Rs):
533d65f6f70SBen Gras p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
534d65f6f70SBen Gras break;
535d65f6f70SBen Gras default:
536d65f6f70SBen Gras break;
537d65f6f70SBen Gras }
538d65f6f70SBen Gras
539*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, p))
540d65f6f70SBen Gras return(0);
541*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_CHILD;
542d65f6f70SBen Gras return(1);
543d65f6f70SBen Gras }
544d65f6f70SBen Gras
545d65f6f70SBen Gras
546d65f6f70SBen Gras int
mdoc_elem_alloc(struct mdoc * mdoc,int line,int pos,enum mdoct tok,struct mdoc_arg * args)547*0a6a1f1dSLionel Sambuc mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos,
548d65f6f70SBen Gras enum mdoct tok, struct mdoc_arg *args)
549d65f6f70SBen Gras {
550d65f6f70SBen Gras struct mdoc_node *p;
551d65f6f70SBen Gras
552*0a6a1f1dSLionel Sambuc p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM);
553d65f6f70SBen Gras p->args = args;
554d65f6f70SBen Gras if (p->args)
555d65f6f70SBen Gras (args->refcnt)++;
556d65f6f70SBen Gras
557d65f6f70SBen Gras switch (tok) {
558d65f6f70SBen Gras case (MDOC_An):
559d65f6f70SBen Gras p->norm = mandoc_calloc(1, sizeof(union mdoc_data));
560d65f6f70SBen Gras break;
561d65f6f70SBen Gras default:
562d65f6f70SBen Gras break;
563d65f6f70SBen Gras }
564d65f6f70SBen Gras
565*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, p))
566d65f6f70SBen Gras return(0);
567*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_CHILD;
568d65f6f70SBen Gras return(1);
569d65f6f70SBen Gras }
570d65f6f70SBen Gras
571d65f6f70SBen Gras int
mdoc_word_alloc(struct mdoc * mdoc,int line,int pos,const char * p)572*0a6a1f1dSLionel Sambuc mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p)
573d65f6f70SBen Gras {
574d65f6f70SBen Gras struct mdoc_node *n;
575d65f6f70SBen Gras
576*0a6a1f1dSLionel Sambuc n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT);
577*0a6a1f1dSLionel Sambuc n->string = roff_strdup(mdoc->roff, p);
578d65f6f70SBen Gras
579*0a6a1f1dSLionel Sambuc if ( ! node_append(mdoc, n))
580d65f6f70SBen Gras return(0);
581d65f6f70SBen Gras
582*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_SIBLING;
583d65f6f70SBen Gras return(1);
584d65f6f70SBen Gras }
585d65f6f70SBen Gras
586*0a6a1f1dSLionel Sambuc void
mdoc_word_append(struct mdoc * mdoc,const char * p)587*0a6a1f1dSLionel Sambuc mdoc_word_append(struct mdoc *mdoc, const char *p)
588*0a6a1f1dSLionel Sambuc {
589*0a6a1f1dSLionel Sambuc struct mdoc_node *n;
590*0a6a1f1dSLionel Sambuc char *addstr, *newstr;
591*0a6a1f1dSLionel Sambuc
592*0a6a1f1dSLionel Sambuc n = mdoc->last;
593*0a6a1f1dSLionel Sambuc addstr = roff_strdup(mdoc->roff, p);
594*0a6a1f1dSLionel Sambuc if (-1 == asprintf(&newstr, "%s %s", n->string, addstr)) {
595*0a6a1f1dSLionel Sambuc perror(NULL);
596*0a6a1f1dSLionel Sambuc exit((int)MANDOCLEVEL_SYSERR);
597*0a6a1f1dSLionel Sambuc }
598*0a6a1f1dSLionel Sambuc free(addstr);
599*0a6a1f1dSLionel Sambuc free(n->string);
600*0a6a1f1dSLionel Sambuc n->string = newstr;
601*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_SIBLING;
602*0a6a1f1dSLionel Sambuc }
603d65f6f70SBen Gras
604d65f6f70SBen Gras static void
mdoc_node_free(struct mdoc_node * p)605d65f6f70SBen Gras mdoc_node_free(struct mdoc_node *p)
606d65f6f70SBen Gras {
607d65f6f70SBen Gras
608d65f6f70SBen Gras if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type)
609d65f6f70SBen Gras free(p->norm);
610d65f6f70SBen Gras if (p->string)
611d65f6f70SBen Gras free(p->string);
612d65f6f70SBen Gras if (p->args)
613d65f6f70SBen Gras mdoc_argv_free(p->args);
614d65f6f70SBen Gras free(p);
615d65f6f70SBen Gras }
616d65f6f70SBen Gras
617d65f6f70SBen Gras
618d65f6f70SBen Gras static void
mdoc_node_unlink(struct mdoc * mdoc,struct mdoc_node * n)619*0a6a1f1dSLionel Sambuc mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n)
620d65f6f70SBen Gras {
621d65f6f70SBen Gras
622d65f6f70SBen Gras /* Adjust siblings. */
623d65f6f70SBen Gras
624d65f6f70SBen Gras if (n->prev)
625d65f6f70SBen Gras n->prev->next = n->next;
626d65f6f70SBen Gras if (n->next)
627d65f6f70SBen Gras n->next->prev = n->prev;
628d65f6f70SBen Gras
629d65f6f70SBen Gras /* Adjust parent. */
630d65f6f70SBen Gras
631d65f6f70SBen Gras if (n->parent) {
632d65f6f70SBen Gras n->parent->nchild--;
633d65f6f70SBen Gras if (n->parent->child == n)
634d65f6f70SBen Gras n->parent->child = n->prev ? n->prev : n->next;
635d65f6f70SBen Gras if (n->parent->last == n)
636d65f6f70SBen Gras n->parent->last = n->prev ? n->prev : NULL;
637d65f6f70SBen Gras }
638d65f6f70SBen Gras
639d65f6f70SBen Gras /* Adjust parse point, if applicable. */
640d65f6f70SBen Gras
641*0a6a1f1dSLionel Sambuc if (mdoc && mdoc->last == n) {
642d65f6f70SBen Gras if (n->prev) {
643*0a6a1f1dSLionel Sambuc mdoc->last = n->prev;
644*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_SIBLING;
645d65f6f70SBen Gras } else {
646*0a6a1f1dSLionel Sambuc mdoc->last = n->parent;
647*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_CHILD;
648d65f6f70SBen Gras }
649d65f6f70SBen Gras }
650d65f6f70SBen Gras
651*0a6a1f1dSLionel Sambuc if (mdoc && mdoc->first == n)
652*0a6a1f1dSLionel Sambuc mdoc->first = NULL;
653d65f6f70SBen Gras }
654d65f6f70SBen Gras
655d65f6f70SBen Gras
656d65f6f70SBen Gras void
mdoc_node_delete(struct mdoc * mdoc,struct mdoc_node * p)657*0a6a1f1dSLionel Sambuc mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p)
658d65f6f70SBen Gras {
659d65f6f70SBen Gras
660d65f6f70SBen Gras while (p->child) {
661d65f6f70SBen Gras assert(p->nchild);
662*0a6a1f1dSLionel Sambuc mdoc_node_delete(mdoc, p->child);
663d65f6f70SBen Gras }
664d65f6f70SBen Gras assert(0 == p->nchild);
665d65f6f70SBen Gras
666*0a6a1f1dSLionel Sambuc mdoc_node_unlink(mdoc, p);
667d65f6f70SBen Gras mdoc_node_free(p);
668d65f6f70SBen Gras }
669d65f6f70SBen Gras
670*0a6a1f1dSLionel Sambuc int
mdoc_node_relink(struct mdoc * mdoc,struct mdoc_node * p)671*0a6a1f1dSLionel Sambuc mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p)
672*0a6a1f1dSLionel Sambuc {
673*0a6a1f1dSLionel Sambuc
674*0a6a1f1dSLionel Sambuc mdoc_node_unlink(mdoc, p);
675*0a6a1f1dSLionel Sambuc return(node_append(mdoc, p));
676*0a6a1f1dSLionel Sambuc }
677*0a6a1f1dSLionel Sambuc
67892395e9cSLionel Sambuc #if 0
67992395e9cSLionel Sambuc /*
68092395e9cSLionel Sambuc * Pre-treat a text line.
68192395e9cSLionel Sambuc * Text lines can consist of equations, which must be handled apart from
68292395e9cSLionel Sambuc * the regular text.
68392395e9cSLionel Sambuc * Thus, use this function to step through a line checking if it has any
68492395e9cSLionel Sambuc * equations embedded in it.
68592395e9cSLionel Sambuc * This must handle multiple equations AND equations that do not end at
68692395e9cSLionel Sambuc * the end-of-line, i.e., will re-enter in the next roff parse.
68792395e9cSLionel Sambuc */
68892395e9cSLionel Sambuc static int
689*0a6a1f1dSLionel Sambuc mdoc_preptext(struct mdoc *mdoc, int line, char *buf, int offs)
69092395e9cSLionel Sambuc {
69192395e9cSLionel Sambuc char *start, *end;
69292395e9cSLionel Sambuc char delim;
69392395e9cSLionel Sambuc
69492395e9cSLionel Sambuc while ('\0' != buf[offs]) {
69592395e9cSLionel Sambuc /* Mark starting position if eqn is set. */
69692395e9cSLionel Sambuc start = NULL;
697*0a6a1f1dSLionel Sambuc if ('\0' != (delim = roff_eqndelim(mdoc->roff)))
69892395e9cSLionel Sambuc if (NULL != (start = strchr(buf + offs, delim)))
69992395e9cSLionel Sambuc *start++ = '\0';
70092395e9cSLionel Sambuc
70192395e9cSLionel Sambuc /* Parse text as normal. */
702*0a6a1f1dSLionel Sambuc if ( ! mdoc_ptext(mdoc, line, buf, offs))
70392395e9cSLionel Sambuc return(0);
70492395e9cSLionel Sambuc
70592395e9cSLionel Sambuc /* Continue only if an equation exists. */
70692395e9cSLionel Sambuc if (NULL == start)
70792395e9cSLionel Sambuc break;
70892395e9cSLionel Sambuc
70992395e9cSLionel Sambuc /* Read past the end of the equation. */
71092395e9cSLionel Sambuc offs += start - (buf + offs);
71192395e9cSLionel Sambuc assert(start == &buf[offs]);
71292395e9cSLionel Sambuc if (NULL != (end = strchr(buf + offs, delim))) {
71392395e9cSLionel Sambuc *end++ = '\0';
71492395e9cSLionel Sambuc while (' ' == *end)
71592395e9cSLionel Sambuc end++;
71692395e9cSLionel Sambuc }
71792395e9cSLionel Sambuc
71892395e9cSLionel Sambuc /* Parse the equation itself. */
719*0a6a1f1dSLionel Sambuc roff_openeqn(mdoc->roff, NULL, line, offs, buf);
72092395e9cSLionel Sambuc
72192395e9cSLionel Sambuc /* Process a finished equation? */
722*0a6a1f1dSLionel Sambuc if (roff_closeeqn(mdoc->roff))
723*0a6a1f1dSLionel Sambuc if ( ! mdoc_addeqn(mdoc, roff_eqn(mdoc->roff)))
72492395e9cSLionel Sambuc return(0);
72592395e9cSLionel Sambuc offs += (end - (buf + offs));
72692395e9cSLionel Sambuc }
72792395e9cSLionel Sambuc
72892395e9cSLionel Sambuc return(1);
72992395e9cSLionel Sambuc }
73092395e9cSLionel Sambuc #endif
731d65f6f70SBen Gras
732d65f6f70SBen Gras /*
733d65f6f70SBen Gras * Parse free-form text, that is, a line that does not begin with the
734d65f6f70SBen Gras * control character.
735d65f6f70SBen Gras */
736d65f6f70SBen Gras static int
mdoc_ptext(struct mdoc * mdoc,int line,char * buf,int offs)737*0a6a1f1dSLionel Sambuc mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
738d65f6f70SBen Gras {
739d65f6f70SBen Gras char *c, *ws, *end;
740d65f6f70SBen Gras struct mdoc_node *n;
741d65f6f70SBen Gras
742d65f6f70SBen Gras /* No text before an initial macro. */
743d65f6f70SBen Gras
744*0a6a1f1dSLionel Sambuc if (SEC_NONE == mdoc->lastnamed) {
745*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, line, offs, MANDOCERR_NOTEXT);
746d65f6f70SBen Gras return(1);
747d65f6f70SBen Gras }
748d65f6f70SBen Gras
749*0a6a1f1dSLionel Sambuc assert(mdoc->last);
750*0a6a1f1dSLionel Sambuc n = mdoc->last;
751d65f6f70SBen Gras
752d65f6f70SBen Gras /*
753d65f6f70SBen Gras * Divert directly to list processing if we're encountering a
754d65f6f70SBen Gras * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry
755d65f6f70SBen Gras * (a MDOC_BODY means it's already open, in which case we should
756d65f6f70SBen Gras * process within its context in the normal way).
757d65f6f70SBen Gras */
758d65f6f70SBen Gras
759d65f6f70SBen Gras if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
760d65f6f70SBen Gras LIST_column == n->norm->Bl.type) {
761d65f6f70SBen Gras /* `Bl' is open without any children. */
762*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_FREECOL;
763*0a6a1f1dSLionel Sambuc return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
764d65f6f70SBen Gras }
765d65f6f70SBen Gras
766d65f6f70SBen Gras if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
767d65f6f70SBen Gras NULL != n->parent &&
768d65f6f70SBen Gras MDOC_Bl == n->parent->tok &&
769d65f6f70SBen Gras LIST_column == n->parent->norm->Bl.type) {
770d65f6f70SBen Gras /* `Bl' has block-level `It' children. */
771*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_FREECOL;
772*0a6a1f1dSLionel Sambuc return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
773d65f6f70SBen Gras }
774d65f6f70SBen Gras
775d65f6f70SBen Gras /*
776d65f6f70SBen Gras * Search for the beginning of unescaped trailing whitespace (ws)
777d65f6f70SBen Gras * and for the first character not to be output (end).
778d65f6f70SBen Gras */
779d65f6f70SBen Gras
780d65f6f70SBen Gras /* FIXME: replace with strcspn(). */
781d65f6f70SBen Gras ws = NULL;
782d65f6f70SBen Gras for (c = end = buf + offs; *c; c++) {
783d65f6f70SBen Gras switch (*c) {
784d65f6f70SBen Gras case ' ':
785d65f6f70SBen Gras if (NULL == ws)
786d65f6f70SBen Gras ws = c;
787d65f6f70SBen Gras continue;
788d65f6f70SBen Gras case '\t':
789d65f6f70SBen Gras /*
790d65f6f70SBen Gras * Always warn about trailing tabs,
791d65f6f70SBen Gras * even outside literal context,
792d65f6f70SBen Gras * where they should be put on the next line.
793d65f6f70SBen Gras */
794d65f6f70SBen Gras if (NULL == ws)
795d65f6f70SBen Gras ws = c;
796d65f6f70SBen Gras /*
797d65f6f70SBen Gras * Strip trailing tabs in literal context only;
798d65f6f70SBen Gras * outside, they affect the next line.
799d65f6f70SBen Gras */
800*0a6a1f1dSLionel Sambuc if (MDOC_LITERAL & mdoc->flags)
801d65f6f70SBen Gras continue;
802d65f6f70SBen Gras break;
803d65f6f70SBen Gras case '\\':
804d65f6f70SBen Gras /* Skip the escaped character, too, if any. */
805d65f6f70SBen Gras if (c[1])
806d65f6f70SBen Gras c++;
807d65f6f70SBen Gras /* FALLTHROUGH */
808d65f6f70SBen Gras default:
809d65f6f70SBen Gras ws = NULL;
810d65f6f70SBen Gras break;
811d65f6f70SBen Gras }
812d65f6f70SBen Gras end = c + 1;
813d65f6f70SBen Gras }
814d65f6f70SBen Gras *end = '\0';
815d65f6f70SBen Gras
816d65f6f70SBen Gras if (ws)
817*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);
818d65f6f70SBen Gras
819*0a6a1f1dSLionel Sambuc if ('\0' == buf[offs] && ! (MDOC_LITERAL & mdoc->flags)) {
820*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, line, (int)(c-buf), MANDOCERR_NOBLANKLN);
821d65f6f70SBen Gras
822d65f6f70SBen Gras /*
823d65f6f70SBen Gras * Insert a `sp' in the case of a blank line. Technically,
824d65f6f70SBen Gras * blank lines aren't allowed, but enough manuals assume this
825d65f6f70SBen Gras * behaviour that we want to work around it.
826d65f6f70SBen Gras */
827*0a6a1f1dSLionel Sambuc if ( ! mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL))
828d65f6f70SBen Gras return(0);
829d65f6f70SBen Gras
830*0a6a1f1dSLionel Sambuc mdoc->next = MDOC_NEXT_SIBLING;
831*0a6a1f1dSLionel Sambuc
832*0a6a1f1dSLionel Sambuc return(mdoc_valid_post(mdoc));
833d65f6f70SBen Gras }
834d65f6f70SBen Gras
835*0a6a1f1dSLionel Sambuc if ( ! mdoc_word_alloc(mdoc, line, offs, buf+offs))
836d65f6f70SBen Gras return(0);
837d65f6f70SBen Gras
838*0a6a1f1dSLionel Sambuc if (MDOC_LITERAL & mdoc->flags)
839d65f6f70SBen Gras return(1);
840d65f6f70SBen Gras
841d65f6f70SBen Gras /*
842d65f6f70SBen Gras * End-of-sentence check. If the last character is an unescaped
843d65f6f70SBen Gras * EOS character, then flag the node as being the end of a
844d65f6f70SBen Gras * sentence. The front-end will know how to interpret this.
845d65f6f70SBen Gras */
846d65f6f70SBen Gras
847d65f6f70SBen Gras assert(buf < end);
848d65f6f70SBen Gras
849*0a6a1f1dSLionel Sambuc if (mandoc_eos(buf+offs, (size_t)(end-buf-offs)))
850*0a6a1f1dSLionel Sambuc mdoc->last->flags |= MDOC_EOS;
851d65f6f70SBen Gras
852d65f6f70SBen Gras return(1);
853d65f6f70SBen Gras }
854d65f6f70SBen Gras
855d65f6f70SBen Gras
856d65f6f70SBen Gras /*
857d65f6f70SBen Gras * Parse a macro line, that is, a line beginning with the control
858d65f6f70SBen Gras * character.
859d65f6f70SBen Gras */
860d65f6f70SBen Gras static int
mdoc_pmacro(struct mdoc * mdoc,int ln,char * buf,int offs)861*0a6a1f1dSLionel Sambuc mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
862d65f6f70SBen Gras {
863d65f6f70SBen Gras enum mdoct tok;
86492395e9cSLionel Sambuc int i, sv;
865d65f6f70SBen Gras char mac[5];
866d65f6f70SBen Gras struct mdoc_node *n;
867d65f6f70SBen Gras
86892395e9cSLionel Sambuc /* Empty post-control lines are ignored. */
869d65f6f70SBen Gras
87092395e9cSLionel Sambuc if ('"' == buf[offs]) {
871*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, ln, offs, MANDOCERR_BADCOMMENT);
87292395e9cSLionel Sambuc return(1);
87392395e9cSLionel Sambuc } else if ('\0' == buf[offs])
874d65f6f70SBen Gras return(1);
875d65f6f70SBen Gras
87692395e9cSLionel Sambuc sv = offs;
877d65f6f70SBen Gras
878d65f6f70SBen Gras /*
879d65f6f70SBen Gras * Copy the first word into a nil-terminated buffer.
880d65f6f70SBen Gras * Stop copying when a tab, space, or eoln is encountered.
881d65f6f70SBen Gras */
882d65f6f70SBen Gras
88392395e9cSLionel Sambuc i = 0;
88492395e9cSLionel Sambuc while (i < 4 && '\0' != buf[offs] &&
88592395e9cSLionel Sambuc ' ' != buf[offs] && '\t' != buf[offs])
88692395e9cSLionel Sambuc mac[i++] = buf[offs++];
887d65f6f70SBen Gras
88892395e9cSLionel Sambuc mac[i] = '\0';
88992395e9cSLionel Sambuc
89092395e9cSLionel Sambuc tok = (i > 1 || i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
89192395e9cSLionel Sambuc
892d65f6f70SBen Gras if (MDOC_MAX == tok) {
893*0a6a1f1dSLionel Sambuc mandoc_vmsg(MANDOCERR_MACRO, mdoc->parse,
89492395e9cSLionel Sambuc ln, sv, "%s", buf + sv - 1);
895d65f6f70SBen Gras return(1);
896d65f6f70SBen Gras }
897d65f6f70SBen Gras
898d65f6f70SBen Gras /* Disregard the first trailing tab, if applicable. */
899d65f6f70SBen Gras
90092395e9cSLionel Sambuc if ('\t' == buf[offs])
90192395e9cSLionel Sambuc offs++;
902d65f6f70SBen Gras
903d65f6f70SBen Gras /* Jump to the next non-whitespace word. */
904d65f6f70SBen Gras
90592395e9cSLionel Sambuc while (buf[offs] && ' ' == buf[offs])
90692395e9cSLionel Sambuc offs++;
907d65f6f70SBen Gras
908d65f6f70SBen Gras /*
909d65f6f70SBen Gras * Trailing whitespace. Note that tabs are allowed to be passed
910d65f6f70SBen Gras * into the parser as "text", so we only warn about spaces here.
911d65f6f70SBen Gras */
912d65f6f70SBen Gras
91392395e9cSLionel Sambuc if ('\0' == buf[offs] && ' ' == buf[offs - 1])
914*0a6a1f1dSLionel Sambuc mdoc_pmsg(mdoc, ln, offs - 1, MANDOCERR_EOLNSPACE);
915d65f6f70SBen Gras
916d65f6f70SBen Gras /*
917d65f6f70SBen Gras * If an initial macro or a list invocation, divert directly
918d65f6f70SBen Gras * into macro processing.
919d65f6f70SBen Gras */
920d65f6f70SBen Gras
921*0a6a1f1dSLionel Sambuc if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) {
922*0a6a1f1dSLionel Sambuc if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
923d65f6f70SBen Gras goto err;
924d65f6f70SBen Gras return(1);
925d65f6f70SBen Gras }
926d65f6f70SBen Gras
927*0a6a1f1dSLionel Sambuc n = mdoc->last;
928*0a6a1f1dSLionel Sambuc assert(mdoc->last);
929d65f6f70SBen Gras
930d65f6f70SBen Gras /*
931d65f6f70SBen Gras * If the first macro of a `Bl -column', open an `It' block
932d65f6f70SBen Gras * context around the parsed macro.
933d65f6f70SBen Gras */
934d65f6f70SBen Gras
935d65f6f70SBen Gras if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
936d65f6f70SBen Gras LIST_column == n->norm->Bl.type) {
937*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_FREECOL;
938*0a6a1f1dSLionel Sambuc if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf))
939d65f6f70SBen Gras goto err;
940d65f6f70SBen Gras return(1);
941d65f6f70SBen Gras }
942d65f6f70SBen Gras
943d65f6f70SBen Gras /*
944d65f6f70SBen Gras * If we're following a block-level `It' within a `Bl -column'
945d65f6f70SBen Gras * context (perhaps opened in the above block or in ptext()),
946d65f6f70SBen Gras * then open an `It' block context around the parsed macro.
947d65f6f70SBen Gras */
948d65f6f70SBen Gras
949d65f6f70SBen Gras if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
950d65f6f70SBen Gras NULL != n->parent &&
951d65f6f70SBen Gras MDOC_Bl == n->parent->tok &&
952d65f6f70SBen Gras LIST_column == n->parent->norm->Bl.type) {
953*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_FREECOL;
954*0a6a1f1dSLionel Sambuc if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf))
955d65f6f70SBen Gras goto err;
956d65f6f70SBen Gras return(1);
957d65f6f70SBen Gras }
958d65f6f70SBen Gras
959d65f6f70SBen Gras /* Normal processing of a macro. */
960d65f6f70SBen Gras
961*0a6a1f1dSLionel Sambuc if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf))
962d65f6f70SBen Gras goto err;
963d65f6f70SBen Gras
964d65f6f70SBen Gras return(1);
965d65f6f70SBen Gras
966d65f6f70SBen Gras err: /* Error out. */
967d65f6f70SBen Gras
968*0a6a1f1dSLionel Sambuc mdoc->flags |= MDOC_HALT;
969d65f6f70SBen Gras return(0);
970d65f6f70SBen Gras }
971d65f6f70SBen Gras
97292395e9cSLionel Sambuc enum mdelim
mdoc_isdelim(const char * p)97392395e9cSLionel Sambuc mdoc_isdelim(const char *p)
97492395e9cSLionel Sambuc {
975d65f6f70SBen Gras
97692395e9cSLionel Sambuc if ('\0' == p[0])
97792395e9cSLionel Sambuc return(DELIM_NONE);
97892395e9cSLionel Sambuc
97992395e9cSLionel Sambuc if ('\0' == p[1])
98092395e9cSLionel Sambuc switch (p[0]) {
98192395e9cSLionel Sambuc case('('):
98292395e9cSLionel Sambuc /* FALLTHROUGH */
98392395e9cSLionel Sambuc case('['):
98492395e9cSLionel Sambuc return(DELIM_OPEN);
98592395e9cSLionel Sambuc case('|'):
98692395e9cSLionel Sambuc return(DELIM_MIDDLE);
98792395e9cSLionel Sambuc case('.'):
98892395e9cSLionel Sambuc /* FALLTHROUGH */
98992395e9cSLionel Sambuc case(','):
99092395e9cSLionel Sambuc /* FALLTHROUGH */
99192395e9cSLionel Sambuc case(';'):
99292395e9cSLionel Sambuc /* FALLTHROUGH */
99392395e9cSLionel Sambuc case(':'):
99492395e9cSLionel Sambuc /* FALLTHROUGH */
99592395e9cSLionel Sambuc case('?'):
99692395e9cSLionel Sambuc /* FALLTHROUGH */
99792395e9cSLionel Sambuc case('!'):
99892395e9cSLionel Sambuc /* FALLTHROUGH */
99992395e9cSLionel Sambuc case(')'):
100092395e9cSLionel Sambuc /* FALLTHROUGH */
100192395e9cSLionel Sambuc case(']'):
100292395e9cSLionel Sambuc return(DELIM_CLOSE);
100392395e9cSLionel Sambuc default:
100492395e9cSLionel Sambuc return(DELIM_NONE);
100592395e9cSLionel Sambuc }
100692395e9cSLionel Sambuc
100792395e9cSLionel Sambuc if ('\\' != p[0])
100892395e9cSLionel Sambuc return(DELIM_NONE);
100992395e9cSLionel Sambuc
101092395e9cSLionel Sambuc if (0 == strcmp(p + 1, "."))
101192395e9cSLionel Sambuc return(DELIM_CLOSE);
1012*0a6a1f1dSLionel Sambuc if (0 == strcmp(p + 1, "fR|\\fP"))
101392395e9cSLionel Sambuc return(DELIM_MIDDLE);
101492395e9cSLionel Sambuc
101592395e9cSLionel Sambuc return(DELIM_NONE);
101692395e9cSLionel Sambuc }
1017