xref: /openbsd/usr.bin/mandoc/mdoc_man.c (revision 723822f6)
1 /* $OpenBSD: mdoc_man.c,v 1.137 2025/01/24 22:36:51 schwarze Exp $ */
2 /*
3  * Copyright (c) 2011-2021, 2025 Ingo Schwarze <schwarze@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <sys/types.h>
18 
19 #include <assert.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "mandoc_aux.h"
25 #include "mandoc.h"
26 #include "roff.h"
27 #include "mdoc.h"
28 #include "man.h"
29 #include "out.h"
30 #include "main.h"
31 
32 #define	DECL_ARGS const struct roff_meta *meta, struct roff_node *n
33 
34 typedef	int	(*int_fp)(DECL_ARGS);
35 typedef	void	(*void_fp)(DECL_ARGS);
36 
37 struct	mdoc_man_act {
38 	int_fp		  cond; /* DON'T run actions */
39 	int_fp		  pre; /* pre-node action */
40 	void_fp		  post; /* post-node action */
41 	const char	 *prefix; /* pre-node string constant */
42 	const char	 *suffix; /* post-node string constant */
43 };
44 
45 static	int	  cond_body(DECL_ARGS);
46 static	int	  cond_head(DECL_ARGS);
47 static  void	  font_push(char);
48 static	void	  font_pop(void);
49 static	int	  man_strlen(const char *);
50 static	void	  mid_it(void);
51 static	void	  post__t(DECL_ARGS);
52 static	void	  post_aq(DECL_ARGS);
53 static	void	  post_bd(DECL_ARGS);
54 static	void	  post_bf(DECL_ARGS);
55 static	void	  post_bk(DECL_ARGS);
56 static	void	  post_bl(DECL_ARGS);
57 static	void	  post_dl(DECL_ARGS);
58 static	void	  post_en(DECL_ARGS);
59 static	void	  post_enc(DECL_ARGS);
60 static	void	  post_eo(DECL_ARGS);
61 static	void	  post_fa(DECL_ARGS);
62 static	void	  post_fd(DECL_ARGS);
63 static	void	  post_fl(DECL_ARGS);
64 static	void	  post_fn(DECL_ARGS);
65 static	void	  post_fo(DECL_ARGS);
66 static	void	  post_font(DECL_ARGS);
67 static	void	  post_in(DECL_ARGS);
68 static	void	  post_it(DECL_ARGS);
69 static	void	  post_lb(DECL_ARGS);
70 static	void	  post_nm(DECL_ARGS);
71 static	void	  post_percent(DECL_ARGS);
72 static	void	  post_pf(DECL_ARGS);
73 static	void	  post_sect(DECL_ARGS);
74 static	void	  post_vt(DECL_ARGS);
75 static	int	  pre__t(DECL_ARGS);
76 static	int	  pre_abort(DECL_ARGS);
77 static	int	  pre_an(DECL_ARGS);
78 static	int	  pre_ap(DECL_ARGS);
79 static	int	  pre_aq(DECL_ARGS);
80 static	int	  pre_bd(DECL_ARGS);
81 static	int	  pre_bf(DECL_ARGS);
82 static	int	  pre_bk(DECL_ARGS);
83 static	int	  pre_bl(DECL_ARGS);
84 static	void	  pre_br(DECL_ARGS);
85 static	int	  pre_dl(DECL_ARGS);
86 static	int	  pre_en(DECL_ARGS);
87 static	int	  pre_enc(DECL_ARGS);
88 static	int	  pre_em(DECL_ARGS);
89 static	int	  pre_skip(DECL_ARGS);
90 static	int	  pre_eo(DECL_ARGS);
91 static	int	  pre_ex(DECL_ARGS);
92 static	int	  pre_fa(DECL_ARGS);
93 static	int	  pre_fd(DECL_ARGS);
94 static	int	  pre_fl(DECL_ARGS);
95 static	int	  pre_fn(DECL_ARGS);
96 static	int	  pre_fo(DECL_ARGS);
97 static	void	  pre_ft(DECL_ARGS);
98 static	int	  pre_Ft(DECL_ARGS);
99 static	int	  pre_in(DECL_ARGS);
100 static	int	  pre_it(DECL_ARGS);
101 static	int	  pre_lk(DECL_ARGS);
102 static	int	  pre_li(DECL_ARGS);
103 static	int	  pre_nm(DECL_ARGS);
104 static	int	  pre_no(DECL_ARGS);
105 static	void	  pre_noarg(DECL_ARGS);
106 static	int	  pre_ns(DECL_ARGS);
107 static	void	  pre_onearg(DECL_ARGS);
108 static	int	  pre_pp(DECL_ARGS);
109 static	int	  pre_rs(DECL_ARGS);
110 static	int	  pre_sm(DECL_ARGS);
111 static	void	  pre_sp(DECL_ARGS);
112 static	int	  pre_sect(DECL_ARGS);
113 static	int	  pre_sy(DECL_ARGS);
114 static	void	  pre_syn(struct roff_node *);
115 static	void	  pre_ta(DECL_ARGS);
116 static	int	  pre_vt(DECL_ARGS);
117 static	int	  pre_xr(DECL_ARGS);
118 static	void	  print_word(const char *);
119 static	void	  print_line(const char *, int);
120 static	void	  print_block(const char *, int);
121 static	void	  print_offs(const char *, int);
122 static	void	  print_width(const struct mdoc_bl *,
123 			const struct roff_node *);
124 static	void	  print_count(int *);
125 static	void	  print_node(DECL_ARGS);
126 
127 static const void_fp roff_man_acts[ROFF_MAX] = {
128 	pre_br,		/* br */
129 	pre_onearg,	/* ce */
130 	pre_noarg,	/* fi */
131 	pre_ft,		/* ft */
132 	pre_onearg,	/* ll */
133 	pre_onearg,	/* mc */
134 	pre_noarg,	/* nf */
135 	pre_onearg,	/* po */
136 	pre_onearg,	/* rj */
137 	pre_sp,		/* sp */
138 	pre_ta,		/* ta */
139 	pre_onearg,	/* ti */
140 };
141 
142 static const struct mdoc_man_act mdoc_man_acts[MDOC_MAX - MDOC_Dd] = {
143 	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
144 	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
145 	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
146 	{ NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
147 	{ NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
148 	{ NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
149 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
150 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
151 	{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
152 	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
153 	{ cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
154 	{ NULL, NULL, NULL, NULL, NULL }, /* El */
155 	{ NULL, pre_it, post_it, NULL, NULL }, /* It */
156 	{ NULL, pre_em, post_font, NULL, NULL }, /* Ad */
157 	{ NULL, pre_an, NULL, NULL, NULL }, /* An */
158 	{ NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
159 	{ NULL, pre_em, post_font, NULL, NULL }, /* Ar */
160 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
161 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
162 	{ NULL, pre_li, post_font, NULL, NULL }, /* Dv */
163 	{ NULL, pre_li, post_font, NULL, NULL }, /* Er */
164 	{ NULL, pre_li, post_font, NULL, NULL }, /* Ev */
165 	{ NULL, pre_ex, NULL, NULL, NULL }, /* Ex */
166 	{ NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
167 	{ NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
168 	{ NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
169 	{ NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
170 	{ NULL, pre_Ft, post_font, NULL, NULL }, /* Ft */
171 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
172 	{ NULL, pre_in, post_in, NULL, NULL }, /* In */
173 	{ NULL, pre_li, post_font, NULL, NULL }, /* Li */
174 	{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
175 	{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
176 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
177 	{ NULL, pre_abort, NULL, NULL, NULL }, /* Ot */
178 	{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
179 	{ NULL, pre_ex, NULL, NULL, NULL }, /* Rv */
180 	{ NULL, NULL, NULL, NULL, NULL }, /* St */
181 	{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
182 	{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
183 	{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
184 	{ NULL, NULL, post_percent, NULL, NULL }, /* %A */
185 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %B */
186 	{ NULL, NULL, post_percent, NULL, NULL }, /* %D */
187 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %I */
188 	{ NULL, pre_em, post_percent, NULL, NULL }, /* %J */
189 	{ NULL, NULL, post_percent, NULL, NULL }, /* %N */
190 	{ NULL, NULL, post_percent, NULL, NULL }, /* %O */
191 	{ NULL, NULL, post_percent, NULL, NULL }, /* %P */
192 	{ NULL, NULL, post_percent, NULL, NULL }, /* %R */
193 	{ NULL, pre__t, post__t, NULL, NULL }, /* %T */
194 	{ NULL, NULL, post_percent, NULL, NULL }, /* %V */
195 	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
196 	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Ao */
197 	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Aq */
198 	{ NULL, NULL, NULL, NULL, NULL }, /* At */
199 	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
200 	{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
201 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
202 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
203 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bsx */
204 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bx */
205 	{ NULL, pre_skip, NULL, NULL, NULL }, /* Db */
206 	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
207 	{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Do */
208 	{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Dq */
209 	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
210 	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
211 	{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
212 	{ cond_body, pre_eo, post_eo, NULL, NULL }, /* Eo */
213 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Fx */
214 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
215 	{ NULL, pre_no, NULL, NULL, NULL }, /* No */
216 	{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
217 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Nx */
218 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Ox */
219 	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
220 	{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
221 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
222 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
223 	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
224 	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Ql */
225 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
226 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
227 	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
228 	{ cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
229 	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
230 	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* So */
231 	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Sq */
232 	{ NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
233 	{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
234 	{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
235 	{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
236 	{ NULL, NULL, NULL, NULL, NULL }, /* Ux */
237 	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
238 	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
239 	{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
240 	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
241 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
242 	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
243 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
244 	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
245 	{ NULL, NULL, NULL, NULL, NULL }, /* Bt */
246 	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
247 	{ NULL, pre_em, post_font, NULL, NULL }, /* Fr */
248 	{ NULL, NULL, NULL, NULL, NULL }, /* Ud */
249 	{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
250 	{ NULL, pre_abort, NULL, NULL, NULL }, /* Lp */
251 	{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
252 	{ NULL, pre_em, post_font, NULL, NULL }, /* Mt */
253 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
254 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
255 	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
256 	{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
257 	{ NULL, pre_skip, NULL, NULL, NULL }, /* Es */
258 	{ cond_body, pre_en, post_en, NULL, NULL }, /* En */
259 	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Dx */
260 	{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
261 	{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
262 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
263 	{ NULL, pre_skip, NULL, NULL, NULL }, /* Tg */
264 };
265 static const struct mdoc_man_act *mdoc_man_act(enum roff_tok);
266 
267 static	int		outflags;
268 #define	MMAN_spc	(1 << 0)  /* blank character before next word */
269 #define	MMAN_spc_force	(1 << 1)  /* even before trailing punctuation */
270 #define	MMAN_nl		(1 << 2)  /* break man(7) code line */
271 #define	MMAN_br		(1 << 3)  /* break output line */
272 #define	MMAN_sp		(1 << 4)  /* insert a blank output line */
273 #define	MMAN_PP		(1 << 5)  /* reset indentation etc. */
274 #define	MMAN_Sm		(1 << 6)  /* horizontal spacing mode */
275 #define	MMAN_Bk		(1 << 7)  /* word keep mode */
276 #define	MMAN_Bk_susp	(1 << 8)  /* suspend this (after a macro) */
277 #define	MMAN_An_split	(1 << 9)  /* author mode is "split" */
278 #define	MMAN_An_nosplit	(1 << 10) /* author mode is "nosplit" */
279 #define	MMAN_PD		(1 << 11) /* inter-paragraph spacing disabled */
280 #define	MMAN_nbrword	(1 << 12) /* do not break the next word */
281 
282 #define	BL_STACK_MAX	32
283 
284 static	int		Bl_stack[BL_STACK_MAX];  /* offsets [chars] */
285 static	int		Bl_stack_post[BL_STACK_MAX];  /* add final .RE */
286 static	int		Bl_stack_len;  /* number of nested Bl blocks */
287 static	int		TPremain;  /* characters before tag is full */
288 
289 static	struct {
290 	char	*head;
291 	char	*tail;
292 	size_t	 size;
293 }	fontqueue;
294 
295 
296 static const struct mdoc_man_act *
mdoc_man_act(enum roff_tok tok)297 mdoc_man_act(enum roff_tok tok)
298 {
299 	assert(tok >= MDOC_Dd && tok <= MDOC_MAX);
300 	return mdoc_man_acts + (tok - MDOC_Dd);
301 }
302 
303 static int
man_strlen(const char * cp)304 man_strlen(const char *cp)
305 {
306 	size_t	 rsz;
307 	int	 skip, sz;
308 
309 	sz = 0;
310 	skip = 0;
311 	for (;;) {
312 		rsz = strcspn(cp, "\\");
313 		if (rsz) {
314 			cp += rsz;
315 			if (skip) {
316 				skip = 0;
317 				rsz--;
318 			}
319 			sz += rsz;
320 		}
321 		if ('\0' == *cp)
322 			break;
323 		cp++;
324 		switch (mandoc_escape(&cp, NULL, NULL)) {
325 		case ESCAPE_ERROR:
326 			return sz;
327 		case ESCAPE_UNICODE:
328 		case ESCAPE_NUMBERED:
329 		case ESCAPE_SPECIAL:
330 		case ESCAPE_UNDEF:
331 		case ESCAPE_OVERSTRIKE:
332 			if (skip)
333 				skip = 0;
334 			else
335 				sz++;
336 			break;
337 		case ESCAPE_SKIPCHAR:
338 			skip = 1;
339 			break;
340 		default:
341 			break;
342 		}
343 	}
344 	return sz;
345 }
346 
347 static void
font_push(char newfont)348 font_push(char newfont)
349 {
350 
351 	if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
352 		fontqueue.size += 8;
353 		fontqueue.head = mandoc_realloc(fontqueue.head,
354 		    fontqueue.size);
355 	}
356 	*fontqueue.tail = newfont;
357 	print_word("");
358 	printf("\\f");
359 	putchar(newfont);
360 	outflags &= ~MMAN_spc;
361 }
362 
363 static void
font_pop(void)364 font_pop(void)
365 {
366 
367 	if (fontqueue.tail > fontqueue.head)
368 		fontqueue.tail--;
369 	outflags &= ~MMAN_spc;
370 	print_word("");
371 	printf("\\f");
372 	putchar(*fontqueue.tail);
373 }
374 
375 static void
print_word(const char * s)376 print_word(const char *s)
377 {
378 
379 	if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
380 		/*
381 		 * If we need a newline, print it now and start afresh.
382 		 */
383 		if (MMAN_PP & outflags) {
384 			if (MMAN_sp & outflags) {
385 				if (MMAN_PD & outflags) {
386 					printf("\n.PD");
387 					outflags &= ~MMAN_PD;
388 				}
389 			} else if ( ! (MMAN_PD & outflags)) {
390 				printf("\n.PD 0");
391 				outflags |= MMAN_PD;
392 			}
393 			printf("\n.PP\n");
394 		} else if (MMAN_sp & outflags)
395 			printf("\n.sp\n");
396 		else if (MMAN_br & outflags)
397 			printf("\n.br\n");
398 		else if (MMAN_nl & outflags)
399 			putchar('\n');
400 		outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
401 		if (1 == TPremain)
402 			printf(".br\n");
403 		TPremain = 0;
404 	} else if (MMAN_spc & outflags) {
405 		/*
406 		 * If we need a space, only print it if
407 		 * (1) it is forced by `No' or
408 		 * (2) what follows is not terminating punctuation or
409 		 * (3) what follows is longer than one character.
410 		 */
411 		if (MMAN_spc_force & outflags || '\0' == s[0] ||
412 		    NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
413 			if (MMAN_Bk & outflags &&
414 			    ! (MMAN_Bk_susp & outflags))
415 				putchar('\\');
416 			putchar(' ');
417 			if (TPremain)
418 				TPremain--;
419 		}
420 	}
421 
422 	/*
423 	 * Reassign needing space if we're not following opening
424 	 * punctuation.
425 	 */
426 	if (MMAN_Sm & outflags && ('\0' == s[0] ||
427 	    (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
428 		outflags |= MMAN_spc;
429 	else
430 		outflags &= ~MMAN_spc;
431 	outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
432 
433 	for ( ; *s; s++) {
434 		switch (*s) {
435 		case ASCII_NBRSP:
436 			printf("\\ ");
437 			break;
438 		case ASCII_HYPH:
439 			putchar('-');
440 			break;
441 		case ASCII_BREAK:
442 			printf("\\:");
443 			break;
444 		case ' ':
445 			if (MMAN_nbrword & outflags) {
446 				printf("\\ ");
447 				break;
448 			}
449 			/* FALLTHROUGH */
450 		default:
451 			putchar((unsigned char)*s);
452 			break;
453 		}
454 		if (TPremain)
455 			TPremain--;
456 	}
457 	outflags &= ~MMAN_nbrword;
458 }
459 
460 static void
print_line(const char * s,int newflags)461 print_line(const char *s, int newflags)
462 {
463 
464 	outflags |= MMAN_nl;
465 	print_word(s);
466 	outflags |= newflags;
467 }
468 
469 static void
print_block(const char * s,int newflags)470 print_block(const char *s, int newflags)
471 {
472 
473 	outflags &= ~MMAN_PP;
474 	if (MMAN_sp & outflags) {
475 		outflags &= ~(MMAN_sp | MMAN_br);
476 		if (MMAN_PD & outflags) {
477 			print_line(".PD", 0);
478 			outflags &= ~MMAN_PD;
479 		}
480 	} else if (! (MMAN_PD & outflags))
481 		print_line(".PD 0", MMAN_PD);
482 	outflags |= MMAN_nl;
483 	print_word(s);
484 	outflags |= MMAN_Bk_susp | newflags;
485 }
486 
487 static void
print_offs(const char * v,int keywords)488 print_offs(const char *v, int keywords)
489 {
490 	char		  buf[24];
491 	struct roffsu	  su;
492 	const char	 *end;
493 	int		  sz;
494 
495 	print_line(".RS", MMAN_Bk_susp);
496 
497 	/* Convert v into a number (of characters). */
498 	if (NULL == v || '\0' == *v || (keywords && !strcmp(v, "left")))
499 		sz = 0;
500 	else if (keywords && !strcmp(v, "indent"))
501 		sz = 6;
502 	else if (keywords && !strcmp(v, "indent-two"))
503 		sz = 12;
504 	else {
505 		end = a2roffsu(v, &su, SCALE_EN);
506 		if (end == NULL || *end != '\0')
507 			sz = man_strlen(v);
508 		else if (SCALE_EN == su.unit)
509 			sz = su.scale;
510 		else {
511 			/*
512 			 * XXX
513 			 * If we are inside an enclosing list,
514 			 * there is no easy way to add the two
515 			 * indentations because they are provided
516 			 * in terms of different units.
517 			 */
518 			print_word(v);
519 			outflags |= MMAN_nl;
520 			return;
521 		}
522 	}
523 
524 	/*
525 	 * We are inside an enclosing list.
526 	 * Add the two indentations.
527 	 */
528 	if (Bl_stack_len)
529 		sz += Bl_stack[Bl_stack_len - 1];
530 
531 	(void)snprintf(buf, sizeof(buf), "%dn", sz);
532 	print_word(buf);
533 	outflags |= MMAN_nl;
534 }
535 
536 /*
537  * Set up the indentation for a list item; used from pre_it().
538  */
539 static void
print_width(const struct mdoc_bl * bl,const struct roff_node * child)540 print_width(const struct mdoc_bl *bl, const struct roff_node *child)
541 {
542 	char		  buf[24];
543 	struct roffsu	  su;
544 	const char	 *end;
545 	int		  numeric, remain, sz, chsz;
546 
547 	numeric = 1;
548 	remain = 0;
549 
550 	/* Convert the width into a number (of characters). */
551 	if (bl->width == NULL)
552 		sz = (bl->type == LIST_hang) ? 6 : 0;
553 	else {
554 		end = a2roffsu(bl->width, &su, SCALE_MAX);
555 		if (end == NULL || *end != '\0')
556 			sz = man_strlen(bl->width);
557 		else if (SCALE_EN == su.unit)
558 			sz = su.scale;
559 		else {
560 			sz = 0;
561 			numeric = 0;
562 		}
563 	}
564 
565 	/* XXX Rough estimation, might have multiple parts. */
566 	if (bl->type == LIST_enum)
567 		chsz = (bl->count > 8) + 1;
568 	else if (child != NULL && child->type == ROFFT_TEXT)
569 		chsz = man_strlen(child->string);
570 	else
571 		chsz = 0;
572 
573 	/* Maybe we are inside an enclosing list? */
574 	mid_it();
575 
576 	/*
577 	 * Save our own indentation,
578 	 * such that child lists can use it.
579 	 */
580 	Bl_stack[Bl_stack_len++] = sz + 2;
581 
582 	/* Set up the current list. */
583 	if (chsz > sz && bl->type != LIST_tag)
584 		print_block(".HP", MMAN_spc);
585 	else {
586 		print_block(".TP", MMAN_spc);
587 		remain = sz + 2;
588 	}
589 	if (numeric) {
590 		(void)snprintf(buf, sizeof(buf), "%dn", sz + 2);
591 		print_word(buf);
592 	} else
593 		print_word(bl->width);
594 	TPremain = remain;
595 }
596 
597 static void
print_count(int * count)598 print_count(int *count)
599 {
600 	char		  buf[24];
601 
602 	(void)snprintf(buf, sizeof(buf), "%d.\\&", ++*count);
603 	print_word(buf);
604 }
605 
606 void
man_mdoc(void * arg,const struct roff_meta * mdoc)607 man_mdoc(void *arg, const struct roff_meta *mdoc)
608 {
609 	struct roff_node *n;
610 
611 	printf(".\\\" Automatically generated from an mdoc input file."
612 	    "  Do not edit.\n");
613 	for (n = mdoc->first->child; n != NULL; n = n->next) {
614 		if (n->type != ROFFT_COMMENT)
615 			break;
616 		printf(".\\\"%s\n", n->string);
617 	}
618 
619 	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
620 	    mdoc->title, (mdoc->msec == NULL ? "" : mdoc->msec),
621 	    mdoc->date, mdoc->os, mdoc->vol);
622 
623 	/* Disable hyphenation and if nroff, disable justification. */
624 	printf(".nh\n.if n .ad l");
625 
626 	outflags = MMAN_nl | MMAN_Sm;
627 	if (0 == fontqueue.size) {
628 		fontqueue.size = 8;
629 		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
630 		*fontqueue.tail = 'R';
631 	}
632 	for (; n != NULL; n = n->next)
633 		print_node(mdoc, n);
634 	putchar('\n');
635 }
636 
637 static void
print_node(DECL_ARGS)638 print_node(DECL_ARGS)
639 {
640 	const struct mdoc_man_act	*act;
641 	struct roff_node		*sub;
642 	int				 cond, do_sub;
643 
644 	if (n->flags & NODE_NOPRT)
645 		return;
646 
647 	/*
648 	 * Break the line if we were parsed subsequent the current node.
649 	 * This makes the page structure be more consistent.
650 	 */
651 	if (outflags & MMAN_spc &&
652 	    n->flags & NODE_LINE &&
653 	    !roff_node_transparent(n))
654 		outflags |= MMAN_nl;
655 
656 	act = NULL;
657 	cond = 0;
658 	do_sub = 1;
659 	n->flags &= ~NODE_ENDED;
660 
661 	switch (n->type) {
662 	case ROFFT_EQN:
663 	case ROFFT_TBL:
664 		mandoc_msg(n->type == ROFFT_EQN ? MANDOCERR_EQN_TMAN :
665 		    MANDOCERR_TBL_TMAN, n->line, n->pos, NULL);
666 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
667 		print_word("The");
668 		print_line(".B \\-T man", MMAN_nl);
669 		print_word("output mode does not support");
670 		print_word(n->type == ROFFT_EQN ? "eqn(7)" : "tbl(7)");
671 		print_word("input.");
672 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
673 		return;
674 	case ROFFT_TEXT:
675 		/*
676 		 * Make sure that we don't happen to start with a
677 		 * control character at the start of a line.
678 		 */
679 		if (MMAN_nl & outflags &&
680 		    ('.' == *n->string || '\'' == *n->string)) {
681 			print_word("");
682 			printf("\\&");
683 			outflags &= ~MMAN_spc;
684 		}
685 		if (n->flags & NODE_DELIMC)
686 			outflags &= ~(MMAN_spc | MMAN_spc_force);
687 		else if (outflags & MMAN_Sm)
688 			outflags |= MMAN_spc_force;
689 		print_word(n->string);
690 		if (n->flags & NODE_DELIMO)
691 			outflags &= ~(MMAN_spc | MMAN_spc_force);
692 		else if (outflags & MMAN_Sm)
693 			outflags |= MMAN_spc;
694 		break;
695 	default:
696 		if (n->tok < ROFF_MAX) {
697 			(*roff_man_acts[n->tok])(meta, n);
698 			return;
699 		}
700 		act = mdoc_man_act(n->tok);
701 		cond = act->cond == NULL || (*act->cond)(meta, n);
702 		if (cond && act->pre != NULL &&
703 		    (n->end == ENDBODY_NOT || n->child != NULL))
704 			do_sub = (*act->pre)(meta, n);
705 		break;
706 	}
707 
708 	/*
709 	 * Conditionally run all child nodes.
710 	 * Note that this iterates over children instead of using
711 	 * recursion.  This prevents unnecessary depth in the stack.
712 	 */
713 	if (do_sub)
714 		for (sub = n->child; sub; sub = sub->next)
715 			print_node(meta, sub);
716 
717 	/*
718 	 * Lastly, conditionally run the post-node handler.
719 	 */
720 	if (NODE_ENDED & n->flags)
721 		return;
722 
723 	if (cond && act->post)
724 		(*act->post)(meta, n);
725 
726 	if (ENDBODY_NOT != n->end)
727 		n->body->flags |= NODE_ENDED;
728 }
729 
730 static int
cond_head(DECL_ARGS)731 cond_head(DECL_ARGS)
732 {
733 
734 	return n->type == ROFFT_HEAD;
735 }
736 
737 static int
cond_body(DECL_ARGS)738 cond_body(DECL_ARGS)
739 {
740 
741 	return n->type == ROFFT_BODY;
742 }
743 
744 static int
pre_abort(DECL_ARGS)745 pre_abort(DECL_ARGS)
746 {
747 	abort();
748 }
749 
750 static int
pre_enc(DECL_ARGS)751 pre_enc(DECL_ARGS)
752 {
753 	const char	*prefix;
754 
755 	prefix = mdoc_man_act(n->tok)->prefix;
756 	if (NULL == prefix)
757 		return 1;
758 	print_word(prefix);
759 	outflags &= ~MMAN_spc;
760 	return 1;
761 }
762 
763 static void
post_enc(DECL_ARGS)764 post_enc(DECL_ARGS)
765 {
766 	const char *suffix;
767 
768 	suffix = mdoc_man_act(n->tok)->suffix;
769 	if (NULL == suffix)
770 		return;
771 	outflags &= ~(MMAN_spc | MMAN_nl);
772 	print_word(suffix);
773 }
774 
775 static int
pre_ex(DECL_ARGS)776 pre_ex(DECL_ARGS)
777 {
778 	outflags |= MMAN_br | MMAN_nl;
779 	return 1;
780 }
781 
782 static void
post_font(DECL_ARGS)783 post_font(DECL_ARGS)
784 {
785 
786 	font_pop();
787 }
788 
789 static void
post_percent(DECL_ARGS)790 post_percent(DECL_ARGS)
791 {
792 	struct roff_node *np, *nn, *nnn;
793 
794 	if (mdoc_man_act(n->tok)->pre == pre_em)
795 		font_pop();
796 
797 	if (n->parent == NULL || n->parent->tok != MDOC_Rs)
798 		return;
799 
800 	if ((nn = roff_node_next(n)) != NULL) {
801 		np = roff_node_prev(n);
802 		nnn = nn == NULL ? NULL : roff_node_next(nn);
803 		if (nn->tok != n->tok ||
804 		    (np != NULL && np->tok == n->tok) ||
805 		    (nnn != NULL && nnn->tok == n->tok))
806 			print_word(",");
807 		if (nn->tok == n->tok &&
808 		    (nnn == NULL || nnn->tok != n->tok))
809 			print_word("and");
810 	} else {
811 		print_word(".");
812 		outflags |= MMAN_nl;
813 	}
814 }
815 
816 static int
pre__t(DECL_ARGS)817 pre__t(DECL_ARGS)
818 {
819 
820 	if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) {
821 		print_word("\\(lq");
822 		outflags &= ~MMAN_spc;
823 	} else
824 		font_push('I');
825 	return 1;
826 }
827 
828 static void
post__t(DECL_ARGS)829 post__t(DECL_ARGS)
830 {
831 
832 	if (n->parent->tok  == MDOC_Rs && n->parent->norm->Rs.quote_T) {
833 		outflags &= ~MMAN_spc;
834 		print_word("\\(rq");
835 	} else
836 		font_pop();
837 	post_percent(meta, n);
838 }
839 
840 /*
841  * Print before a section header.
842  */
843 static int
pre_sect(DECL_ARGS)844 pre_sect(DECL_ARGS)
845 {
846 
847 	if (n->type == ROFFT_HEAD) {
848 		outflags |= MMAN_sp;
849 		print_block(mdoc_man_act(n->tok)->prefix, 0);
850 		print_word("");
851 		putchar('\"');
852 		outflags &= ~MMAN_spc;
853 	}
854 	return 1;
855 }
856 
857 /*
858  * Print subsequent a section header.
859  */
860 static void
post_sect(DECL_ARGS)861 post_sect(DECL_ARGS)
862 {
863 
864 	if (n->type != ROFFT_HEAD)
865 		return;
866 	outflags &= ~MMAN_spc;
867 	print_word("");
868 	putchar('\"');
869 	outflags |= MMAN_nl;
870 	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
871 		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
872 }
873 
874 /* See mdoc_term.c, synopsis_pre() for comments. */
875 static void
pre_syn(struct roff_node * n)876 pre_syn(struct roff_node *n)
877 {
878 	struct roff_node *np;
879 
880 	if ((n->flags & NODE_SYNPRETTY) == 0 ||
881 	    (np = roff_node_prev(n)) == NULL)
882 		return;
883 
884 	if (np->tok == n->tok &&
885 	    MDOC_Ft != n->tok &&
886 	    MDOC_Fo != n->tok &&
887 	    MDOC_Fn != n->tok) {
888 		outflags |= MMAN_br;
889 		return;
890 	}
891 
892 	switch (np->tok) {
893 	case MDOC_Fd:
894 	case MDOC_Fn:
895 	case MDOC_Fo:
896 	case MDOC_In:
897 	case MDOC_Vt:
898 		outflags |= MMAN_sp;
899 		break;
900 	case MDOC_Ft:
901 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
902 			outflags |= MMAN_sp;
903 			break;
904 		}
905 		/* FALLTHROUGH */
906 	default:
907 		outflags |= MMAN_br;
908 		break;
909 	}
910 }
911 
912 static int
pre_an(DECL_ARGS)913 pre_an(DECL_ARGS)
914 {
915 
916 	switch (n->norm->An.auth) {
917 	case AUTH_split:
918 		outflags &= ~MMAN_An_nosplit;
919 		outflags |= MMAN_An_split;
920 		return 0;
921 	case AUTH_nosplit:
922 		outflags &= ~MMAN_An_split;
923 		outflags |= MMAN_An_nosplit;
924 		return 0;
925 	default:
926 		if (MMAN_An_split & outflags)
927 			outflags |= MMAN_br;
928 		else if (SEC_AUTHORS == n->sec &&
929 		    ! (MMAN_An_nosplit & outflags))
930 			outflags |= MMAN_An_split;
931 		return 1;
932 	}
933 }
934 
935 static int
pre_ap(DECL_ARGS)936 pre_ap(DECL_ARGS)
937 {
938 
939 	outflags &= ~MMAN_spc;
940 	print_word("'");
941 	outflags &= ~MMAN_spc;
942 	return 0;
943 }
944 
945 static int
pre_aq(DECL_ARGS)946 pre_aq(DECL_ARGS)
947 {
948 
949 	print_word(n->child != NULL && n->child->next == NULL &&
950 	    n->child->tok == MDOC_Mt ?  "<" : "\\(la");
951 	outflags &= ~MMAN_spc;
952 	return 1;
953 }
954 
955 static void
post_aq(DECL_ARGS)956 post_aq(DECL_ARGS)
957 {
958 
959 	outflags &= ~(MMAN_spc | MMAN_nl);
960 	print_word(n->child != NULL && n->child->next == NULL &&
961 	    n->child->tok == MDOC_Mt ?  ">" : "\\(ra");
962 }
963 
964 static int
pre_bd(DECL_ARGS)965 pre_bd(DECL_ARGS)
966 {
967 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
968 	if (n->norm->Bd.type == DISP_unfilled ||
969 	    n->norm->Bd.type == DISP_literal)
970 		print_line(".nf", 0);
971 	if (n->norm->Bd.comp == 0 && roff_node_prev(n->parent) != NULL)
972 		outflags |= MMAN_sp;
973 	print_offs(n->norm->Bd.offs, 1);
974 	return 1;
975 }
976 
977 static void
post_bd(DECL_ARGS)978 post_bd(DECL_ARGS)
979 {
980 	enum roff_tok	 bef, now;
981 
982 	/* Close out this display. */
983 	print_line(".RE", MMAN_nl);
984 	bef = n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
985 	if (n->last == NULL)
986 		now = n->norm->Bd.type == DISP_unfilled ||
987 		    n->norm->Bd.type == DISP_literal ? ROFF_nf : ROFF_fi;
988 	else if (n->last->tok == ROFF_nf)
989 		now = ROFF_nf;
990 	else if (n->last->tok == ROFF_fi)
991 		now = ROFF_fi;
992 	else
993 		now = n->last->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi;
994 	if (bef != now) {
995 		outflags |= MMAN_nl;
996 		print_word(".");
997 		outflags &= ~MMAN_spc;
998 		print_word(roff_name[bef]);
999 		outflags |= MMAN_nl;
1000 	}
1001 
1002 	/* Maybe we are inside an enclosing list? */
1003 	if (roff_node_next(n->parent) != NULL)
1004 		mid_it();
1005 }
1006 
1007 static int
pre_bf(DECL_ARGS)1008 pre_bf(DECL_ARGS)
1009 {
1010 
1011 	switch (n->type) {
1012 	case ROFFT_BLOCK:
1013 		return 1;
1014 	case ROFFT_BODY:
1015 		break;
1016 	default:
1017 		return 0;
1018 	}
1019 	switch (n->norm->Bf.font) {
1020 	case FONT_Em:
1021 		font_push('I');
1022 		break;
1023 	case FONT_Sy:
1024 		font_push('B');
1025 		break;
1026 	default:
1027 		font_push('R');
1028 		break;
1029 	}
1030 	return 1;
1031 }
1032 
1033 static void
post_bf(DECL_ARGS)1034 post_bf(DECL_ARGS)
1035 {
1036 
1037 	if (n->type == ROFFT_BODY)
1038 		font_pop();
1039 }
1040 
1041 static int
pre_bk(DECL_ARGS)1042 pre_bk(DECL_ARGS)
1043 {
1044 	switch (n->type) {
1045 	case ROFFT_BLOCK:
1046 		return 1;
1047 	case ROFFT_BODY:
1048 	case ROFFT_ELEM:
1049 		outflags |= MMAN_Bk;
1050 		return 1;
1051 	default:
1052 		return 0;
1053 	}
1054 }
1055 
1056 static void
post_bk(DECL_ARGS)1057 post_bk(DECL_ARGS)
1058 {
1059 	switch (n->type) {
1060 	case ROFFT_ELEM:
1061 		while ((n = n->parent) != NULL)
1062 			 if (n->tok == MDOC_Bk)
1063 				return;
1064 		/* FALLTHROUGH */
1065 	case ROFFT_BODY:
1066 		outflags &= ~MMAN_Bk;
1067 		break;
1068 	default:
1069 		break;
1070 	}
1071 }
1072 
1073 static int
pre_bl(DECL_ARGS)1074 pre_bl(DECL_ARGS)
1075 {
1076 	size_t		 icol;
1077 
1078 	/*
1079 	 * print_offs() will increase the -offset to account for
1080 	 * a possible enclosing .It, but any enclosed .It blocks
1081 	 * just nest and do not add up their indentation.
1082 	 */
1083 	if (n->norm->Bl.offs) {
1084 		print_offs(n->norm->Bl.offs, 0);
1085 		Bl_stack[Bl_stack_len++] = 0;
1086 	}
1087 
1088 	switch (n->norm->Bl.type) {
1089 	case LIST_enum:
1090 		n->norm->Bl.count = 0;
1091 		return 1;
1092 	case LIST_column:
1093 		break;
1094 	default:
1095 		return 1;
1096 	}
1097 
1098 	if (n->child != NULL) {
1099 		print_line(".TS", MMAN_nl);
1100 		for (icol = 0; icol < n->norm->Bl.ncols; icol++)
1101 			print_word("l");
1102 		print_word(".");
1103 	}
1104 	outflags |= MMAN_nl;
1105 	return 1;
1106 }
1107 
1108 static void
post_bl(DECL_ARGS)1109 post_bl(DECL_ARGS)
1110 {
1111 
1112 	switch (n->norm->Bl.type) {
1113 	case LIST_column:
1114 		if (n->child != NULL)
1115 			print_line(".TE", 0);
1116 		break;
1117 	case LIST_enum:
1118 		n->norm->Bl.count = 0;
1119 		break;
1120 	default:
1121 		break;
1122 	}
1123 
1124 	if (n->norm->Bl.offs) {
1125 		print_line(".RE", MMAN_nl);
1126 		assert(Bl_stack_len);
1127 		Bl_stack_len--;
1128 		assert(Bl_stack[Bl_stack_len] == 0);
1129 	} else {
1130 		outflags |= MMAN_PP | MMAN_nl;
1131 		outflags &= ~(MMAN_sp | MMAN_br);
1132 	}
1133 
1134 	/* Maybe we are inside an enclosing list? */
1135 	if (roff_node_next(n->parent) != NULL)
1136 		mid_it();
1137 }
1138 
1139 static void
pre_br(DECL_ARGS)1140 pre_br(DECL_ARGS)
1141 {
1142 	outflags |= MMAN_br;
1143 }
1144 
1145 static int
pre_dl(DECL_ARGS)1146 pre_dl(DECL_ARGS)
1147 {
1148 	print_offs("6n", 0);
1149 	return 1;
1150 }
1151 
1152 static void
post_dl(DECL_ARGS)1153 post_dl(DECL_ARGS)
1154 {
1155 	print_line(".RE", MMAN_nl);
1156 
1157 	/* Maybe we are inside an enclosing list? */
1158 	if (roff_node_next(n->parent) != NULL)
1159 		mid_it();
1160 }
1161 
1162 static int
pre_em(DECL_ARGS)1163 pre_em(DECL_ARGS)
1164 {
1165 
1166 	font_push('I');
1167 	return 1;
1168 }
1169 
1170 static int
pre_en(DECL_ARGS)1171 pre_en(DECL_ARGS)
1172 {
1173 
1174 	if (NULL == n->norm->Es ||
1175 	    NULL == n->norm->Es->child)
1176 		return 1;
1177 
1178 	print_word(n->norm->Es->child->string);
1179 	outflags &= ~MMAN_spc;
1180 	return 1;
1181 }
1182 
1183 static void
post_en(DECL_ARGS)1184 post_en(DECL_ARGS)
1185 {
1186 
1187 	if (NULL == n->norm->Es ||
1188 	    NULL == n->norm->Es->child ||
1189 	    NULL == n->norm->Es->child->next)
1190 		return;
1191 
1192 	outflags &= ~MMAN_spc;
1193 	print_word(n->norm->Es->child->next->string);
1194 	return;
1195 }
1196 
1197 static int
pre_eo(DECL_ARGS)1198 pre_eo(DECL_ARGS)
1199 {
1200 
1201 	if (n->end == ENDBODY_NOT &&
1202 	    n->parent->head->child == NULL &&
1203 	    n->child != NULL &&
1204 	    n->child->end != ENDBODY_NOT)
1205 		print_word("\\&");
1206 	else if (n->end != ENDBODY_NOT ? n->child != NULL :
1207 	    n->parent->head->child != NULL && (n->child != NULL ||
1208 	    (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1209 		outflags &= ~(MMAN_spc | MMAN_nl);
1210 	return 1;
1211 }
1212 
1213 static void
post_eo(DECL_ARGS)1214 post_eo(DECL_ARGS)
1215 {
1216 	int	 body, tail;
1217 
1218 	if (n->end != ENDBODY_NOT) {
1219 		outflags |= MMAN_spc;
1220 		return;
1221 	}
1222 
1223 	body = n->child != NULL || n->parent->head->child != NULL;
1224 	tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1225 
1226 	if (body && tail)
1227 		outflags &= ~MMAN_spc;
1228 	else if ( ! (body || tail))
1229 		print_word("\\&");
1230 	else if ( ! tail)
1231 		outflags |= MMAN_spc;
1232 }
1233 
1234 static int
pre_fa(DECL_ARGS)1235 pre_fa(DECL_ARGS)
1236 {
1237 	int	 am_Fa;
1238 
1239 	am_Fa = MDOC_Fa == n->tok;
1240 
1241 	if (am_Fa)
1242 		n = n->child;
1243 
1244 	while (NULL != n) {
1245 		font_push('I');
1246 		if (am_Fa || NODE_SYNPRETTY & n->flags)
1247 			outflags |= MMAN_nbrword;
1248 		print_node(meta, n);
1249 		font_pop();
1250 		if (NULL != (n = n->next))
1251 			print_word(",");
1252 	}
1253 	return 0;
1254 }
1255 
1256 static void
post_fa(DECL_ARGS)1257 post_fa(DECL_ARGS)
1258 {
1259 	struct roff_node *nn;
1260 
1261 	if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa)
1262 		print_word(",");
1263 }
1264 
1265 static int
pre_fd(DECL_ARGS)1266 pre_fd(DECL_ARGS)
1267 {
1268 	pre_syn(n);
1269 	font_push('B');
1270 	return 1;
1271 }
1272 
1273 static void
post_fd(DECL_ARGS)1274 post_fd(DECL_ARGS)
1275 {
1276 	font_pop();
1277 	outflags |= MMAN_br;
1278 }
1279 
1280 static int
pre_fl(DECL_ARGS)1281 pre_fl(DECL_ARGS)
1282 {
1283 	font_push('B');
1284 	print_word("\\-");
1285 	if (n->child != NULL)
1286 		outflags &= ~MMAN_spc;
1287 	return 1;
1288 }
1289 
1290 static void
post_fl(DECL_ARGS)1291 post_fl(DECL_ARGS)
1292 {
1293 	struct roff_node *nn;
1294 
1295 	font_pop();
1296 	if (n->child == NULL &&
1297 	    ((nn = roff_node_next(n)) != NULL &&
1298 	    nn->type != ROFFT_TEXT &&
1299 	    (nn->flags & NODE_LINE) == 0))
1300 		outflags &= ~MMAN_spc;
1301 }
1302 
1303 static int
pre_fn(DECL_ARGS)1304 pre_fn(DECL_ARGS)
1305 {
1306 
1307 	pre_syn(n);
1308 
1309 	n = n->child;
1310 	if (NULL == n)
1311 		return 0;
1312 
1313 	if (NODE_SYNPRETTY & n->flags)
1314 		print_block(".HP 4n", MMAN_nl);
1315 
1316 	font_push('B');
1317 	print_node(meta, n);
1318 	font_pop();
1319 	outflags &= ~MMAN_spc;
1320 	print_word("(");
1321 	outflags &= ~MMAN_spc;
1322 
1323 	n = n->next;
1324 	if (NULL != n)
1325 		pre_fa(meta, n);
1326 	return 0;
1327 }
1328 
1329 static void
post_fn(DECL_ARGS)1330 post_fn(DECL_ARGS)
1331 {
1332 
1333 	print_word(")");
1334 	if (NODE_SYNPRETTY & n->flags) {
1335 		print_word(";");
1336 		outflags |= MMAN_PP;
1337 	}
1338 }
1339 
1340 static int
pre_fo(DECL_ARGS)1341 pre_fo(DECL_ARGS)
1342 {
1343 
1344 	switch (n->type) {
1345 	case ROFFT_BLOCK:
1346 		pre_syn(n);
1347 		break;
1348 	case ROFFT_HEAD:
1349 		if (n->child == NULL)
1350 			return 0;
1351 		if (NODE_SYNPRETTY & n->flags)
1352 			print_block(".HP 4n", MMAN_nl);
1353 		font_push('B');
1354 		break;
1355 	case ROFFT_BODY:
1356 		outflags &= ~(MMAN_spc | MMAN_nl);
1357 		print_word("(");
1358 		outflags &= ~MMAN_spc;
1359 		break;
1360 	default:
1361 		break;
1362 	}
1363 	return 1;
1364 }
1365 
1366 static void
post_fo(DECL_ARGS)1367 post_fo(DECL_ARGS)
1368 {
1369 
1370 	switch (n->type) {
1371 	case ROFFT_HEAD:
1372 		if (n->child != NULL)
1373 			font_pop();
1374 		break;
1375 	case ROFFT_BODY:
1376 		post_fn(meta, n);
1377 		break;
1378 	default:
1379 		break;
1380 	}
1381 }
1382 
1383 static int
pre_Ft(DECL_ARGS)1384 pre_Ft(DECL_ARGS)
1385 {
1386 
1387 	pre_syn(n);
1388 	font_push('I');
1389 	return 1;
1390 }
1391 
1392 static void
pre_ft(DECL_ARGS)1393 pre_ft(DECL_ARGS)
1394 {
1395 	print_line(".ft", 0);
1396 	print_word(n->child->string);
1397 	outflags |= MMAN_nl;
1398 }
1399 
1400 static int
pre_in(DECL_ARGS)1401 pre_in(DECL_ARGS)
1402 {
1403 
1404 	if (NODE_SYNPRETTY & n->flags) {
1405 		pre_syn(n);
1406 		font_push('B');
1407 		print_word("#include <");
1408 		outflags &= ~MMAN_spc;
1409 	} else {
1410 		print_word("<");
1411 		outflags &= ~MMAN_spc;
1412 		font_push('I');
1413 	}
1414 	return 1;
1415 }
1416 
1417 static void
post_in(DECL_ARGS)1418 post_in(DECL_ARGS)
1419 {
1420 
1421 	if (NODE_SYNPRETTY & n->flags) {
1422 		outflags &= ~MMAN_spc;
1423 		print_word(">");
1424 		font_pop();
1425 		outflags |= MMAN_br;
1426 	} else {
1427 		font_pop();
1428 		outflags &= ~MMAN_spc;
1429 		print_word(">");
1430 	}
1431 }
1432 
1433 static int
pre_it(DECL_ARGS)1434 pre_it(DECL_ARGS)
1435 {
1436 	const struct roff_node *bln;
1437 
1438 	switch (n->type) {
1439 	case ROFFT_HEAD:
1440 		outflags |= MMAN_PP | MMAN_nl;
1441 		bln = n->parent->parent;
1442 		if (bln->norm->Bl.comp == 0 ||
1443 		    (n->parent->prev == NULL &&
1444 		     roff_node_prev(bln->parent) == NULL))
1445 			outflags |= MMAN_sp;
1446 		outflags &= ~MMAN_br;
1447 		switch (bln->norm->Bl.type) {
1448 		case LIST_item:
1449 			return 0;
1450 		case LIST_inset:
1451 		case LIST_diag:
1452 		case LIST_ohang:
1453 			if (bln->norm->Bl.type == LIST_diag)
1454 				print_line(".B \"", 0);
1455 			else
1456 				print_line(".BR \\& \"", 0);
1457 			outflags &= ~MMAN_spc;
1458 			return 1;
1459 		case LIST_bullet:
1460 		case LIST_dash:
1461 		case LIST_hyphen:
1462 			print_width(&bln->norm->Bl, NULL);
1463 			TPremain = 0;
1464 			outflags |= MMAN_nl;
1465 			font_push('B');
1466 			if (LIST_bullet == bln->norm->Bl.type)
1467 				print_word("\\(bu");
1468 			else
1469 				print_word("-");
1470 			font_pop();
1471 			outflags |= MMAN_nl;
1472 			return 0;
1473 		case LIST_enum:
1474 			print_width(&bln->norm->Bl, NULL);
1475 			TPremain = 0;
1476 			outflags |= MMAN_nl;
1477 			print_count(&bln->norm->Bl.count);
1478 			outflags |= MMAN_nl;
1479 			return 0;
1480 		case LIST_hang:
1481 			print_width(&bln->norm->Bl, n->child);
1482 			TPremain = 0;
1483 			outflags |= MMAN_nl;
1484 			return 1;
1485 		case LIST_tag:
1486 			print_width(&bln->norm->Bl, n->child);
1487 			putchar('\n');
1488 			outflags &= ~MMAN_spc;
1489 			return 1;
1490 		default:
1491 			return 1;
1492 		}
1493 	default:
1494 		break;
1495 	}
1496 	return 1;
1497 }
1498 
1499 /*
1500  * This function is called after closing out an indented block.
1501  * If we are inside an enclosing list, restore its indentation.
1502  */
1503 static void
mid_it(void)1504 mid_it(void)
1505 {
1506 	char		 buf[24];
1507 
1508 	/* Nothing to do outside a list. */
1509 	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
1510 		return;
1511 
1512 	/* The indentation has already been set up. */
1513 	if (Bl_stack_post[Bl_stack_len - 1])
1514 		return;
1515 
1516 	/* Restore the indentation of the enclosing list. */
1517 	print_line(".RS", MMAN_Bk_susp);
1518 	(void)snprintf(buf, sizeof(buf), "%dn",
1519 	    Bl_stack[Bl_stack_len - 1]);
1520 	print_word(buf);
1521 
1522 	/* Remember to close out this .RS block later. */
1523 	Bl_stack_post[Bl_stack_len - 1] = 1;
1524 }
1525 
1526 static void
post_it(DECL_ARGS)1527 post_it(DECL_ARGS)
1528 {
1529 	const struct roff_node *bln;
1530 
1531 	bln = n->parent->parent;
1532 
1533 	switch (n->type) {
1534 	case ROFFT_HEAD:
1535 		switch (bln->norm->Bl.type) {
1536 		case LIST_diag:
1537 			outflags &= ~MMAN_spc;
1538 			print_word("\\ ");
1539 			break;
1540 		case LIST_ohang:
1541 			outflags |= MMAN_br;
1542 			break;
1543 		default:
1544 			break;
1545 		}
1546 		break;
1547 	case ROFFT_BODY:
1548 		switch (bln->norm->Bl.type) {
1549 		case LIST_bullet:
1550 		case LIST_dash:
1551 		case LIST_hyphen:
1552 		case LIST_enum:
1553 		case LIST_hang:
1554 		case LIST_tag:
1555 			assert(Bl_stack_len);
1556 			Bl_stack[--Bl_stack_len] = 0;
1557 
1558 			/*
1559 			 * Our indentation had to be restored
1560 			 * after a child display or child list.
1561 			 * Close out that indentation block now.
1562 			 */
1563 			if (Bl_stack_post[Bl_stack_len]) {
1564 				print_line(".RE", MMAN_nl);
1565 				Bl_stack_post[Bl_stack_len] = 0;
1566 			}
1567 			break;
1568 		case LIST_column:
1569 			if (NULL != n->next) {
1570 				putchar('\t');
1571 				outflags &= ~MMAN_spc;
1572 			}
1573 			break;
1574 		default:
1575 			break;
1576 		}
1577 		break;
1578 	default:
1579 		break;
1580 	}
1581 }
1582 
1583 static void
post_lb(DECL_ARGS)1584 post_lb(DECL_ARGS)
1585 {
1586 
1587 	if (SEC_LIBRARY == n->sec)
1588 		outflags |= MMAN_br;
1589 }
1590 
1591 static int
pre_lk(DECL_ARGS)1592 pre_lk(DECL_ARGS)
1593 {
1594 	const struct roff_node *link, *descr, *punct;
1595 
1596 	if ((link = n->child) == NULL)
1597 		return 0;
1598 
1599 	/* Find beginning of trailing punctuation. */
1600 	punct = n->last;
1601 	while (punct != link && punct->flags & NODE_DELIMC)
1602 		punct = punct->prev;
1603 	punct = punct->next;
1604 
1605 	/* Link text. */
1606 	if ((descr = link->next) != NULL && descr != punct) {
1607 		font_push('I');
1608 		while (descr != punct) {
1609 			print_word(descr->string);
1610 			descr = descr->next;
1611 		}
1612 		font_pop();
1613 		print_word(":");
1614 	}
1615 
1616 	/* Link target. */
1617 	font_push('B');
1618 	print_word(link->string);
1619 	font_pop();
1620 
1621 	/* Trailing punctuation. */
1622 	while (punct != NULL) {
1623 		print_word(punct->string);
1624 		punct = punct->next;
1625 	}
1626 	return 0;
1627 }
1628 
1629 static void
pre_onearg(DECL_ARGS)1630 pre_onearg(DECL_ARGS)
1631 {
1632 	outflags |= MMAN_nl;
1633 	print_word(".");
1634 	outflags &= ~MMAN_spc;
1635 	print_word(roff_name[n->tok]);
1636 	if (n->child != NULL)
1637 		print_word(n->child->string);
1638 	outflags |= MMAN_nl;
1639 	if (n->tok == ROFF_ce)
1640 		for (n = n->child->next; n != NULL; n = n->next)
1641 			print_node(meta, n);
1642 }
1643 
1644 static int
pre_li(DECL_ARGS)1645 pre_li(DECL_ARGS)
1646 {
1647 	font_push('R');
1648 	return 1;
1649 }
1650 
1651 static int
pre_nm(DECL_ARGS)1652 pre_nm(DECL_ARGS)
1653 {
1654 	char	*name;
1655 
1656 	switch (n->type) {
1657 	case ROFFT_BLOCK:
1658 		outflags |= MMAN_Bk;
1659 		pre_syn(n);
1660 		return 1;
1661 	case ROFFT_HEAD:
1662 	case ROFFT_ELEM:
1663 		break;
1664 	default:
1665 		return 1;
1666 	}
1667 	name = n->child == NULL ? NULL : n->child->string;
1668 	if (name == NULL)
1669 		return 0;
1670 	if (n->type == ROFFT_HEAD) {
1671 		if (roff_node_prev(n->parent) == NULL)
1672 			outflags |= MMAN_sp;
1673 		print_block(".HP", 0);
1674 		printf(" %dn", man_strlen(name) + 1);
1675 		outflags |= MMAN_nl;
1676 	}
1677 	font_push('B');
1678 	return 1;
1679 }
1680 
1681 static void
post_nm(DECL_ARGS)1682 post_nm(DECL_ARGS)
1683 {
1684 	switch (n->type) {
1685 	case ROFFT_BLOCK:
1686 		outflags &= ~MMAN_Bk;
1687 		break;
1688 	case ROFFT_HEAD:
1689 	case ROFFT_ELEM:
1690 		if (n->child != NULL && n->child->string != NULL)
1691 			font_pop();
1692 		break;
1693 	default:
1694 		break;
1695 	}
1696 }
1697 
1698 static int
pre_no(DECL_ARGS)1699 pre_no(DECL_ARGS)
1700 {
1701 	outflags |= MMAN_spc_force;
1702 	return 1;
1703 }
1704 
1705 static void
pre_noarg(DECL_ARGS)1706 pre_noarg(DECL_ARGS)
1707 {
1708 	outflags |= MMAN_nl;
1709 	print_word(".");
1710 	outflags &= ~MMAN_spc;
1711 	print_word(roff_name[n->tok]);
1712 	outflags |= MMAN_nl;
1713 }
1714 
1715 static int
pre_ns(DECL_ARGS)1716 pre_ns(DECL_ARGS)
1717 {
1718 	outflags &= ~MMAN_spc;
1719 	return 0;
1720 }
1721 
1722 static void
post_pf(DECL_ARGS)1723 post_pf(DECL_ARGS)
1724 {
1725 
1726 	if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1727 		outflags &= ~MMAN_spc;
1728 }
1729 
1730 static int
pre_pp(DECL_ARGS)1731 pre_pp(DECL_ARGS)
1732 {
1733 
1734 	if (MDOC_It != n->parent->tok)
1735 		outflags |= MMAN_PP;
1736 	outflags |= MMAN_sp | MMAN_nl;
1737 	outflags &= ~MMAN_br;
1738 	return 0;
1739 }
1740 
1741 static int
pre_rs(DECL_ARGS)1742 pre_rs(DECL_ARGS)
1743 {
1744 
1745 	if (SEC_SEE_ALSO == n->sec) {
1746 		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
1747 		outflags &= ~MMAN_br;
1748 	}
1749 	return 1;
1750 }
1751 
1752 static int
pre_skip(DECL_ARGS)1753 pre_skip(DECL_ARGS)
1754 {
1755 
1756 	return 0;
1757 }
1758 
1759 static int
pre_sm(DECL_ARGS)1760 pre_sm(DECL_ARGS)
1761 {
1762 
1763 	if (NULL == n->child)
1764 		outflags ^= MMAN_Sm;
1765 	else if (0 == strcmp("on", n->child->string))
1766 		outflags |= MMAN_Sm;
1767 	else
1768 		outflags &= ~MMAN_Sm;
1769 
1770 	if (MMAN_Sm & outflags)
1771 		outflags |= MMAN_spc;
1772 
1773 	return 0;
1774 }
1775 
1776 static void
pre_sp(DECL_ARGS)1777 pre_sp(DECL_ARGS)
1778 {
1779 	if (outflags & MMAN_PP) {
1780 		outflags &= ~MMAN_PP;
1781 		print_line(".PP", 0);
1782 	} else {
1783 		print_line(".sp", 0);
1784 		if (n->child != NULL)
1785 			print_word(n->child->string);
1786 	}
1787 	outflags |= MMAN_nl;
1788 }
1789 
1790 static int
pre_sy(DECL_ARGS)1791 pre_sy(DECL_ARGS)
1792 {
1793 
1794 	font_push('B');
1795 	return 1;
1796 }
1797 
1798 static void
pre_ta(DECL_ARGS)1799 pre_ta(DECL_ARGS)
1800 {
1801 	print_line(".ta", 0);
1802 	for (n = n->child; n != NULL; n = n->next)
1803 		print_word(n->string);
1804 	outflags |= MMAN_nl;
1805 }
1806 
1807 static int
pre_vt(DECL_ARGS)1808 pre_vt(DECL_ARGS)
1809 {
1810 
1811 	if (NODE_SYNPRETTY & n->flags) {
1812 		switch (n->type) {
1813 		case ROFFT_BLOCK:
1814 			pre_syn(n);
1815 			return 1;
1816 		case ROFFT_BODY:
1817 			break;
1818 		default:
1819 			return 0;
1820 		}
1821 	}
1822 	font_push('I');
1823 	return 1;
1824 }
1825 
1826 static void
post_vt(DECL_ARGS)1827 post_vt(DECL_ARGS)
1828 {
1829 
1830 	if (n->flags & NODE_SYNPRETTY && n->type != ROFFT_BODY)
1831 		return;
1832 	font_pop();
1833 }
1834 
1835 static int
pre_xr(DECL_ARGS)1836 pre_xr(DECL_ARGS)
1837 {
1838 
1839 	n = n->child;
1840 	if (NULL == n)
1841 		return 0;
1842 	print_node(meta, n);
1843 	n = n->next;
1844 	if (NULL == n)
1845 		return 0;
1846 	outflags &= ~MMAN_spc;
1847 	print_word("(");
1848 	print_node(meta, n);
1849 	print_word(")");
1850 	return 0;
1851 }
1852