1*0a6a1f1dSLionel Sambuc /* Id: man.c,v 1.122 2013/12/31 23:23:10 schwarze Exp */
2d65f6f70SBen Gras /*
392395e9cSLionel Sambuc * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4d65f6f70SBen Gras *
5d65f6f70SBen Gras * Permission to use, copy, modify, and distribute this software for any
6d65f6f70SBen Gras * purpose with or without fee is hereby granted, provided that the above
7d65f6f70SBen Gras * copyright notice and this permission notice appear in all copies.
8d65f6f70SBen Gras *
9d65f6f70SBen Gras * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10d65f6f70SBen Gras * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11d65f6f70SBen Gras * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12d65f6f70SBen Gras * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13d65f6f70SBen Gras * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14d65f6f70SBen Gras * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15d65f6f70SBen Gras * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16d65f6f70SBen Gras */
17d65f6f70SBen Gras #ifdef HAVE_CONFIG_H
18d65f6f70SBen Gras #include "config.h"
19d65f6f70SBen Gras #endif
20d65f6f70SBen Gras
21d65f6f70SBen Gras #include <sys/types.h>
22d65f6f70SBen Gras
23d65f6f70SBen Gras #include <assert.h>
24d65f6f70SBen Gras #include <stdarg.h>
25d65f6f70SBen Gras #include <stdlib.h>
26d65f6f70SBen Gras #include <stdio.h>
27d65f6f70SBen Gras #include <string.h>
28d65f6f70SBen Gras
2992395e9cSLionel Sambuc #include "man.h"
30d65f6f70SBen Gras #include "mandoc.h"
31d65f6f70SBen Gras #include "libman.h"
32d65f6f70SBen Gras #include "libmandoc.h"
33d65f6f70SBen Gras
34d65f6f70SBen Gras const char *const __man_macronames[MAN_MAX] = {
35d65f6f70SBen Gras "br", "TH", "SH", "SS",
36d65f6f70SBen Gras "TP", "LP", "PP", "P",
37d65f6f70SBen Gras "IP", "HP", "SM", "SB",
38d65f6f70SBen Gras "BI", "IB", "BR", "RB",
39d65f6f70SBen Gras "R", "B", "I", "IR",
40d65f6f70SBen Gras "RI", "na", "sp", "nf",
41d65f6f70SBen Gras "fi", "RE", "RS", "DT",
42d65f6f70SBen Gras "UC", "PD", "AT", "in",
43*0a6a1f1dSLionel Sambuc "ft", "OP", "EX", "EE",
44*0a6a1f1dSLionel Sambuc "UR", "UE"
45d65f6f70SBen Gras };
46d65f6f70SBen Gras
47d65f6f70SBen Gras const char * const *man_macronames = __man_macronames;
48d65f6f70SBen Gras
4992395e9cSLionel Sambuc static struct man_node *man_node_alloc(struct man *, int, int,
50d65f6f70SBen Gras enum man_type, enum mant);
51d65f6f70SBen Gras static int man_node_append(struct man *,
52d65f6f70SBen Gras struct man_node *);
53d65f6f70SBen Gras static void man_node_free(struct man_node *);
54d65f6f70SBen Gras static void man_node_unlink(struct man *,
55d65f6f70SBen Gras struct man_node *);
56d65f6f70SBen Gras static int man_ptext(struct man *, int, char *, int);
57d65f6f70SBen Gras static int man_pmacro(struct man *, int, char *, int);
58d65f6f70SBen Gras static void man_free1(struct man *);
59d65f6f70SBen Gras static void man_alloc1(struct man *);
60d65f6f70SBen Gras static int man_descope(struct man *, int, int);
61d65f6f70SBen Gras
62d65f6f70SBen Gras
63d65f6f70SBen Gras const struct man_node *
man_node(const struct man * man)64*0a6a1f1dSLionel Sambuc man_node(const struct man *man)
65d65f6f70SBen Gras {
66d65f6f70SBen Gras
67*0a6a1f1dSLionel Sambuc assert( ! (MAN_HALT & man->flags));
68*0a6a1f1dSLionel Sambuc return(man->first);
69d65f6f70SBen Gras }
70d65f6f70SBen Gras
71d65f6f70SBen Gras
72d65f6f70SBen Gras const struct man_meta *
man_meta(const struct man * man)73*0a6a1f1dSLionel Sambuc man_meta(const struct man *man)
74d65f6f70SBen Gras {
75d65f6f70SBen Gras
76*0a6a1f1dSLionel Sambuc assert( ! (MAN_HALT & man->flags));
77*0a6a1f1dSLionel Sambuc return(&man->meta);
78d65f6f70SBen Gras }
79d65f6f70SBen Gras
80d65f6f70SBen Gras
81d65f6f70SBen Gras void
man_reset(struct man * man)82d65f6f70SBen Gras man_reset(struct man *man)
83d65f6f70SBen Gras {
84d65f6f70SBen Gras
85d65f6f70SBen Gras man_free1(man);
86d65f6f70SBen Gras man_alloc1(man);
87d65f6f70SBen Gras }
88d65f6f70SBen Gras
89d65f6f70SBen Gras
90d65f6f70SBen Gras void
man_free(struct man * man)91d65f6f70SBen Gras man_free(struct man *man)
92d65f6f70SBen Gras {
93d65f6f70SBen Gras
94d65f6f70SBen Gras man_free1(man);
95d65f6f70SBen Gras free(man);
96d65f6f70SBen Gras }
97d65f6f70SBen Gras
98d65f6f70SBen Gras
99d65f6f70SBen Gras struct man *
man_alloc(struct roff * roff,struct mparse * parse)10092395e9cSLionel Sambuc man_alloc(struct roff *roff, struct mparse *parse)
101d65f6f70SBen Gras {
102d65f6f70SBen Gras struct man *p;
103d65f6f70SBen Gras
104d65f6f70SBen Gras p = mandoc_calloc(1, sizeof(struct man));
105d65f6f70SBen Gras
106d65f6f70SBen Gras man_hash_init();
10792395e9cSLionel Sambuc p->parse = parse;
10892395e9cSLionel Sambuc p->roff = roff;
109d65f6f70SBen Gras
110d65f6f70SBen Gras man_alloc1(p);
111d65f6f70SBen Gras return(p);
112d65f6f70SBen Gras }
113d65f6f70SBen Gras
114d65f6f70SBen Gras
115d65f6f70SBen Gras int
man_endparse(struct man * man)116*0a6a1f1dSLionel Sambuc man_endparse(struct man *man)
117d65f6f70SBen Gras {
118d65f6f70SBen Gras
119*0a6a1f1dSLionel Sambuc assert( ! (MAN_HALT & man->flags));
120*0a6a1f1dSLionel Sambuc if (man_macroend(man))
121d65f6f70SBen Gras return(1);
122*0a6a1f1dSLionel Sambuc man->flags |= MAN_HALT;
123d65f6f70SBen Gras return(0);
124d65f6f70SBen Gras }
125d65f6f70SBen Gras
126d65f6f70SBen Gras
127d65f6f70SBen Gras int
man_parseln(struct man * man,int ln,char * buf,int offs)128*0a6a1f1dSLionel Sambuc man_parseln(struct man *man, int ln, char *buf, int offs)
129d65f6f70SBen Gras {
130d65f6f70SBen Gras
131*0a6a1f1dSLionel Sambuc man->flags |= MAN_NEWLINE;
13292395e9cSLionel Sambuc
133*0a6a1f1dSLionel Sambuc assert( ! (MAN_HALT & man->flags));
13492395e9cSLionel Sambuc
135*0a6a1f1dSLionel Sambuc return (roff_getcontrol(man->roff, buf, &offs) ?
136*0a6a1f1dSLionel Sambuc man_pmacro(man, ln, buf, offs) :
137*0a6a1f1dSLionel Sambuc man_ptext(man, ln, buf, offs));
138d65f6f70SBen Gras }
139d65f6f70SBen Gras
140d65f6f70SBen Gras
141d65f6f70SBen Gras static void
man_free1(struct man * man)142d65f6f70SBen Gras man_free1(struct man *man)
143d65f6f70SBen Gras {
144d65f6f70SBen Gras
145d65f6f70SBen Gras if (man->first)
146d65f6f70SBen Gras man_node_delete(man, man->first);
147d65f6f70SBen Gras if (man->meta.title)
148d65f6f70SBen Gras free(man->meta.title);
149d65f6f70SBen Gras if (man->meta.source)
150d65f6f70SBen Gras free(man->meta.source);
15192395e9cSLionel Sambuc if (man->meta.date)
15292395e9cSLionel Sambuc free(man->meta.date);
153d65f6f70SBen Gras if (man->meta.vol)
154d65f6f70SBen Gras free(man->meta.vol);
155d65f6f70SBen Gras if (man->meta.msec)
156d65f6f70SBen Gras free(man->meta.msec);
157d65f6f70SBen Gras }
158d65f6f70SBen Gras
159d65f6f70SBen Gras
160d65f6f70SBen Gras static void
man_alloc1(struct man * man)161*0a6a1f1dSLionel Sambuc man_alloc1(struct man *man)
162d65f6f70SBen Gras {
163d65f6f70SBen Gras
164*0a6a1f1dSLionel Sambuc memset(&man->meta, 0, sizeof(struct man_meta));
165*0a6a1f1dSLionel Sambuc man->flags = 0;
166*0a6a1f1dSLionel Sambuc man->last = mandoc_calloc(1, sizeof(struct man_node));
167*0a6a1f1dSLionel Sambuc man->first = man->last;
168*0a6a1f1dSLionel Sambuc man->last->type = MAN_ROOT;
169*0a6a1f1dSLionel Sambuc man->last->tok = MAN_MAX;
170*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_CHILD;
171d65f6f70SBen Gras }
172d65f6f70SBen Gras
173d65f6f70SBen Gras
174d65f6f70SBen Gras static int
man_node_append(struct man * man,struct man_node * p)175d65f6f70SBen Gras man_node_append(struct man *man, struct man_node *p)
176d65f6f70SBen Gras {
177d65f6f70SBen Gras
178d65f6f70SBen Gras assert(man->last);
179d65f6f70SBen Gras assert(man->first);
180d65f6f70SBen Gras assert(MAN_ROOT != p->type);
181d65f6f70SBen Gras
182d65f6f70SBen Gras switch (man->next) {
183d65f6f70SBen Gras case (MAN_NEXT_SIBLING):
184d65f6f70SBen Gras man->last->next = p;
185d65f6f70SBen Gras p->prev = man->last;
186d65f6f70SBen Gras p->parent = man->last->parent;
187d65f6f70SBen Gras break;
188d65f6f70SBen Gras case (MAN_NEXT_CHILD):
189d65f6f70SBen Gras man->last->child = p;
190d65f6f70SBen Gras p->parent = man->last;
191d65f6f70SBen Gras break;
192d65f6f70SBen Gras default:
193d65f6f70SBen Gras abort();
194d65f6f70SBen Gras /* NOTREACHED */
195d65f6f70SBen Gras }
196d65f6f70SBen Gras
197d65f6f70SBen Gras assert(p->parent);
198d65f6f70SBen Gras p->parent->nchild++;
199d65f6f70SBen Gras
200d65f6f70SBen Gras if ( ! man_valid_pre(man, p))
201d65f6f70SBen Gras return(0);
202d65f6f70SBen Gras
203d65f6f70SBen Gras switch (p->type) {
204d65f6f70SBen Gras case (MAN_HEAD):
205d65f6f70SBen Gras assert(MAN_BLOCK == p->parent->type);
206d65f6f70SBen Gras p->parent->head = p;
207d65f6f70SBen Gras break;
20892395e9cSLionel Sambuc case (MAN_TAIL):
20992395e9cSLionel Sambuc assert(MAN_BLOCK == p->parent->type);
21092395e9cSLionel Sambuc p->parent->tail = p;
21192395e9cSLionel Sambuc break;
212d65f6f70SBen Gras case (MAN_BODY):
213d65f6f70SBen Gras assert(MAN_BLOCK == p->parent->type);
214d65f6f70SBen Gras p->parent->body = p;
215d65f6f70SBen Gras break;
216d65f6f70SBen Gras default:
217d65f6f70SBen Gras break;
218d65f6f70SBen Gras }
219d65f6f70SBen Gras
220d65f6f70SBen Gras man->last = p;
221d65f6f70SBen Gras
222d65f6f70SBen Gras switch (p->type) {
223d65f6f70SBen Gras case (MAN_TBL):
224d65f6f70SBen Gras /* FALLTHROUGH */
225d65f6f70SBen Gras case (MAN_TEXT):
226d65f6f70SBen Gras if ( ! man_valid_post(man))
227d65f6f70SBen Gras return(0);
228d65f6f70SBen Gras break;
229d65f6f70SBen Gras default:
230d65f6f70SBen Gras break;
231d65f6f70SBen Gras }
232d65f6f70SBen Gras
233d65f6f70SBen Gras return(1);
234d65f6f70SBen Gras }
235d65f6f70SBen Gras
236d65f6f70SBen Gras
237d65f6f70SBen Gras static struct man_node *
man_node_alloc(struct man * man,int line,int pos,enum man_type type,enum mant tok)238*0a6a1f1dSLionel Sambuc man_node_alloc(struct man *man, int line, int pos,
23992395e9cSLionel Sambuc enum man_type type, enum mant tok)
240d65f6f70SBen Gras {
241d65f6f70SBen Gras struct man_node *p;
242d65f6f70SBen Gras
243d65f6f70SBen Gras p = mandoc_calloc(1, sizeof(struct man_node));
244d65f6f70SBen Gras p->line = line;
245d65f6f70SBen Gras p->pos = pos;
246d65f6f70SBen Gras p->type = type;
247d65f6f70SBen Gras p->tok = tok;
24892395e9cSLionel Sambuc
249*0a6a1f1dSLionel Sambuc if (MAN_NEWLINE & man->flags)
25092395e9cSLionel Sambuc p->flags |= MAN_LINE;
251*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_NEWLINE;
252d65f6f70SBen Gras return(p);
253d65f6f70SBen Gras }
254d65f6f70SBen Gras
255d65f6f70SBen Gras
256d65f6f70SBen Gras int
man_elem_alloc(struct man * man,int line,int pos,enum mant tok)257*0a6a1f1dSLionel Sambuc man_elem_alloc(struct man *man, int line, int pos, enum mant tok)
258d65f6f70SBen Gras {
259d65f6f70SBen Gras struct man_node *p;
260d65f6f70SBen Gras
261*0a6a1f1dSLionel Sambuc p = man_node_alloc(man, line, pos, MAN_ELEM, tok);
262*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, p))
26392395e9cSLionel Sambuc return(0);
264*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_CHILD;
26592395e9cSLionel Sambuc return(1);
26692395e9cSLionel Sambuc }
26792395e9cSLionel Sambuc
26892395e9cSLionel Sambuc
26992395e9cSLionel Sambuc int
man_tail_alloc(struct man * man,int line,int pos,enum mant tok)270*0a6a1f1dSLionel Sambuc man_tail_alloc(struct man *man, int line, int pos, enum mant tok)
27192395e9cSLionel Sambuc {
27292395e9cSLionel Sambuc struct man_node *p;
27392395e9cSLionel Sambuc
274*0a6a1f1dSLionel Sambuc p = man_node_alloc(man, line, pos, MAN_TAIL, tok);
275*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, p))
276d65f6f70SBen Gras return(0);
277*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_CHILD;
278d65f6f70SBen Gras return(1);
279d65f6f70SBen Gras }
280d65f6f70SBen Gras
281d65f6f70SBen Gras
282d65f6f70SBen Gras int
man_head_alloc(struct man * man,int line,int pos,enum mant tok)283*0a6a1f1dSLionel Sambuc man_head_alloc(struct man *man, int line, int pos, enum mant tok)
284d65f6f70SBen Gras {
285d65f6f70SBen Gras struct man_node *p;
286d65f6f70SBen Gras
287*0a6a1f1dSLionel Sambuc p = man_node_alloc(man, line, pos, MAN_HEAD, tok);
288*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, p))
289d65f6f70SBen Gras return(0);
290*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_CHILD;
291d65f6f70SBen Gras return(1);
292d65f6f70SBen Gras }
293d65f6f70SBen Gras
294d65f6f70SBen Gras
295d65f6f70SBen Gras int
man_body_alloc(struct man * man,int line,int pos,enum mant tok)296*0a6a1f1dSLionel Sambuc man_body_alloc(struct man *man, int line, int pos, enum mant tok)
297d65f6f70SBen Gras {
298d65f6f70SBen Gras struct man_node *p;
299d65f6f70SBen Gras
300*0a6a1f1dSLionel Sambuc p = man_node_alloc(man, line, pos, MAN_BODY, tok);
301*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, p))
302d65f6f70SBen Gras return(0);
303*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_CHILD;
304d65f6f70SBen Gras return(1);
305d65f6f70SBen Gras }
306d65f6f70SBen Gras
307d65f6f70SBen Gras
308d65f6f70SBen Gras int
man_block_alloc(struct man * man,int line,int pos,enum mant tok)309*0a6a1f1dSLionel Sambuc man_block_alloc(struct man *man, int line, int pos, enum mant tok)
310d65f6f70SBen Gras {
311d65f6f70SBen Gras struct man_node *p;
312d65f6f70SBen Gras
313*0a6a1f1dSLionel Sambuc p = man_node_alloc(man, line, pos, MAN_BLOCK, tok);
314*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, p))
315d65f6f70SBen Gras return(0);
316*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_CHILD;
317d65f6f70SBen Gras return(1);
318d65f6f70SBen Gras }
319d65f6f70SBen Gras
320d65f6f70SBen Gras int
man_word_alloc(struct man * man,int line,int pos,const char * word)321*0a6a1f1dSLionel Sambuc man_word_alloc(struct man *man, int line, int pos, const char *word)
322d65f6f70SBen Gras {
323d65f6f70SBen Gras struct man_node *n;
324d65f6f70SBen Gras
325*0a6a1f1dSLionel Sambuc n = man_node_alloc(man, line, pos, MAN_TEXT, MAN_MAX);
326*0a6a1f1dSLionel Sambuc n->string = roff_strdup(man->roff, word);
327d65f6f70SBen Gras
328*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, n))
329d65f6f70SBen Gras return(0);
330d65f6f70SBen Gras
331*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_SIBLING;
332d65f6f70SBen Gras return(1);
333d65f6f70SBen Gras }
334d65f6f70SBen Gras
335d65f6f70SBen Gras
336d65f6f70SBen Gras /*
337d65f6f70SBen Gras * Free all of the resources held by a node. This does NOT unlink a
338d65f6f70SBen Gras * node from its context; for that, see man_node_unlink().
339d65f6f70SBen Gras */
340d65f6f70SBen Gras static void
man_node_free(struct man_node * p)341d65f6f70SBen Gras man_node_free(struct man_node *p)
342d65f6f70SBen Gras {
343d65f6f70SBen Gras
344d65f6f70SBen Gras if (p->string)
345d65f6f70SBen Gras free(p->string);
346d65f6f70SBen Gras free(p);
347d65f6f70SBen Gras }
348d65f6f70SBen Gras
349d65f6f70SBen Gras
350d65f6f70SBen Gras void
man_node_delete(struct man * man,struct man_node * p)351*0a6a1f1dSLionel Sambuc man_node_delete(struct man *man, struct man_node *p)
352d65f6f70SBen Gras {
353d65f6f70SBen Gras
354d65f6f70SBen Gras while (p->child)
355*0a6a1f1dSLionel Sambuc man_node_delete(man, p->child);
356d65f6f70SBen Gras
357*0a6a1f1dSLionel Sambuc man_node_unlink(man, p);
358d65f6f70SBen Gras man_node_free(p);
359d65f6f70SBen Gras }
360d65f6f70SBen Gras
36192395e9cSLionel Sambuc int
man_addeqn(struct man * man,const struct eqn * ep)362*0a6a1f1dSLionel Sambuc man_addeqn(struct man *man, const struct eqn *ep)
36392395e9cSLionel Sambuc {
36492395e9cSLionel Sambuc struct man_node *n;
36592395e9cSLionel Sambuc
366*0a6a1f1dSLionel Sambuc assert( ! (MAN_HALT & man->flags));
36792395e9cSLionel Sambuc
368*0a6a1f1dSLionel Sambuc n = man_node_alloc(man, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
36992395e9cSLionel Sambuc n->eqn = ep;
37092395e9cSLionel Sambuc
371*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, n))
37292395e9cSLionel Sambuc return(0);
37392395e9cSLionel Sambuc
374*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_SIBLING;
375*0a6a1f1dSLionel Sambuc return(man_descope(man, ep->ln, ep->pos));
37692395e9cSLionel Sambuc }
377d65f6f70SBen Gras
378d65f6f70SBen Gras int
man_addspan(struct man * man,const struct tbl_span * sp)379*0a6a1f1dSLionel Sambuc man_addspan(struct man *man, const struct tbl_span *sp)
380d65f6f70SBen Gras {
38192395e9cSLionel Sambuc struct man_node *n;
382d65f6f70SBen Gras
383*0a6a1f1dSLionel Sambuc assert( ! (MAN_HALT & man->flags));
38492395e9cSLionel Sambuc
385*0a6a1f1dSLionel Sambuc n = man_node_alloc(man, sp->line, 0, MAN_TBL, MAN_MAX);
38692395e9cSLionel Sambuc n->span = sp;
38792395e9cSLionel Sambuc
388*0a6a1f1dSLionel Sambuc if ( ! man_node_append(man, n))
389d65f6f70SBen Gras return(0);
39092395e9cSLionel Sambuc
391*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_SIBLING;
392*0a6a1f1dSLionel Sambuc return(man_descope(man, sp->line, 0));
393d65f6f70SBen Gras }
394d65f6f70SBen Gras
395d65f6f70SBen Gras static int
man_descope(struct man * man,int line,int offs)396*0a6a1f1dSLionel Sambuc man_descope(struct man *man, int line, int offs)
397d65f6f70SBen Gras {
398d65f6f70SBen Gras /*
399d65f6f70SBen Gras * Co-ordinate what happens with having a next-line scope open:
400d65f6f70SBen Gras * first close out the element scope (if applicable), then close
401d65f6f70SBen Gras * out the block scope (also if applicable).
402d65f6f70SBen Gras */
403d65f6f70SBen Gras
404*0a6a1f1dSLionel Sambuc if (MAN_ELINE & man->flags) {
405*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_ELINE;
406*0a6a1f1dSLionel Sambuc if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
407d65f6f70SBen Gras return(0);
408d65f6f70SBen Gras }
409d65f6f70SBen Gras
410*0a6a1f1dSLionel Sambuc if ( ! (MAN_BLINE & man->flags))
411d65f6f70SBen Gras return(1);
412*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_BLINE;
413d65f6f70SBen Gras
414*0a6a1f1dSLionel Sambuc if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
415d65f6f70SBen Gras return(0);
416*0a6a1f1dSLionel Sambuc return(man_body_alloc(man, line, offs, man->last->tok));
417d65f6f70SBen Gras }
418d65f6f70SBen Gras
419d65f6f70SBen Gras static int
man_ptext(struct man * man,int line,char * buf,int offs)420*0a6a1f1dSLionel Sambuc man_ptext(struct man *man, int line, char *buf, int offs)
421d65f6f70SBen Gras {
422d65f6f70SBen Gras int i;
423d65f6f70SBen Gras
424d65f6f70SBen Gras /* Literal free-form text whitespace is preserved. */
425d65f6f70SBen Gras
426*0a6a1f1dSLionel Sambuc if (MAN_LITERAL & man->flags) {
427*0a6a1f1dSLionel Sambuc if ( ! man_word_alloc(man, line, offs, buf + offs))
428d65f6f70SBen Gras return(0);
429*0a6a1f1dSLionel Sambuc return(man_descope(man, line, offs));
430d65f6f70SBen Gras }
431d65f6f70SBen Gras
432d65f6f70SBen Gras for (i = offs; ' ' == buf[i]; i++)
433d65f6f70SBen Gras /* Skip leading whitespace. */ ;
434d65f6f70SBen Gras
435*0a6a1f1dSLionel Sambuc /*
436*0a6a1f1dSLionel Sambuc * Blank lines are ignored right after headings
437*0a6a1f1dSLionel Sambuc * but add a single vertical space elsewhere.
438*0a6a1f1dSLionel Sambuc */
439*0a6a1f1dSLionel Sambuc
440d65f6f70SBen Gras if ('\0' == buf[i]) {
441d65f6f70SBen Gras /* Allocate a blank entry. */
442*0a6a1f1dSLionel Sambuc if (MAN_SH != man->last->tok &&
443*0a6a1f1dSLionel Sambuc MAN_SS != man->last->tok) {
444*0a6a1f1dSLionel Sambuc if ( ! man_elem_alloc(man, line, offs, MAN_sp))
445d65f6f70SBen Gras return(0);
446*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_SIBLING;
447*0a6a1f1dSLionel Sambuc }
448*0a6a1f1dSLionel Sambuc return(1);
449d65f6f70SBen Gras }
450d65f6f70SBen Gras
451d65f6f70SBen Gras /*
452d65f6f70SBen Gras * Warn if the last un-escaped character is whitespace. Then
453d65f6f70SBen Gras * strip away the remaining spaces (tabs stay!).
454d65f6f70SBen Gras */
455d65f6f70SBen Gras
456d65f6f70SBen Gras i = (int)strlen(buf);
457d65f6f70SBen Gras assert(i);
458d65f6f70SBen Gras
459d65f6f70SBen Gras if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
460d65f6f70SBen Gras if (i > 1 && '\\' != buf[i - 2])
461*0a6a1f1dSLionel Sambuc man_pmsg(man, line, i - 1, MANDOCERR_EOLNSPACE);
462d65f6f70SBen Gras
463d65f6f70SBen Gras for (--i; i && ' ' == buf[i]; i--)
464d65f6f70SBen Gras /* Spin back to non-space. */ ;
465d65f6f70SBen Gras
466d65f6f70SBen Gras /* Jump ahead of escaped whitespace. */
467d65f6f70SBen Gras i += '\\' == buf[i] ? 2 : 1;
468d65f6f70SBen Gras
469d65f6f70SBen Gras buf[i] = '\0';
470d65f6f70SBen Gras }
471d65f6f70SBen Gras
472*0a6a1f1dSLionel Sambuc if ( ! man_word_alloc(man, line, offs, buf + offs))
473d65f6f70SBen Gras return(0);
474d65f6f70SBen Gras
475d65f6f70SBen Gras /*
476d65f6f70SBen Gras * End-of-sentence check. If the last character is an unescaped
477d65f6f70SBen Gras * EOS character, then flag the node as being the end of a
478d65f6f70SBen Gras * sentence. The front-end will know how to interpret this.
479d65f6f70SBen Gras */
480d65f6f70SBen Gras
481d65f6f70SBen Gras assert(i);
482*0a6a1f1dSLionel Sambuc if (mandoc_eos(buf, (size_t)i))
483*0a6a1f1dSLionel Sambuc man->last->flags |= MAN_EOS;
484d65f6f70SBen Gras
485*0a6a1f1dSLionel Sambuc return(man_descope(man, line, offs));
486d65f6f70SBen Gras }
487d65f6f70SBen Gras
488d65f6f70SBen Gras static int
man_pmacro(struct man * man,int ln,char * buf,int offs)489*0a6a1f1dSLionel Sambuc man_pmacro(struct man *man, int ln, char *buf, int offs)
490d65f6f70SBen Gras {
49192395e9cSLionel Sambuc int i, ppos;
492d65f6f70SBen Gras enum mant tok;
493d65f6f70SBen Gras char mac[5];
494d65f6f70SBen Gras struct man_node *n;
495d65f6f70SBen Gras
49692395e9cSLionel Sambuc if ('"' == buf[offs]) {
497*0a6a1f1dSLionel Sambuc man_pmsg(man, ln, offs, MANDOCERR_BADCOMMENT);
49892395e9cSLionel Sambuc return(1);
49992395e9cSLionel Sambuc } else if ('\0' == buf[offs])
500d65f6f70SBen Gras return(1);
501d65f6f70SBen Gras
50292395e9cSLionel Sambuc ppos = offs;
503d65f6f70SBen Gras
504d65f6f70SBen Gras /*
505d65f6f70SBen Gras * Copy the first word into a nil-terminated buffer.
506d65f6f70SBen Gras * Stop copying when a tab, space, or eoln is encountered.
507d65f6f70SBen Gras */
508d65f6f70SBen Gras
50992395e9cSLionel Sambuc i = 0;
51092395e9cSLionel Sambuc while (i < 4 && '\0' != buf[offs] &&
51192395e9cSLionel Sambuc ' ' != buf[offs] && '\t' != buf[offs])
51292395e9cSLionel Sambuc mac[i++] = buf[offs++];
513d65f6f70SBen Gras
51492395e9cSLionel Sambuc mac[i] = '\0';
51592395e9cSLionel Sambuc
51692395e9cSLionel Sambuc tok = (i > 0 && i < 4) ? man_hash_find(mac) : MAN_MAX;
51792395e9cSLionel Sambuc
518d65f6f70SBen Gras if (MAN_MAX == tok) {
519*0a6a1f1dSLionel Sambuc mandoc_vmsg(MANDOCERR_MACRO, man->parse, ln,
52092395e9cSLionel Sambuc ppos, "%s", buf + ppos - 1);
521d65f6f70SBen Gras return(1);
522d65f6f70SBen Gras }
523d65f6f70SBen Gras
524d65f6f70SBen Gras /* The macro is sane. Jump to the next word. */
525d65f6f70SBen Gras
52692395e9cSLionel Sambuc while (buf[offs] && ' ' == buf[offs])
52792395e9cSLionel Sambuc offs++;
528d65f6f70SBen Gras
529d65f6f70SBen Gras /*
530d65f6f70SBen Gras * Trailing whitespace. Note that tabs are allowed to be passed
531d65f6f70SBen Gras * into the parser as "text", so we only warn about spaces here.
532d65f6f70SBen Gras */
533d65f6f70SBen Gras
53492395e9cSLionel Sambuc if ('\0' == buf[offs] && ' ' == buf[offs - 1])
535*0a6a1f1dSLionel Sambuc man_pmsg(man, ln, offs - 1, MANDOCERR_EOLNSPACE);
536d65f6f70SBen Gras
537d65f6f70SBen Gras /*
538d65f6f70SBen Gras * Remove prior ELINE macro, as it's being clobbered by a new
539d65f6f70SBen Gras * macro. Note that NSCOPED macros do not close out ELINE
540d65f6f70SBen Gras * macros---they don't print text---so we let those slip by.
541d65f6f70SBen Gras */
542d65f6f70SBen Gras
543d65f6f70SBen Gras if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
544*0a6a1f1dSLionel Sambuc man->flags & MAN_ELINE) {
545*0a6a1f1dSLionel Sambuc n = man->last;
546d65f6f70SBen Gras assert(MAN_TEXT != n->type);
547d65f6f70SBen Gras
548d65f6f70SBen Gras /* Remove repeated NSCOPED macros causing ELINE. */
549d65f6f70SBen Gras
550d65f6f70SBen Gras if (MAN_NSCOPED & man_macros[n->tok].flags)
551d65f6f70SBen Gras n = n->parent;
552d65f6f70SBen Gras
553*0a6a1f1dSLionel Sambuc mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line,
55492395e9cSLionel Sambuc n->pos, "%s breaks %s", man_macronames[tok],
55592395e9cSLionel Sambuc man_macronames[n->tok]);
556d65f6f70SBen Gras
557*0a6a1f1dSLionel Sambuc man_node_delete(man, n);
558*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_ELINE;
559d65f6f70SBen Gras }
560d65f6f70SBen Gras
561d65f6f70SBen Gras /*
56292395e9cSLionel Sambuc * Remove prior BLINE macro that is being clobbered.
56392395e9cSLionel Sambuc */
564*0a6a1f1dSLionel Sambuc if ((man->flags & MAN_BLINE) &&
56592395e9cSLionel Sambuc (MAN_BSCOPE & man_macros[tok].flags)) {
566*0a6a1f1dSLionel Sambuc n = man->last;
56792395e9cSLionel Sambuc
56892395e9cSLionel Sambuc /* Might be a text node like 8 in
56992395e9cSLionel Sambuc * .TP 8
57092395e9cSLionel Sambuc * .SH foo
57192395e9cSLionel Sambuc */
57292395e9cSLionel Sambuc if (MAN_TEXT == n->type)
57392395e9cSLionel Sambuc n = n->parent;
57492395e9cSLionel Sambuc
57592395e9cSLionel Sambuc /* Remove element that didn't end BLINE, if any. */
57692395e9cSLionel Sambuc if ( ! (MAN_BSCOPE & man_macros[n->tok].flags))
57792395e9cSLionel Sambuc n = n->parent;
57892395e9cSLionel Sambuc
57992395e9cSLionel Sambuc assert(MAN_HEAD == n->type);
58092395e9cSLionel Sambuc n = n->parent;
58192395e9cSLionel Sambuc assert(MAN_BLOCK == n->type);
58292395e9cSLionel Sambuc assert(MAN_SCOPED & man_macros[n->tok].flags);
58392395e9cSLionel Sambuc
584*0a6a1f1dSLionel Sambuc mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line,
58592395e9cSLionel Sambuc n->pos, "%s breaks %s", man_macronames[tok],
58692395e9cSLionel Sambuc man_macronames[n->tok]);
58792395e9cSLionel Sambuc
588*0a6a1f1dSLionel Sambuc man_node_delete(man, n);
589*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_BLINE;
59092395e9cSLionel Sambuc }
59192395e9cSLionel Sambuc
59292395e9cSLionel Sambuc /*
593d65f6f70SBen Gras * Save the fact that we're in the next-line for a block. In
594d65f6f70SBen Gras * this way, embedded roff instructions can "remember" state
595d65f6f70SBen Gras * when they exit.
596d65f6f70SBen Gras */
597d65f6f70SBen Gras
598*0a6a1f1dSLionel Sambuc if (MAN_BLINE & man->flags)
599*0a6a1f1dSLionel Sambuc man->flags |= MAN_BPLINE;
600d65f6f70SBen Gras
601d65f6f70SBen Gras /* Call to handler... */
602d65f6f70SBen Gras
603d65f6f70SBen Gras assert(man_macros[tok].fp);
604*0a6a1f1dSLionel Sambuc if ( ! (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf))
605d65f6f70SBen Gras goto err;
606d65f6f70SBen Gras
607d65f6f70SBen Gras /*
608d65f6f70SBen Gras * We weren't in a block-line scope when entering the
609d65f6f70SBen Gras * above-parsed macro, so return.
610d65f6f70SBen Gras */
611d65f6f70SBen Gras
612*0a6a1f1dSLionel Sambuc if ( ! (MAN_BPLINE & man->flags)) {
613*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_ILINE;
614d65f6f70SBen Gras return(1);
615d65f6f70SBen Gras }
616*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_BPLINE;
617d65f6f70SBen Gras
618d65f6f70SBen Gras /*
619d65f6f70SBen Gras * If we're in a block scope, then allow this macro to slip by
620d65f6f70SBen Gras * without closing scope around it.
621d65f6f70SBen Gras */
622d65f6f70SBen Gras
623*0a6a1f1dSLionel Sambuc if (MAN_ILINE & man->flags) {
624*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_ILINE;
625d65f6f70SBen Gras return(1);
626d65f6f70SBen Gras }
627d65f6f70SBen Gras
628d65f6f70SBen Gras /*
629d65f6f70SBen Gras * If we've opened a new next-line element scope, then return
630d65f6f70SBen Gras * now, as the next line will close out the block scope.
631d65f6f70SBen Gras */
632d65f6f70SBen Gras
633*0a6a1f1dSLionel Sambuc if (MAN_ELINE & man->flags)
634d65f6f70SBen Gras return(1);
635d65f6f70SBen Gras
636d65f6f70SBen Gras /* Close out the block scope opened in the prior line. */
637d65f6f70SBen Gras
638*0a6a1f1dSLionel Sambuc assert(MAN_BLINE & man->flags);
639*0a6a1f1dSLionel Sambuc man->flags &= ~MAN_BLINE;
640d65f6f70SBen Gras
641*0a6a1f1dSLionel Sambuc if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
642d65f6f70SBen Gras return(0);
643*0a6a1f1dSLionel Sambuc return(man_body_alloc(man, ln, ppos, man->last->tok));
644d65f6f70SBen Gras
645d65f6f70SBen Gras err: /* Error out. */
646d65f6f70SBen Gras
647*0a6a1f1dSLionel Sambuc man->flags |= MAN_HALT;
648d65f6f70SBen Gras return(0);
649d65f6f70SBen Gras }
650d65f6f70SBen Gras
651d65f6f70SBen Gras /*
652*0a6a1f1dSLionel Sambuc * Unlink a node from its context. If "man" is provided, the last parse
653d65f6f70SBen Gras * point will also be adjusted accordingly.
654d65f6f70SBen Gras */
655d65f6f70SBen Gras static void
man_node_unlink(struct man * man,struct man_node * n)656*0a6a1f1dSLionel Sambuc man_node_unlink(struct man *man, struct man_node *n)
657d65f6f70SBen Gras {
658d65f6f70SBen Gras
659d65f6f70SBen Gras /* Adjust siblings. */
660d65f6f70SBen Gras
661d65f6f70SBen Gras if (n->prev)
662d65f6f70SBen Gras n->prev->next = n->next;
663d65f6f70SBen Gras if (n->next)
664d65f6f70SBen Gras n->next->prev = n->prev;
665d65f6f70SBen Gras
666d65f6f70SBen Gras /* Adjust parent. */
667d65f6f70SBen Gras
668d65f6f70SBen Gras if (n->parent) {
669d65f6f70SBen Gras n->parent->nchild--;
670d65f6f70SBen Gras if (n->parent->child == n)
671d65f6f70SBen Gras n->parent->child = n->prev ? n->prev : n->next;
672d65f6f70SBen Gras }
673d65f6f70SBen Gras
674d65f6f70SBen Gras /* Adjust parse point, if applicable. */
675d65f6f70SBen Gras
676*0a6a1f1dSLionel Sambuc if (man && man->last == n) {
677d65f6f70SBen Gras /*XXX: this can occur when bailing from validation. */
678d65f6f70SBen Gras /*assert(NULL == n->next);*/
679d65f6f70SBen Gras if (n->prev) {
680*0a6a1f1dSLionel Sambuc man->last = n->prev;
681*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_SIBLING;
682d65f6f70SBen Gras } else {
683*0a6a1f1dSLionel Sambuc man->last = n->parent;
684*0a6a1f1dSLionel Sambuc man->next = MAN_NEXT_CHILD;
685d65f6f70SBen Gras }
686d65f6f70SBen Gras }
687d65f6f70SBen Gras
688*0a6a1f1dSLionel Sambuc if (man && man->first == n)
689*0a6a1f1dSLionel Sambuc man->first = NULL;
690d65f6f70SBen Gras }
69192395e9cSLionel Sambuc
69292395e9cSLionel Sambuc const struct mparse *
man_mparse(const struct man * man)693*0a6a1f1dSLionel Sambuc man_mparse(const struct man *man)
69492395e9cSLionel Sambuc {
69592395e9cSLionel Sambuc
696*0a6a1f1dSLionel Sambuc assert(man && man->parse);
697*0a6a1f1dSLionel Sambuc return(man->parse);
69892395e9cSLionel Sambuc }
699