xref: /minix3/external/bsd/mdocml/dist/man.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
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