1241675Suqs/*	$Id: man_term.c,v 1.127 2012/01/03 15:16:24 kristaps Exp $ */
2241675Suqs/*
3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5241675Suqs *
6241675Suqs * Permission to use, copy, modify, and distribute this software for any
7241675Suqs * purpose with or without fee is hereby granted, provided that the above
8241675Suqs * copyright notice and this permission notice appear in all copies.
9241675Suqs *
10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17241675Suqs */
18241675Suqs#ifdef HAVE_CONFIG_H
19241675Suqs#include "config.h"
20241675Suqs#endif
21241675Suqs
22241675Suqs#include <sys/types.h>
23241675Suqs
24241675Suqs#include <assert.h>
25241675Suqs#include <ctype.h>
26241675Suqs#include <stdio.h>
27241675Suqs#include <stdlib.h>
28241675Suqs#include <string.h>
29241675Suqs
30241675Suqs#include "mandoc.h"
31241675Suqs#include "out.h"
32241675Suqs#include "man.h"
33241675Suqs#include "term.h"
34241675Suqs#include "main.h"
35241675Suqs
36241675Suqs#define	MAXMARGINS	  64 /* maximum number of indented scopes */
37241675Suqs
38241675Suqs/* FIXME: have PD set the default vspace width. */
39241675Suqs
40241675Suqsstruct	mtermp {
41241675Suqs	int		  fl;
42241675Suqs#define	MANT_LITERAL	 (1 << 0)
43241675Suqs	size_t		  lmargin[MAXMARGINS]; /* margins (incl. visible page) */
44241675Suqs	int		  lmargincur; /* index of current margin */
45241675Suqs	int		  lmarginsz; /* actual number of nested margins */
46241675Suqs	size_t		  offset; /* default offset to visible page */
47241675Suqs};
48241675Suqs
49241675Suqs#define	DECL_ARGS 	  struct termp *p, \
50241675Suqs			  struct mtermp *mt, \
51241675Suqs			  const struct man_node *n, \
52241675Suqs			  const struct man_meta *m
53241675Suqs
54241675Suqsstruct	termact {
55241675Suqs	int		(*pre)(DECL_ARGS);
56241675Suqs	void		(*post)(DECL_ARGS);
57241675Suqs	int		  flags;
58241675Suqs#define	MAN_NOTEXT	 (1 << 0) /* Never has text children. */
59241675Suqs};
60241675Suqs
61241675Suqsstatic	int		  a2width(const struct termp *, const char *);
62241675Suqsstatic	size_t		  a2height(const struct termp *, const char *);
63241675Suqs
64241675Suqsstatic	void		  print_man_nodelist(DECL_ARGS);
65241675Suqsstatic	void		  print_man_node(DECL_ARGS);
66241675Suqsstatic	void		  print_man_head(struct termp *, const void *);
67241675Suqsstatic	void		  print_man_foot(struct termp *, const void *);
68241675Suqsstatic	void		  print_bvspace(struct termp *,
69241675Suqs				const struct man_node *);
70241675Suqs
71241675Suqsstatic	int		  pre_B(DECL_ARGS);
72241675Suqsstatic	int		  pre_HP(DECL_ARGS);
73241675Suqsstatic	int		  pre_I(DECL_ARGS);
74241675Suqsstatic	int		  pre_IP(DECL_ARGS);
75241675Suqsstatic	int		  pre_OP(DECL_ARGS);
76241675Suqsstatic	int		  pre_PP(DECL_ARGS);
77241675Suqsstatic	int		  pre_RS(DECL_ARGS);
78241675Suqsstatic	int		  pre_SH(DECL_ARGS);
79241675Suqsstatic	int		  pre_SS(DECL_ARGS);
80241675Suqsstatic	int		  pre_TP(DECL_ARGS);
81241675Suqsstatic	int		  pre_alternate(DECL_ARGS);
82241675Suqsstatic	int		  pre_ft(DECL_ARGS);
83241675Suqsstatic	int		  pre_ign(DECL_ARGS);
84241675Suqsstatic	int		  pre_in(DECL_ARGS);
85241675Suqsstatic	int		  pre_literal(DECL_ARGS);
86241675Suqsstatic	int		  pre_sp(DECL_ARGS);
87241675Suqs
88241675Suqsstatic	void		  post_IP(DECL_ARGS);
89241675Suqsstatic	void		  post_HP(DECL_ARGS);
90241675Suqsstatic	void		  post_RS(DECL_ARGS);
91241675Suqsstatic	void		  post_SH(DECL_ARGS);
92241675Suqsstatic	void		  post_SS(DECL_ARGS);
93241675Suqsstatic	void		  post_TP(DECL_ARGS);
94241675Suqs
95241675Suqsstatic	const struct termact termacts[MAN_MAX] = {
96241675Suqs	{ pre_sp, NULL, MAN_NOTEXT }, /* br */
97241675Suqs	{ NULL, NULL, 0 }, /* TH */
98241675Suqs	{ pre_SH, post_SH, 0 }, /* SH */
99241675Suqs	{ pre_SS, post_SS, 0 }, /* SS */
100241675Suqs	{ pre_TP, post_TP, 0 }, /* TP */
101241675Suqs	{ pre_PP, NULL, 0 }, /* LP */
102241675Suqs	{ pre_PP, NULL, 0 }, /* PP */
103241675Suqs	{ pre_PP, NULL, 0 }, /* P */
104241675Suqs	{ pre_IP, post_IP, 0 }, /* IP */
105241675Suqs	{ pre_HP, post_HP, 0 }, /* HP */
106241675Suqs	{ NULL, NULL, 0 }, /* SM */
107241675Suqs	{ pre_B, NULL, 0 }, /* SB */
108241675Suqs	{ pre_alternate, NULL, 0 }, /* BI */
109241675Suqs	{ pre_alternate, NULL, 0 }, /* IB */
110241675Suqs	{ pre_alternate, NULL, 0 }, /* BR */
111241675Suqs	{ pre_alternate, NULL, 0 }, /* RB */
112241675Suqs	{ NULL, NULL, 0 }, /* R */
113241675Suqs	{ pre_B, NULL, 0 }, /* B */
114241675Suqs	{ pre_I, NULL, 0 }, /* I */
115241675Suqs	{ pre_alternate, NULL, 0 }, /* IR */
116241675Suqs	{ pre_alternate, NULL, 0 }, /* RI */
117241675Suqs	{ pre_ign, NULL, MAN_NOTEXT }, /* na */
118241675Suqs	{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
119241675Suqs	{ pre_literal, NULL, 0 }, /* nf */
120241675Suqs	{ pre_literal, NULL, 0 }, /* fi */
121241675Suqs	{ NULL, NULL, 0 }, /* RE */
122241675Suqs	{ pre_RS, post_RS, 0 }, /* RS */
123241675Suqs	{ pre_ign, NULL, 0 }, /* DT */
124241675Suqs	{ pre_ign, NULL, 0 }, /* UC */
125241675Suqs	{ pre_ign, NULL, 0 }, /* PD */
126241675Suqs	{ pre_ign, NULL, 0 }, /* AT */
127241675Suqs	{ pre_in, NULL, MAN_NOTEXT }, /* in */
128241675Suqs	{ pre_ft, NULL, MAN_NOTEXT }, /* ft */
129241675Suqs	{ pre_OP, NULL, 0 }, /* OP */
130241675Suqs};
131241675Suqs
132241675Suqs
133241675Suqs
134241675Suqsvoid
135241675Suqsterminal_man(void *arg, const struct man *man)
136241675Suqs{
137241675Suqs	struct termp		*p;
138241675Suqs	const struct man_node	*n;
139241675Suqs	const struct man_meta	*m;
140241675Suqs	struct mtermp		 mt;
141241675Suqs
142241675Suqs	p = (struct termp *)arg;
143241675Suqs
144241675Suqs	if (0 == p->defindent)
145241675Suqs		p->defindent = 7;
146241675Suqs
147241675Suqs	p->overstep = 0;
148241675Suqs	p->maxrmargin = p->defrmargin;
149241675Suqs	p->tabwidth = term_len(p, 5);
150241675Suqs
151241675Suqs	if (NULL == p->symtab)
152241675Suqs		p->symtab = mchars_alloc();
153241675Suqs
154241675Suqs	n = man_node(man);
155241675Suqs	m = man_meta(man);
156241675Suqs
157241675Suqs	term_begin(p, print_man_head, print_man_foot, m);
158241675Suqs	p->flags |= TERMP_NOSPACE;
159241675Suqs
160241675Suqs	memset(&mt, 0, sizeof(struct mtermp));
161241675Suqs
162241675Suqs	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
163241675Suqs	mt.offset = term_len(p, p->defindent);
164241675Suqs
165241675Suqs	if (n->child)
166241675Suqs		print_man_nodelist(p, &mt, n->child, m);
167241675Suqs
168241675Suqs	term_end(p);
169241675Suqs}
170241675Suqs
171241675Suqs
172241675Suqsstatic size_t
173241675Suqsa2height(const struct termp *p, const char *cp)
174241675Suqs{
175241675Suqs	struct roffsu	 su;
176241675Suqs
177241675Suqs	if ( ! a2roffsu(cp, &su, SCALE_VS))
178241675Suqs		SCALE_VS_INIT(&su, atoi(cp));
179241675Suqs
180241675Suqs	return(term_vspan(p, &su));
181241675Suqs}
182241675Suqs
183241675Suqs
184241675Suqsstatic int
185241675Suqsa2width(const struct termp *p, const char *cp)
186241675Suqs{
187241675Suqs	struct roffsu	 su;
188241675Suqs
189241675Suqs	if ( ! a2roffsu(cp, &su, SCALE_BU))
190241675Suqs		return(-1);
191241675Suqs
192241675Suqs	return((int)term_hspan(p, &su));
193241675Suqs}
194241675Suqs
195241675Suqs/*
196241675Suqs * Printing leading vertical space before a block.
197241675Suqs * This is used for the paragraph macros.
198241675Suqs * The rules are pretty simple, since there's very little nesting going
199241675Suqs * on here.  Basically, if we're the first within another block (SS/SH),
200241675Suqs * then don't emit vertical space.  If we are (RS), then do.  If not the
201241675Suqs * first, print it.
202241675Suqs */
203241675Suqsstatic void
204241675Suqsprint_bvspace(struct termp *p, const struct man_node *n)
205241675Suqs{
206241675Suqs
207241675Suqs	term_newln(p);
208241675Suqs
209241675Suqs	if (n->body && n->body->child)
210241675Suqs		if (MAN_TBL == n->body->child->type)
211241675Suqs			return;
212241675Suqs
213241675Suqs	if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
214241675Suqs		if (NULL == n->prev)
215241675Suqs			return;
216241675Suqs
217241675Suqs	term_vspace(p);
218241675Suqs}
219241675Suqs
220241675Suqs/* ARGSUSED */
221241675Suqsstatic int
222241675Suqspre_ign(DECL_ARGS)
223241675Suqs{
224241675Suqs
225241675Suqs	return(0);
226241675Suqs}
227241675Suqs
228241675Suqs
229241675Suqs/* ARGSUSED */
230241675Suqsstatic int
231241675Suqspre_I(DECL_ARGS)
232241675Suqs{
233241675Suqs
234241675Suqs	term_fontrepl(p, TERMFONT_UNDER);
235241675Suqs	return(1);
236241675Suqs}
237241675Suqs
238241675Suqs
239241675Suqs/* ARGSUSED */
240241675Suqsstatic int
241241675Suqspre_literal(DECL_ARGS)
242241675Suqs{
243241675Suqs
244241675Suqs	term_newln(p);
245241675Suqs
246241675Suqs	if (MAN_nf == n->tok)
247241675Suqs		mt->fl |= MANT_LITERAL;
248241675Suqs	else
249241675Suqs		mt->fl &= ~MANT_LITERAL;
250241675Suqs
251241675Suqs	/*
252241675Suqs	 * Unlike .IP and .TP, .HP does not have a HEAD.
253241675Suqs	 * So in case a second call to term_flushln() is needed,
254241675Suqs	 * indentation has to be set up explicitly.
255241675Suqs	 */
256241675Suqs	if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
257241675Suqs		p->offset = p->rmargin;
258241675Suqs		p->rmargin = p->maxrmargin;
259241675Suqs		p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE);
260241675Suqs		p->flags |= TERMP_NOSPACE;
261241675Suqs	}
262241675Suqs
263241675Suqs	return(0);
264241675Suqs}
265241675Suqs
266241675Suqs/* ARGSUSED */
267241675Suqsstatic int
268241675Suqspre_alternate(DECL_ARGS)
269241675Suqs{
270241675Suqs	enum termfont		 font[2];
271241675Suqs	const struct man_node	*nn;
272241675Suqs	int			 savelit, i;
273241675Suqs
274241675Suqs	switch (n->tok) {
275241675Suqs	case (MAN_RB):
276241675Suqs		font[0] = TERMFONT_NONE;
277241675Suqs		font[1] = TERMFONT_BOLD;
278241675Suqs		break;
279241675Suqs	case (MAN_RI):
280241675Suqs		font[0] = TERMFONT_NONE;
281241675Suqs		font[1] = TERMFONT_UNDER;
282241675Suqs		break;
283241675Suqs	case (MAN_BR):
284241675Suqs		font[0] = TERMFONT_BOLD;
285241675Suqs		font[1] = TERMFONT_NONE;
286241675Suqs		break;
287241675Suqs	case (MAN_BI):
288241675Suqs		font[0] = TERMFONT_BOLD;
289241675Suqs		font[1] = TERMFONT_UNDER;
290241675Suqs		break;
291241675Suqs	case (MAN_IR):
292241675Suqs		font[0] = TERMFONT_UNDER;
293241675Suqs		font[1] = TERMFONT_NONE;
294241675Suqs		break;
295241675Suqs	case (MAN_IB):
296241675Suqs		font[0] = TERMFONT_UNDER;
297241675Suqs		font[1] = TERMFONT_BOLD;
298241675Suqs		break;
299241675Suqs	default:
300241675Suqs		abort();
301241675Suqs	}
302241675Suqs
303241675Suqs	savelit = MANT_LITERAL & mt->fl;
304241675Suqs	mt->fl &= ~MANT_LITERAL;
305241675Suqs
306241675Suqs	for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
307241675Suqs		term_fontrepl(p, font[i]);
308241675Suqs		if (savelit && NULL == nn->next)
309241675Suqs			mt->fl |= MANT_LITERAL;
310241675Suqs		print_man_node(p, mt, nn, m);
311241675Suqs		if (nn->next)
312241675Suqs			p->flags |= TERMP_NOSPACE;
313241675Suqs	}
314241675Suqs
315241675Suqs	return(0);
316241675Suqs}
317241675Suqs
318241675Suqs/* ARGSUSED */
319241675Suqsstatic int
320241675Suqspre_B(DECL_ARGS)
321241675Suqs{
322241675Suqs
323241675Suqs	term_fontrepl(p, TERMFONT_BOLD);
324241675Suqs	return(1);
325241675Suqs}
326241675Suqs
327241675Suqs/* ARGSUSED */
328241675Suqsstatic int
329241675Suqspre_OP(DECL_ARGS)
330241675Suqs{
331241675Suqs
332241675Suqs	term_word(p, "[");
333241675Suqs	p->flags |= TERMP_NOSPACE;
334241675Suqs
335241675Suqs	if (NULL != (n = n->child)) {
336241675Suqs		term_fontrepl(p, TERMFONT_BOLD);
337241675Suqs		term_word(p, n->string);
338241675Suqs	}
339241675Suqs	if (NULL != n && NULL != n->next) {
340241675Suqs		term_fontrepl(p, TERMFONT_UNDER);
341241675Suqs		term_word(p, n->next->string);
342241675Suqs	}
343241675Suqs
344241675Suqs	term_fontrepl(p, TERMFONT_NONE);
345241675Suqs	p->flags |= TERMP_NOSPACE;
346241675Suqs	term_word(p, "]");
347241675Suqs	return(0);
348241675Suqs}
349241675Suqs
350241675Suqs/* ARGSUSED */
351241675Suqsstatic int
352241675Suqspre_ft(DECL_ARGS)
353241675Suqs{
354241675Suqs	const char	*cp;
355241675Suqs
356241675Suqs	if (NULL == n->child) {
357241675Suqs		term_fontlast(p);
358241675Suqs		return(0);
359241675Suqs	}
360241675Suqs
361241675Suqs	cp = n->child->string;
362241675Suqs	switch (*cp) {
363241675Suqs	case ('4'):
364241675Suqs		/* FALLTHROUGH */
365241675Suqs	case ('3'):
366241675Suqs		/* FALLTHROUGH */
367241675Suqs	case ('B'):
368241675Suqs		term_fontrepl(p, TERMFONT_BOLD);
369241675Suqs		break;
370241675Suqs	case ('2'):
371241675Suqs		/* FALLTHROUGH */
372241675Suqs	case ('I'):
373241675Suqs		term_fontrepl(p, TERMFONT_UNDER);
374241675Suqs		break;
375241675Suqs	case ('P'):
376241675Suqs		term_fontlast(p);
377241675Suqs		break;
378241675Suqs	case ('1'):
379241675Suqs		/* FALLTHROUGH */
380241675Suqs	case ('C'):
381241675Suqs		/* FALLTHROUGH */
382241675Suqs	case ('R'):
383241675Suqs		term_fontrepl(p, TERMFONT_NONE);
384241675Suqs		break;
385241675Suqs	default:
386241675Suqs		break;
387241675Suqs	}
388241675Suqs	return(0);
389241675Suqs}
390241675Suqs
391241675Suqs/* ARGSUSED */
392241675Suqsstatic int
393241675Suqspre_in(DECL_ARGS)
394241675Suqs{
395241675Suqs	int		 len, less;
396241675Suqs	size_t		 v;
397241675Suqs	const char	*cp;
398241675Suqs
399241675Suqs	term_newln(p);
400241675Suqs
401241675Suqs	if (NULL == n->child) {
402241675Suqs		p->offset = mt->offset;
403241675Suqs		return(0);
404241675Suqs	}
405241675Suqs
406241675Suqs	cp = n->child->string;
407241675Suqs	less = 0;
408241675Suqs
409241675Suqs	if ('-' == *cp)
410241675Suqs		less = -1;
411241675Suqs	else if ('+' == *cp)
412241675Suqs		less = 1;
413241675Suqs	else
414241675Suqs		cp--;
415241675Suqs
416241675Suqs	if ((len = a2width(p, ++cp)) < 0)
417241675Suqs		return(0);
418241675Suqs
419241675Suqs	v = (size_t)len;
420241675Suqs
421241675Suqs	if (less < 0)
422241675Suqs		p->offset -= p->offset > v ? v : p->offset;
423241675Suqs	else if (less > 0)
424241675Suqs		p->offset += v;
425241675Suqs	else
426241675Suqs		p->offset = v;
427241675Suqs
428241675Suqs	/* Don't let this creep beyond the right margin. */
429241675Suqs
430241675Suqs	if (p->offset > p->rmargin)
431241675Suqs		p->offset = p->rmargin;
432241675Suqs
433241675Suqs	return(0);
434241675Suqs}
435241675Suqs
436241675Suqs
437241675Suqs/* ARGSUSED */
438241675Suqsstatic int
439241675Suqspre_sp(DECL_ARGS)
440241675Suqs{
441241675Suqs	size_t		 i, len;
442241675Suqs
443241675Suqs	if ((NULL == n->prev && n->parent)) {
444241675Suqs		if (MAN_SS == n->parent->tok)
445241675Suqs			return(0);
446241675Suqs		if (MAN_SH == n->parent->tok)
447241675Suqs			return(0);
448241675Suqs	}
449241675Suqs
450241675Suqs	switch (n->tok) {
451241675Suqs	case (MAN_br):
452241675Suqs		len = 0;
453241675Suqs		break;
454241675Suqs	default:
455241675Suqs		len = n->child ? a2height(p, n->child->string) : 1;
456241675Suqs		break;
457241675Suqs	}
458241675Suqs
459241675Suqs	if (0 == len)
460241675Suqs		term_newln(p);
461241675Suqs	for (i = 0; i < len; i++)
462241675Suqs		term_vspace(p);
463241675Suqs
464241675Suqs	return(0);
465241675Suqs}
466241675Suqs
467241675Suqs
468241675Suqs/* ARGSUSED */
469241675Suqsstatic int
470241675Suqspre_HP(DECL_ARGS)
471241675Suqs{
472241675Suqs	size_t			 len, one;
473241675Suqs	int			 ival;
474241675Suqs	const struct man_node	*nn;
475241675Suqs
476241675Suqs	switch (n->type) {
477241675Suqs	case (MAN_BLOCK):
478241675Suqs		print_bvspace(p, n);
479241675Suqs		return(1);
480241675Suqs	case (MAN_BODY):
481241675Suqs		p->flags |= TERMP_NOBREAK;
482241675Suqs		p->flags |= TERMP_TWOSPACE;
483241675Suqs		break;
484241675Suqs	default:
485241675Suqs		return(0);
486241675Suqs	}
487241675Suqs
488241675Suqs	len = mt->lmargin[mt->lmargincur];
489241675Suqs	ival = -1;
490241675Suqs
491241675Suqs	/* Calculate offset. */
492241675Suqs
493241675Suqs	if (NULL != (nn = n->parent->head->child))
494241675Suqs		if ((ival = a2width(p, nn->string)) >= 0)
495241675Suqs			len = (size_t)ival;
496241675Suqs
497241675Suqs	one = term_len(p, 1);
498241675Suqs	if (len < one)
499241675Suqs		len = one;
500241675Suqs
501241675Suqs	p->offset = mt->offset;
502241675Suqs	p->rmargin = mt->offset + len;
503241675Suqs
504241675Suqs	if (ival >= 0)
505241675Suqs		mt->lmargin[mt->lmargincur] = (size_t)ival;
506241675Suqs
507241675Suqs	return(1);
508241675Suqs}
509241675Suqs
510241675Suqs
511241675Suqs/* ARGSUSED */
512241675Suqsstatic void
513241675Suqspost_HP(DECL_ARGS)
514241675Suqs{
515241675Suqs
516241675Suqs	switch (n->type) {
517241675Suqs	case (MAN_BLOCK):
518241675Suqs		term_flushln(p);
519241675Suqs		break;
520241675Suqs	case (MAN_BODY):
521241675Suqs		term_flushln(p);
522241675Suqs		p->flags &= ~TERMP_NOBREAK;
523241675Suqs		p->flags &= ~TERMP_TWOSPACE;
524241675Suqs		p->offset = mt->offset;
525241675Suqs		p->rmargin = p->maxrmargin;
526241675Suqs		break;
527241675Suqs	default:
528241675Suqs		break;
529241675Suqs	}
530241675Suqs}
531241675Suqs
532241675Suqs
533241675Suqs/* ARGSUSED */
534241675Suqsstatic int
535241675Suqspre_PP(DECL_ARGS)
536241675Suqs{
537241675Suqs
538241675Suqs	switch (n->type) {
539241675Suqs	case (MAN_BLOCK):
540241675Suqs		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
541241675Suqs		print_bvspace(p, n);
542241675Suqs		break;
543241675Suqs	default:
544241675Suqs		p->offset = mt->offset;
545241675Suqs		break;
546241675Suqs	}
547241675Suqs
548241675Suqs	return(MAN_HEAD != n->type);
549241675Suqs}
550241675Suqs
551241675Suqs
552241675Suqs/* ARGSUSED */
553241675Suqsstatic int
554241675Suqspre_IP(DECL_ARGS)
555241675Suqs{
556241675Suqs	const struct man_node	*nn;
557241675Suqs	size_t			 len;
558241675Suqs	int			 savelit, ival;
559241675Suqs
560241675Suqs	switch (n->type) {
561241675Suqs	case (MAN_BODY):
562241675Suqs		p->flags |= TERMP_NOSPACE;
563241675Suqs		break;
564241675Suqs	case (MAN_HEAD):
565241675Suqs		p->flags |= TERMP_NOBREAK;
566241675Suqs		break;
567241675Suqs	case (MAN_BLOCK):
568241675Suqs		print_bvspace(p, n);
569241675Suqs		/* FALLTHROUGH */
570241675Suqs	default:
571241675Suqs		return(1);
572241675Suqs	}
573241675Suqs
574241675Suqs	len = mt->lmargin[mt->lmargincur];
575241675Suqs	ival = -1;
576241675Suqs
577241675Suqs	/* Calculate the offset from the optional second argument. */
578241675Suqs	if (NULL != (nn = n->parent->head->child))
579241675Suqs		if (NULL != (nn = nn->next))
580241675Suqs			if ((ival = a2width(p, nn->string)) >= 0)
581241675Suqs				len = (size_t)ival;
582241675Suqs
583241675Suqs	switch (n->type) {
584241675Suqs	case (MAN_HEAD):
585241675Suqs		/* Handle zero-width lengths. */
586241675Suqs		if (0 == len)
587241675Suqs			len = term_len(p, 1);
588241675Suqs
589241675Suqs		p->offset = mt->offset;
590241675Suqs		p->rmargin = mt->offset + len;
591241675Suqs		if (ival < 0)
592241675Suqs			break;
593241675Suqs
594241675Suqs		/* Set the saved left-margin. */
595241675Suqs		mt->lmargin[mt->lmargincur] = (size_t)ival;
596241675Suqs
597241675Suqs		savelit = MANT_LITERAL & mt->fl;
598241675Suqs		mt->fl &= ~MANT_LITERAL;
599241675Suqs
600241675Suqs		if (n->child)
601241675Suqs			print_man_node(p, mt, n->child, m);
602241675Suqs
603241675Suqs		if (savelit)
604241675Suqs			mt->fl |= MANT_LITERAL;
605241675Suqs
606241675Suqs		return(0);
607241675Suqs	case (MAN_BODY):
608241675Suqs		p->offset = mt->offset + len;
609241675Suqs		p->rmargin = p->maxrmargin;
610241675Suqs		break;
611241675Suqs	default:
612241675Suqs		break;
613241675Suqs	}
614241675Suqs
615241675Suqs	return(1);
616241675Suqs}
617241675Suqs
618241675Suqs
619241675Suqs/* ARGSUSED */
620241675Suqsstatic void
621241675Suqspost_IP(DECL_ARGS)
622241675Suqs{
623241675Suqs
624241675Suqs	switch (n->type) {
625241675Suqs	case (MAN_HEAD):
626241675Suqs		term_flushln(p);
627241675Suqs		p->flags &= ~TERMP_NOBREAK;
628241675Suqs		p->rmargin = p->maxrmargin;
629241675Suqs		break;
630241675Suqs	case (MAN_BODY):
631241675Suqs		term_newln(p);
632241675Suqs		break;
633241675Suqs	default:
634241675Suqs		break;
635241675Suqs	}
636241675Suqs}
637241675Suqs
638241675Suqs
639241675Suqs/* ARGSUSED */
640241675Suqsstatic int
641241675Suqspre_TP(DECL_ARGS)
642241675Suqs{
643241675Suqs	const struct man_node	*nn;
644241675Suqs	size_t			 len;
645241675Suqs	int			 savelit, ival;
646241675Suqs
647241675Suqs	switch (n->type) {
648241675Suqs	case (MAN_HEAD):
649241675Suqs		p->flags |= TERMP_NOBREAK;
650241675Suqs		break;
651241675Suqs	case (MAN_BODY):
652241675Suqs		p->flags |= TERMP_NOSPACE;
653241675Suqs		break;
654241675Suqs	case (MAN_BLOCK):
655241675Suqs		print_bvspace(p, n);
656241675Suqs		/* FALLTHROUGH */
657241675Suqs	default:
658241675Suqs		return(1);
659241675Suqs	}
660241675Suqs
661241675Suqs	len = (size_t)mt->lmargin[mt->lmargincur];
662241675Suqs	ival = -1;
663241675Suqs
664241675Suqs	/* Calculate offset. */
665241675Suqs
666241675Suqs	if (NULL != (nn = n->parent->head->child))
667241675Suqs		if (nn->string && nn->parent->line == nn->line)
668241675Suqs			if ((ival = a2width(p, nn->string)) >= 0)
669241675Suqs				len = (size_t)ival;
670241675Suqs
671241675Suqs	switch (n->type) {
672241675Suqs	case (MAN_HEAD):
673241675Suqs		/* Handle zero-length properly. */
674241675Suqs		if (0 == len)
675241675Suqs			len = term_len(p, 1);
676241675Suqs
677241675Suqs		p->offset = mt->offset;
678241675Suqs		p->rmargin = mt->offset + len;
679241675Suqs
680241675Suqs		savelit = MANT_LITERAL & mt->fl;
681241675Suqs		mt->fl &= ~MANT_LITERAL;
682241675Suqs
683241675Suqs		/* Don't print same-line elements. */
684241675Suqs		for (nn = n->child; nn; nn = nn->next)
685241675Suqs			if (nn->line > n->line)
686241675Suqs				print_man_node(p, mt, nn, m);
687241675Suqs
688241675Suqs		if (savelit)
689241675Suqs			mt->fl |= MANT_LITERAL;
690241675Suqs		if (ival >= 0)
691241675Suqs			mt->lmargin[mt->lmargincur] = (size_t)ival;
692241675Suqs
693241675Suqs		return(0);
694241675Suqs	case (MAN_BODY):
695241675Suqs		p->offset = mt->offset + len;
696241675Suqs		p->rmargin = p->maxrmargin;
697241675Suqs		break;
698241675Suqs	default:
699241675Suqs		break;
700241675Suqs	}
701241675Suqs
702241675Suqs	return(1);
703241675Suqs}
704241675Suqs
705241675Suqs
706241675Suqs/* ARGSUSED */
707241675Suqsstatic void
708241675Suqspost_TP(DECL_ARGS)
709241675Suqs{
710241675Suqs
711241675Suqs	switch (n->type) {
712241675Suqs	case (MAN_HEAD):
713241675Suqs		term_flushln(p);
714241675Suqs		p->flags &= ~TERMP_NOBREAK;
715241675Suqs		p->flags &= ~TERMP_TWOSPACE;
716241675Suqs		p->rmargin = p->maxrmargin;
717241675Suqs		break;
718241675Suqs	case (MAN_BODY):
719241675Suqs		term_newln(p);
720241675Suqs		break;
721241675Suqs	default:
722241675Suqs		break;
723241675Suqs	}
724241675Suqs}
725241675Suqs
726241675Suqs
727241675Suqs/* ARGSUSED */
728241675Suqsstatic int
729241675Suqspre_SS(DECL_ARGS)
730241675Suqs{
731241675Suqs
732241675Suqs	switch (n->type) {
733241675Suqs	case (MAN_BLOCK):
734241675Suqs		mt->fl &= ~MANT_LITERAL;
735241675Suqs		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
736241675Suqs		mt->offset = term_len(p, p->defindent);
737241675Suqs		/* If following a prior empty `SS', no vspace. */
738241675Suqs		if (n->prev && MAN_SS == n->prev->tok)
739241675Suqs			if (NULL == n->prev->body->child)
740241675Suqs				break;
741241675Suqs		if (NULL == n->prev)
742241675Suqs			break;
743241675Suqs		term_vspace(p);
744241675Suqs		break;
745241675Suqs	case (MAN_HEAD):
746241675Suqs		term_fontrepl(p, TERMFONT_BOLD);
747241675Suqs		p->offset = term_len(p, p->defindent/2);
748241675Suqs		break;
749241675Suqs	case (MAN_BODY):
750241675Suqs		p->offset = mt->offset;
751241675Suqs		break;
752241675Suqs	default:
753241675Suqs		break;
754241675Suqs	}
755241675Suqs
756241675Suqs	return(1);
757241675Suqs}
758241675Suqs
759241675Suqs
760241675Suqs/* ARGSUSED */
761241675Suqsstatic void
762241675Suqspost_SS(DECL_ARGS)
763241675Suqs{
764241675Suqs
765241675Suqs	switch (n->type) {
766241675Suqs	case (MAN_HEAD):
767241675Suqs		term_newln(p);
768241675Suqs		break;
769241675Suqs	case (MAN_BODY):
770241675Suqs		term_newln(p);
771241675Suqs		break;
772241675Suqs	default:
773241675Suqs		break;
774241675Suqs	}
775241675Suqs}
776241675Suqs
777241675Suqs
778241675Suqs/* ARGSUSED */
779241675Suqsstatic int
780241675Suqspre_SH(DECL_ARGS)
781241675Suqs{
782241675Suqs
783241675Suqs	switch (n->type) {
784241675Suqs	case (MAN_BLOCK):
785241675Suqs		mt->fl &= ~MANT_LITERAL;
786241675Suqs		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
787241675Suqs		mt->offset = term_len(p, p->defindent);
788241675Suqs		/* If following a prior empty `SH', no vspace. */
789241675Suqs		if (n->prev && MAN_SH == n->prev->tok)
790241675Suqs			if (NULL == n->prev->body->child)
791241675Suqs				break;
792241675Suqs		/* If the first macro, no vspae. */
793241675Suqs		if (NULL == n->prev)
794241675Suqs			break;
795241675Suqs		term_vspace(p);
796241675Suqs		break;
797241675Suqs	case (MAN_HEAD):
798241675Suqs		term_fontrepl(p, TERMFONT_BOLD);
799241675Suqs		p->offset = 0;
800241675Suqs		break;
801241675Suqs	case (MAN_BODY):
802241675Suqs		p->offset = mt->offset;
803241675Suqs		break;
804241675Suqs	default:
805241675Suqs		break;
806241675Suqs	}
807241675Suqs
808241675Suqs	return(1);
809241675Suqs}
810241675Suqs
811241675Suqs
812241675Suqs/* ARGSUSED */
813241675Suqsstatic void
814241675Suqspost_SH(DECL_ARGS)
815241675Suqs{
816241675Suqs
817241675Suqs	switch (n->type) {
818241675Suqs	case (MAN_HEAD):
819241675Suqs		term_newln(p);
820241675Suqs		break;
821241675Suqs	case (MAN_BODY):
822241675Suqs		term_newln(p);
823241675Suqs		break;
824241675Suqs	default:
825241675Suqs		break;
826241675Suqs	}
827241675Suqs}
828241675Suqs
829241675Suqs/* ARGSUSED */
830241675Suqsstatic int
831241675Suqspre_RS(DECL_ARGS)
832241675Suqs{
833241675Suqs	int		 ival;
834241675Suqs	size_t		 sz;
835241675Suqs
836241675Suqs	switch (n->type) {
837241675Suqs	case (MAN_BLOCK):
838241675Suqs		term_newln(p);
839241675Suqs		return(1);
840241675Suqs	case (MAN_HEAD):
841241675Suqs		return(0);
842241675Suqs	default:
843241675Suqs		break;
844241675Suqs	}
845241675Suqs
846241675Suqs	sz = term_len(p, p->defindent);
847241675Suqs
848241675Suqs	if (NULL != (n = n->parent->head->child))
849241675Suqs		if ((ival = a2width(p, n->string)) >= 0)
850241675Suqs			sz = (size_t)ival;
851241675Suqs
852241675Suqs	mt->offset += sz;
853241675Suqs	p->rmargin = p->maxrmargin;
854241675Suqs	p->offset = mt->offset < p->rmargin ? mt->offset : p->rmargin;
855241675Suqs
856241675Suqs	if (++mt->lmarginsz < MAXMARGINS)
857241675Suqs		mt->lmargincur = mt->lmarginsz;
858241675Suqs
859241675Suqs	mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
860241675Suqs	return(1);
861241675Suqs}
862241675Suqs
863241675Suqs/* ARGSUSED */
864241675Suqsstatic void
865241675Suqspost_RS(DECL_ARGS)
866241675Suqs{
867241675Suqs	int		 ival;
868241675Suqs	size_t		 sz;
869241675Suqs
870241675Suqs	switch (n->type) {
871241675Suqs	case (MAN_BLOCK):
872241675Suqs		return;
873241675Suqs	case (MAN_HEAD):
874241675Suqs		return;
875241675Suqs	default:
876241675Suqs		term_newln(p);
877241675Suqs		break;
878241675Suqs	}
879241675Suqs
880241675Suqs	sz = term_len(p, p->defindent);
881241675Suqs
882241675Suqs	if (NULL != (n = n->parent->head->child))
883241675Suqs		if ((ival = a2width(p, n->string)) >= 0)
884241675Suqs			sz = (size_t)ival;
885241675Suqs
886241675Suqs	mt->offset = mt->offset < sz ?  0 : mt->offset - sz;
887241675Suqs	p->offset = mt->offset;
888241675Suqs
889241675Suqs	if (--mt->lmarginsz < MAXMARGINS)
890241675Suqs		mt->lmargincur = mt->lmarginsz;
891241675Suqs}
892241675Suqs
893241675Suqsstatic void
894241675Suqsprint_man_node(DECL_ARGS)
895241675Suqs{
896241675Suqs	size_t		 rm, rmax;
897241675Suqs	int		 c;
898241675Suqs
899241675Suqs	switch (n->type) {
900241675Suqs	case(MAN_TEXT):
901241675Suqs		/*
902241675Suqs		 * If we have a blank line, output a vertical space.
903241675Suqs		 * If we have a space as the first character, break
904241675Suqs		 * before printing the line's data.
905241675Suqs		 */
906241675Suqs		if ('\0' == *n->string) {
907241675Suqs			term_vspace(p);
908241675Suqs			return;
909241675Suqs		} else if (' ' == *n->string && MAN_LINE & n->flags)
910241675Suqs			term_newln(p);
911241675Suqs
912241675Suqs		term_word(p, n->string);
913241675Suqs
914241675Suqs		/*
915241675Suqs		 * If we're in a literal context, make sure that words
916241675Suqs		 * togehter on the same line stay together.  This is a
917241675Suqs		 * POST-printing call, so we check the NEXT word.  Since
918241675Suqs		 * -man doesn't have nested macros, we don't need to be
919241675Suqs		 * more specific than this.
920241675Suqs		 */
921241675Suqs		if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
922241675Suqs				(NULL == n->next ||
923241675Suqs				 n->next->line > n->line)) {
924241675Suqs			rm = p->rmargin;
925241675Suqs			rmax = p->maxrmargin;
926241675Suqs			p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
927241675Suqs			p->flags |= TERMP_NOSPACE;
928241675Suqs			term_flushln(p);
929241675Suqs			p->rmargin = rm;
930241675Suqs			p->maxrmargin = rmax;
931241675Suqs		}
932241675Suqs
933241675Suqs		if (MAN_EOS & n->flags)
934241675Suqs			p->flags |= TERMP_SENTENCE;
935241675Suqs		return;
936241675Suqs	case (MAN_EQN):
937241675Suqs		term_eqn(p, n->eqn);
938241675Suqs		return;
939241675Suqs	case (MAN_TBL):
940241675Suqs		/*
941241675Suqs		 * Tables are preceded by a newline.  Then process a
942241675Suqs		 * table line, which will cause line termination,
943241675Suqs		 */
944241675Suqs		if (TBL_SPAN_FIRST & n->span->flags)
945241675Suqs			term_newln(p);
946241675Suqs		term_tbl(p, n->span);
947241675Suqs		return;
948241675Suqs	default:
949241675Suqs		break;
950241675Suqs	}
951241675Suqs
952241675Suqs	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
953241675Suqs		term_fontrepl(p, TERMFONT_NONE);
954241675Suqs
955241675Suqs	c = 1;
956241675Suqs	if (termacts[n->tok].pre)
957241675Suqs		c = (*termacts[n->tok].pre)(p, mt, n, m);
958241675Suqs
959241675Suqs	if (c && n->child)
960241675Suqs		print_man_nodelist(p, mt, n->child, m);
961241675Suqs
962241675Suqs	if (termacts[n->tok].post)
963241675Suqs		(*termacts[n->tok].post)(p, mt, n, m);
964241675Suqs	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
965241675Suqs		term_fontrepl(p, TERMFONT_NONE);
966241675Suqs
967241675Suqs	if (MAN_EOS & n->flags)
968241675Suqs		p->flags |= TERMP_SENTENCE;
969241675Suqs}
970241675Suqs
971241675Suqs
972241675Suqsstatic void
973241675Suqsprint_man_nodelist(DECL_ARGS)
974241675Suqs{
975241675Suqs
976241675Suqs	print_man_node(p, mt, n, m);
977241675Suqs	if ( ! n->next)
978241675Suqs		return;
979241675Suqs	print_man_nodelist(p, mt, n->next, m);
980241675Suqs}
981241675Suqs
982241675Suqs
983241675Suqsstatic void
984241675Suqsprint_man_foot(struct termp *p, const void *arg)
985241675Suqs{
986241675Suqs	char		title[BUFSIZ];
987241675Suqs	size_t		datelen;
988241675Suqs	const struct man_meta *meta;
989241675Suqs
990241675Suqs	meta = (const struct man_meta *)arg;
991241675Suqs	assert(meta->title);
992241675Suqs	assert(meta->msec);
993241675Suqs	assert(meta->date);
994241675Suqs
995241675Suqs	term_fontrepl(p, TERMFONT_NONE);
996241675Suqs
997241675Suqs	term_vspace(p);
998241675Suqs
999241675Suqs	/*
1000241675Suqs	 * Temporary, undocumented option to imitate mdoc(7) output.
1001241675Suqs	 * In the bottom right corner, use the source instead of
1002241675Suqs	 * the title.
1003241675Suqs	 */
1004241675Suqs
1005241675Suqs	if ( ! p->mdocstyle) {
1006241675Suqs		term_vspace(p);
1007241675Suqs		term_vspace(p);
1008241675Suqs		snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
1009241675Suqs	} else if (meta->source) {
1010241675Suqs		strlcpy(title, meta->source, BUFSIZ);
1011241675Suqs	} else {
1012241675Suqs		title[0] = '\0';
1013241675Suqs	}
1014241675Suqs	datelen = term_strlen(p, meta->date);
1015241675Suqs
1016241675Suqs	/* Bottom left corner: manual source. */
1017241675Suqs
1018241675Suqs	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1019241675Suqs	p->offset = 0;
1020241675Suqs	p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
1021241675Suqs
1022241675Suqs	if (meta->source)
1023241675Suqs		term_word(p, meta->source);
1024241675Suqs	term_flushln(p);
1025241675Suqs
1026241675Suqs	/* At the bottom in the middle: manual date. */
1027241675Suqs
1028241675Suqs	p->flags |= TERMP_NOSPACE;
1029241675Suqs	p->offset = p->rmargin;
1030241675Suqs	p->rmargin = p->maxrmargin - term_strlen(p, title);
1031241675Suqs	if (p->offset + datelen >= p->rmargin)
1032241675Suqs		p->rmargin = p->offset + datelen;
1033241675Suqs
1034241675Suqs	term_word(p, meta->date);
1035241675Suqs	term_flushln(p);
1036241675Suqs
1037241675Suqs	/* Bottom right corner: manual title and section. */
1038241675Suqs
1039241675Suqs	p->flags &= ~TERMP_NOBREAK;
1040241675Suqs	p->flags |= TERMP_NOSPACE;
1041241675Suqs	p->offset = p->rmargin;
1042241675Suqs	p->rmargin = p->maxrmargin;
1043241675Suqs
1044241675Suqs	term_word(p, title);
1045241675Suqs	term_flushln(p);
1046241675Suqs}
1047241675Suqs
1048241675Suqs
1049241675Suqsstatic void
1050241675Suqsprint_man_head(struct termp *p, const void *arg)
1051241675Suqs{
1052241675Suqs	char		buf[BUFSIZ], title[BUFSIZ];
1053241675Suqs	size_t		buflen, titlen;
1054241675Suqs	const struct man_meta *m;
1055241675Suqs
1056241675Suqs	m = (const struct man_meta *)arg;
1057241675Suqs	assert(m->title);
1058241675Suqs	assert(m->msec);
1059241675Suqs
1060241675Suqs	if (m->vol)
1061241675Suqs		strlcpy(buf, m->vol, BUFSIZ);
1062241675Suqs	else
1063241675Suqs		buf[0] = '\0';
1064241675Suqs	buflen = term_strlen(p, buf);
1065241675Suqs
1066241675Suqs	/* Top left corner: manual title and section. */
1067241675Suqs
1068241675Suqs	snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
1069241675Suqs	titlen = term_strlen(p, title);
1070241675Suqs
1071241675Suqs	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
1072241675Suqs	p->offset = 0;
1073241675Suqs	p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
1074241675Suqs	    (p->maxrmargin -
1075241675Suqs	     term_strlen(p, buf) + term_len(p, 1)) / 2 :
1076241675Suqs	    p->maxrmargin - buflen;
1077241675Suqs
1078241675Suqs	term_word(p, title);
1079241675Suqs	term_flushln(p);
1080241675Suqs
1081241675Suqs	/* At the top in the middle: manual volume. */
1082241675Suqs
1083241675Suqs	p->flags |= TERMP_NOSPACE;
1084241675Suqs	p->offset = p->rmargin;
1085241675Suqs	p->rmargin = p->offset + buflen + titlen < p->maxrmargin ?
1086241675Suqs	    p->maxrmargin - titlen : p->maxrmargin;
1087241675Suqs
1088241675Suqs	term_word(p, buf);
1089241675Suqs	term_flushln(p);
1090241675Suqs
1091241675Suqs	/* Top right corner: title and section, again. */
1092241675Suqs
1093241675Suqs	p->flags &= ~TERMP_NOBREAK;
1094241675Suqs	if (p->rmargin + titlen <= p->maxrmargin) {
1095241675Suqs		p->flags |= TERMP_NOSPACE;
1096241675Suqs		p->offset = p->rmargin;
1097241675Suqs		p->rmargin = p->maxrmargin;
1098241675Suqs		term_word(p, title);
1099241675Suqs		term_flushln(p);
1100241675Suqs	}
1101241675Suqs
1102241675Suqs	p->flags &= ~TERMP_NOSPACE;
1103241675Suqs	p->offset = 0;
1104241675Suqs	p->rmargin = p->maxrmargin;
1105241675Suqs
1106241675Suqs	/*
1107241675Suqs	 * Groff prints three blank lines before the content.
1108241675Suqs	 * Do the same, except in the temporary, undocumented
1109241675Suqs	 * mode imitating mdoc(7) output.
1110241675Suqs	 */
1111241675Suqs
1112241675Suqs	term_vspace(p);
1113241675Suqs	if ( ! p->mdocstyle) {
1114241675Suqs		term_vspace(p);
1115241675Suqs		term_vspace(p);
1116241675Suqs	}
1117241675Suqs}
1118