1*84680f53Sschwarze /* $OpenBSD: mdoc_term.c,v 1.282 2023/11/13 19:13:00 schwarze Exp $ */
2f73abda9Skristaps /*
318bbf166Sschwarze * Copyright (c) 2010, 2012-2020, 2022 Ingo Schwarze <schwarze@openbsd.org>
40ac7e6ecSschwarze * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
5740f368bSschwarze * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
6f73abda9Skristaps *
7f73abda9Skristaps * Permission to use, copy, modify, and distribute this software for any
8a6464425Sschwarze * purpose with or without fee is hereby granted, provided that the above
9a6464425Sschwarze * copyright notice and this permission notice appear in all copies.
10f73abda9Skristaps *
11d1982c71Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12a6464425Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d1982c71Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14a6464425Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15a6464425Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a6464425Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a6464425Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
180ac7e6ecSschwarze *
190ac7e6ecSschwarze * Plain text formatter for mdoc(7), used by mandoc(1)
200ac7e6ecSschwarze * for ASCII, UTF-8, PostScript, and PDF output.
21f73abda9Skristaps */
22f73abda9Skristaps #include <sys/types.h>
23f73abda9Skristaps
24f73abda9Skristaps #include <assert.h>
25f73abda9Skristaps #include <ctype.h>
2625da2733Sschwarze #include <limits.h>
27fccfce9dSschwarze #include <stdint.h>
28f73abda9Skristaps #include <stdio.h>
29f73abda9Skristaps #include <stdlib.h>
30f73abda9Skristaps #include <string.h>
31f73abda9Skristaps
32a92c1cd8Sschwarze #include "mandoc_aux.h"
33d1982c71Sschwarze #include "roff.h"
34d1982c71Sschwarze #include "mdoc.h"
354175bdabSschwarze #include "out.h"
36f73abda9Skristaps #include "term.h"
370ac7e6ecSschwarze #include "term_tag.h"
384175bdabSschwarze #include "main.h"
39a231c1b4Sschwarze
40f73abda9Skristaps struct termpair {
41f73abda9Skristaps struct termpair *ppair;
42796a3999Sschwarze int count;
43f73abda9Skristaps };
44f73abda9Skristaps
45d6cd8bdcSschwarze #define DECL_ARGS struct termp *p, \
46d6cd8bdcSschwarze struct termpair *pair, \
472a238f45Sschwarze const struct roff_meta *meta, \
483a0d07afSschwarze struct roff_node *n
49f73abda9Skristaps
5016fe0cfcSschwarze struct mdoc_term_act {
51f73abda9Skristaps int (*pre)(DECL_ARGS);
52f73abda9Skristaps void (*post)(DECL_ARGS);
53f73abda9Skristaps };
54f73abda9Skristaps
5525da2733Sschwarze static int a2width(const struct termp *, const char *);
56b822ca0dSschwarze
57b822ca0dSschwarze static void print_bvspace(struct termp *,
587ebbefbeSschwarze struct roff_node *, struct roff_node *);
59fa70b73eSschwarze static void print_mdoc_node(DECL_ARGS);
60fa70b73eSschwarze static void print_mdoc_nodelist(DECL_ARGS);
612a238f45Sschwarze static void print_mdoc_head(struct termp *, const struct roff_meta *);
622a238f45Sschwarze static void print_mdoc_foot(struct termp *, const struct roff_meta *);
637ebbefbeSschwarze static void synopsis_pre(struct termp *, struct roff_node *);
64b822ca0dSschwarze
65d6cd8bdcSschwarze static void termp____post(DECL_ARGS);
666f207799Sschwarze static void termp__t_post(DECL_ARGS);
67d6cd8bdcSschwarze static void termp_bd_post(DECL_ARGS);
681eead9a6Sschwarze static void termp_bk_post(DECL_ARGS);
69d6cd8bdcSschwarze static void termp_bl_post(DECL_ARGS);
7003ed6ec9Sschwarze static void termp_eo_post(DECL_ARGS);
71d97bd30dSschwarze static void termp_fd_post(DECL_ARGS);
72d6cd8bdcSschwarze static void termp_fo_post(DECL_ARGS);
73d6cd8bdcSschwarze static void termp_in_post(DECL_ARGS);
74d6cd8bdcSschwarze static void termp_it_post(DECL_ARGS);
75d6cd8bdcSschwarze static void termp_lb_post(DECL_ARGS);
762ab36204Sschwarze static void termp_nm_post(DECL_ARGS);
77d6cd8bdcSschwarze static void termp_pf_post(DECL_ARGS);
782c3450fcSschwarze static void termp_quote_post(DECL_ARGS);
79d6cd8bdcSschwarze static void termp_sh_post(DECL_ARGS);
80d6cd8bdcSschwarze static void termp_ss_post(DECL_ARGS);
81816c3c54Sschwarze static void termp_xx_post(DECL_ARGS);
82d6cd8bdcSschwarze
83bf782e42Sschwarze static int termp__a_pre(DECL_ARGS);
846f207799Sschwarze static int termp__t_pre(DECL_ARGS);
857c539ecbSschwarze static int termp_abort_pre(DECL_ARGS);
868f46e13cSschwarze static int termp_an_pre(DECL_ARGS);
87d6cd8bdcSschwarze static int termp_ap_pre(DECL_ARGS);
88d6cd8bdcSschwarze static int termp_bd_pre(DECL_ARGS);
89d6cd8bdcSschwarze static int termp_bf_pre(DECL_ARGS);
901eead9a6Sschwarze static int termp_bk_pre(DECL_ARGS);
91b16e7ddfSschwarze static int termp_bl_pre(DECL_ARGS);
92a231c1b4Sschwarze static int termp_bold_pre(DECL_ARGS);
93d6cd8bdcSschwarze static int termp_d1_pre(DECL_ARGS);
9403ed6ec9Sschwarze static int termp_eo_pre(DECL_ARGS);
95d6cd8bdcSschwarze static int termp_ex_pre(DECL_ARGS);
96d6cd8bdcSschwarze static int termp_fa_pre(DECL_ARGS);
972c3450fcSschwarze static int termp_fd_pre(DECL_ARGS);
98d6cd8bdcSschwarze static int termp_fl_pre(DECL_ARGS);
99d6cd8bdcSschwarze static int termp_fn_pre(DECL_ARGS);
100d6cd8bdcSschwarze static int termp_fo_pre(DECL_ARGS);
101d6cd8bdcSschwarze static int termp_ft_pre(DECL_ARGS);
102d6cd8bdcSschwarze static int termp_in_pre(DECL_ARGS);
103d6cd8bdcSschwarze static int termp_it_pre(DECL_ARGS);
104fa70b73eSschwarze static int termp_li_pre(DECL_ARGS);
105d6cd8bdcSschwarze static int termp_lk_pre(DECL_ARGS);
106d6cd8bdcSschwarze static int termp_nd_pre(DECL_ARGS);
107d6cd8bdcSschwarze static int termp_nm_pre(DECL_ARGS);
108d6cd8bdcSschwarze static int termp_ns_pre(DECL_ARGS);
1092c3450fcSschwarze static int termp_quote_pre(DECL_ARGS);
110d6cd8bdcSschwarze static int termp_rs_pre(DECL_ARGS);
111d6cd8bdcSschwarze static int termp_sh_pre(DECL_ARGS);
11278bbbab4Sschwarze static int termp_skip_pre(DECL_ARGS);
113d6cd8bdcSschwarze static int termp_sm_pre(DECL_ARGS);
1146561cb23Sschwarze static int termp_pp_pre(DECL_ARGS);
115d6cd8bdcSschwarze static int termp_ss_pre(DECL_ARGS);
116a231c1b4Sschwarze static int termp_under_pre(DECL_ARGS);
1178521b0bcSschwarze static int termp_vt_pre(DECL_ARGS);
118d6cd8bdcSschwarze static int termp_xr_pre(DECL_ARGS);
119d6cd8bdcSschwarze static int termp_xx_pre(DECL_ARGS);
120d6cd8bdcSschwarze
12116fe0cfcSschwarze static const struct mdoc_term_act mdoc_term_acts[MDOC_MAX - MDOC_Dd] = {
122f73abda9Skristaps { NULL, NULL }, /* Dd */
123f73abda9Skristaps { NULL, NULL }, /* Dt */
124f73abda9Skristaps { NULL, NULL }, /* Os */
125f73abda9Skristaps { termp_sh_pre, termp_sh_post }, /* Sh */
126f73abda9Skristaps { termp_ss_pre, termp_ss_post }, /* Ss */
1276561cb23Sschwarze { termp_pp_pre, NULL }, /* Pp */
128d97bd30dSschwarze { termp_d1_pre, termp_bl_post }, /* D1 */
129d97bd30dSschwarze { termp_d1_pre, termp_bl_post }, /* Dl */
130f73abda9Skristaps { termp_bd_pre, termp_bd_post }, /* Bd */
131f73abda9Skristaps { NULL, NULL }, /* Ed */
132b16e7ddfSschwarze { termp_bl_pre, termp_bl_post }, /* Bl */
133f73abda9Skristaps { NULL, NULL }, /* El */
134f73abda9Skristaps { termp_it_pre, termp_it_post }, /* It */
1358cd724fbSschwarze { termp_under_pre, NULL }, /* Ad */
1363eeea6b8Sschwarze { termp_an_pre, NULL }, /* An */
13714a309e3Sschwarze { termp_ap_pre, NULL }, /* Ap */
138a231c1b4Sschwarze { termp_under_pre, NULL }, /* Ar */
1390ac7e6ecSschwarze { termp_fd_pre, NULL }, /* Cd */
140a231c1b4Sschwarze { termp_bold_pre, NULL }, /* Cm */
141e9698bccSschwarze { termp_li_pre, NULL }, /* Dv */
1420ac7e6ecSschwarze { NULL, NULL }, /* Er */
1430ac7e6ecSschwarze { NULL, NULL }, /* Ev */
144f73abda9Skristaps { termp_ex_pre, NULL }, /* Ex */
145f73abda9Skristaps { termp_fa_pre, NULL }, /* Fa */
146d97bd30dSschwarze { termp_fd_pre, termp_fd_post }, /* Fd */
147f73abda9Skristaps { termp_fl_pre, NULL }, /* Fl */
1489a98b8a1Sschwarze { termp_fn_pre, NULL }, /* Fn */
1496093755cSschwarze { termp_ft_pre, NULL }, /* Ft */
150a231c1b4Sschwarze { termp_bold_pre, NULL }, /* Ic */
151f73abda9Skristaps { termp_in_pre, termp_in_post }, /* In */
152fa70b73eSschwarze { termp_li_pre, NULL }, /* Li */
153f73abda9Skristaps { termp_nd_pre, NULL }, /* Nd */
1542ab36204Sschwarze { termp_nm_pre, termp_nm_post }, /* Nm */
1552c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Op */
1567c539ecbSschwarze { termp_abort_pre, NULL }, /* Ot */
157a231c1b4Sschwarze { termp_under_pre, NULL }, /* Pa */
1588ccddcd3Sschwarze { termp_ex_pre, NULL }, /* Rv */
15971719887Sschwarze { NULL, NULL }, /* St */
160a231c1b4Sschwarze { termp_under_pre, NULL }, /* Va */
1619a98b8a1Sschwarze { termp_vt_pre, NULL }, /* Vt */
162f73abda9Skristaps { termp_xr_pre, NULL }, /* Xr */
163bf782e42Sschwarze { termp__a_pre, termp____post }, /* %A */
164011fe33bSschwarze { termp_under_pre, termp____post }, /* %B */
165f73abda9Skristaps { NULL, termp____post }, /* %D */
166011fe33bSschwarze { termp_under_pre, termp____post }, /* %I */
167a231c1b4Sschwarze { termp_under_pre, termp____post }, /* %J */
168f73abda9Skristaps { NULL, termp____post }, /* %N */
169f73abda9Skristaps { NULL, termp____post }, /* %O */
170f73abda9Skristaps { NULL, termp____post }, /* %P */
171f73abda9Skristaps { NULL, termp____post }, /* %R */
1726f207799Sschwarze { termp__t_pre, termp__t_post }, /* %T */
173f73abda9Skristaps { NULL, termp____post }, /* %V */
174f73abda9Skristaps { NULL, NULL }, /* Ac */
1752c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Ao */
1762c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Aq */
17771719887Sschwarze { NULL, NULL }, /* At */
178f73abda9Skristaps { NULL, NULL }, /* Bc */
179f73abda9Skristaps { termp_bf_pre, NULL }, /* Bf */
1802c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Bo */
1812c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Bq */
182816c3c54Sschwarze { termp_xx_pre, termp_xx_post }, /* Bsx */
1833af8e8d7Sschwarze { NULL, NULL }, /* Bx */
18478bbbab4Sschwarze { termp_skip_pre, NULL }, /* Db */
185f73abda9Skristaps { NULL, NULL }, /* Dc */
1862c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Do */
1872c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Dq */
188b16e7ddfSschwarze { NULL, NULL }, /* Ec */ /* FIXME: no space */
189f73abda9Skristaps { NULL, NULL }, /* Ef */
1900ac7e6ecSschwarze { termp_under_pre, NULL }, /* Em */
19103ed6ec9Sschwarze { termp_eo_pre, termp_eo_post }, /* Eo */
192816c3c54Sschwarze { termp_xx_pre, termp_xx_post }, /* Fx */
193ddce0b0cSschwarze { termp_bold_pre, NULL }, /* Ms */
1946f9818f6Sschwarze { termp_li_pre, NULL }, /* No */
195f73abda9Skristaps { termp_ns_pre, NULL }, /* Ns */
196816c3c54Sschwarze { termp_xx_pre, termp_xx_post }, /* Nx */
197816c3c54Sschwarze { termp_xx_pre, termp_xx_post }, /* Ox */
198f73abda9Skristaps { NULL, NULL }, /* Pc */
199072a1c5dSschwarze { NULL, termp_pf_post }, /* Pf */
2002c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Po */
2012c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Pq */
202f73abda9Skristaps { NULL, NULL }, /* Qc */
2032c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Ql */
2042c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Qo */
2052c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Qq */
206f73abda9Skristaps { NULL, NULL }, /* Re */
207f73abda9Skristaps { termp_rs_pre, NULL }, /* Rs */
208f73abda9Skristaps { NULL, NULL }, /* Sc */
2092c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* So */
2102c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Sq */
211f73abda9Skristaps { termp_sm_pre, NULL }, /* Sm */
212a231c1b4Sschwarze { termp_under_pre, NULL }, /* Sx */
2130ac7e6ecSschwarze { termp_bold_pre, NULL }, /* Sy */
214f73abda9Skristaps { NULL, NULL }, /* Tn */
215816c3c54Sschwarze { termp_xx_pre, termp_xx_post }, /* Ux */
216f73abda9Skristaps { NULL, NULL }, /* Xc */
217f73abda9Skristaps { NULL, NULL }, /* Xo */
218f73abda9Skristaps { termp_fo_pre, termp_fo_post }, /* Fo */
219f73abda9Skristaps { NULL, NULL }, /* Fc */
2202c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Oo */
221f73abda9Skristaps { NULL, NULL }, /* Oc */
2221eead9a6Sschwarze { termp_bk_pre, termp_bk_post }, /* Bk */
223f73abda9Skristaps { NULL, NULL }, /* Ek */
2248ccddcd3Sschwarze { NULL, NULL }, /* Bt */
225f73abda9Skristaps { NULL, NULL }, /* Hf */
226551cd4a8Sschwarze { termp_under_pre, NULL }, /* Fr */
2278ccddcd3Sschwarze { NULL, NULL }, /* Ud */
22871719887Sschwarze { NULL, termp_lb_post }, /* Lb */
2297c539ecbSschwarze { termp_abort_pre, NULL }, /* Lp */
230f73abda9Skristaps { termp_lk_pre, NULL }, /* Lk */
231a231c1b4Sschwarze { termp_under_pre, NULL }, /* Mt */
2322c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Brq */
2332c3450fcSschwarze { termp_quote_pre, termp_quote_post }, /* Bro */
234f73abda9Skristaps { NULL, NULL }, /* Brc */
235011fe33bSschwarze { NULL, termp____post }, /* %C */
23678bbbab4Sschwarze { termp_skip_pre, NULL }, /* Es */
237551cd4a8Sschwarze { termp_quote_pre, termp_quote_post }, /* En */
238816c3c54Sschwarze { termp_xx_pre, termp_xx_post }, /* Dx */
239011fe33bSschwarze { NULL, termp____post }, /* %Q */
2400397c682Sschwarze { NULL, termp____post }, /* %U */
2416093755cSschwarze { NULL, NULL }, /* Ta */
2429a542ed3Sschwarze { termp_skip_pre, NULL }, /* Tg */
243f73abda9Skristaps };
244f73abda9Skristaps
24514a309e3Sschwarze
2466ae2e8acSschwarze void
terminal_mdoc(void * arg,const struct roff_meta * mdoc)2476b86842eSschwarze terminal_mdoc(void *arg, const struct roff_meta *mdoc)
248f73abda9Skristaps {
249af29ff23Sschwarze struct roff_node *n, *nn;
2504175bdabSschwarze struct termp *p;
2514175bdabSschwarze
2524175bdabSschwarze p = (struct termp *)arg;
253e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin = p->defrmargin;
254f7242c43Sschwarze term_tab_set(p, NULL);
255f7242c43Sschwarze term_tab_set(p, "T");
256f7242c43Sschwarze term_tab_set(p, ".5i");
257ac531cf1Sschwarze
2586d0e9b63Sschwarze n = mdoc->first->child;
2590a0199c7Sschwarze if (p->synopsisonly) {
260af29ff23Sschwarze for (nn = NULL; n != NULL; n = n->next) {
261af29ff23Sschwarze if (n->tok != MDOC_Sh)
262af29ff23Sschwarze continue;
263af29ff23Sschwarze if (n->sec == SEC_SYNOPSIS)
2640a0199c7Sschwarze break;
265af29ff23Sschwarze if (nn == NULL && n->sec == SEC_NAME)
266af29ff23Sschwarze nn = n;
2672e162c13Sschwarze }
268af29ff23Sschwarze if (n == NULL)
269af29ff23Sschwarze n = nn;
270af29ff23Sschwarze p->flags |= TERMP_NOSPACE;
271af29ff23Sschwarze if (n != NULL && (n = n->child->next->child) != NULL)
272af29ff23Sschwarze print_mdoc_nodelist(p, NULL, mdoc, n);
273af29ff23Sschwarze term_newln(p);
2740a0199c7Sschwarze } else {
2756b86842eSschwarze term_begin(p, print_mdoc_head, print_mdoc_foot, mdoc);
2764c293873Sschwarze while (n != NULL &&
2774c293873Sschwarze (n->type == ROFFT_COMMENT ||
2784c293873Sschwarze n->flags & NODE_NOPRT))
27943808411Sschwarze n = n->next;
2800a0199c7Sschwarze if (n != NULL) {
2810a0199c7Sschwarze if (n->tok != MDOC_Sh)
2820a0199c7Sschwarze term_vspace(p);
2836b86842eSschwarze print_mdoc_nodelist(p, NULL, mdoc, n);
2840a0199c7Sschwarze }
285f95d589eSschwarze term_end(p);
286f73abda9Skristaps }
2870a0199c7Sschwarze }
288f73abda9Skristaps
289f73abda9Skristaps static void
print_mdoc_nodelist(DECL_ARGS)290fa70b73eSschwarze print_mdoc_nodelist(DECL_ARGS)
291f73abda9Skristaps {
292e4534905Sschwarze while (n != NULL) {
2937ead8a4eSschwarze print_mdoc_node(p, pair, meta, n);
294e4534905Sschwarze n = n->next;
295e4534905Sschwarze }
296f73abda9Skristaps }
297f73abda9Skristaps
298f73abda9Skristaps static void
print_mdoc_node(DECL_ARGS)299fa70b73eSschwarze print_mdoc_node(DECL_ARGS)
300f73abda9Skristaps {
30116fe0cfcSschwarze const struct mdoc_term_act *act;
302f73abda9Skristaps struct termpair npair;
303c5509750Sschwarze size_t offset, rmargin;
30416fe0cfcSschwarze int chld;
305f73abda9Skristaps
306e1e1437fSschwarze /*
307e1e1437fSschwarze * In no-fill mode, break the output line at the beginning
308e1e1437fSschwarze * of new input lines except after \c, and nowhere else.
309e1e1437fSschwarze */
310e1e1437fSschwarze
311e1e1437fSschwarze if (n->flags & NODE_NOFILL) {
312e1e1437fSschwarze if (n->flags & NODE_LINE &&
313e1e1437fSschwarze (p->flags & TERMP_NONEWLINE) == 0)
314e1e1437fSschwarze term_newln(p);
315e1e1437fSschwarze p->flags |= TERMP_BRNEVER;
31618bbf166Sschwarze } else {
31718bbf166Sschwarze if (n->flags & NODE_LINE)
31818bbf166Sschwarze term_tab_ref(p);
319e1e1437fSschwarze p->flags &= ~TERMP_BRNEVER;
32018bbf166Sschwarze }
321e1e1437fSschwarze
3224c293873Sschwarze if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
32343808411Sschwarze return;
32443808411Sschwarze
325a231c1b4Sschwarze chld = 1;
326e93ea447Sschwarze offset = p->tcol->offset;
327e93ea447Sschwarze rmargin = p->tcol->rmargin;
328c4b0939cSschwarze n->flags &= ~NODE_ENDED;
329f29a1da1Sschwarze n->prev_font = p->fonti;
330c5509750Sschwarze
331a66b65d0Sschwarze memset(&npair, 0, sizeof(struct termpair));
332f73abda9Skristaps npair.ppair = pair;
333f21ae079Sschwarze
334e053e0fdSschwarze if (n->flags & NODE_ID && n->tok != MDOC_Pp &&
335e053e0fdSschwarze (n->tok != MDOC_It || n->type != ROFFT_BLOCK))
3360ac7e6ecSschwarze term_tag_write(n, p->line);
3379a542ed3Sschwarze
338769ee804Sschwarze /*
339769ee804Sschwarze * Keeps only work until the end of a line. If a keep was
340769ee804Sschwarze * invoked in a prior line, revert it to PREKEEP.
341769ee804Sschwarze */
342769ee804Sschwarze
343c4b0939cSschwarze if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
344769ee804Sschwarze p->flags &= ~TERMP_KEEP;
345769ee804Sschwarze p->flags |= TERMP_PREKEEP;
346769ee804Sschwarze }
347769ee804Sschwarze
348eb14422aSschwarze /*
3499a456f81Sschwarze * After the keep flags have been set up, we may now
3509a456f81Sschwarze * produce output. Note that some pre-handlers do so.
3519a456f81Sschwarze */
3529a456f81Sschwarze
3535fff9381Sschwarze act = NULL;
3549a456f81Sschwarze switch (n->type) {
355d1982c71Sschwarze case ROFFT_TEXT:
356e1e1437fSschwarze if (n->flags & NODE_LINE) {
357e1e1437fSschwarze switch (*n->string) {
358e1e1437fSschwarze case '\0':
359e1e1437fSschwarze if (p->flags & TERMP_NONEWLINE)
3609a456f81Sschwarze term_newln(p);
361e1e1437fSschwarze else
362e1e1437fSschwarze term_vspace(p);
363e1e1437fSschwarze return;
364e1e1437fSschwarze case ' ':
365e1e1437fSschwarze if ((p->flags & TERMP_NONEWLINE) == 0)
366e1e1437fSschwarze term_newln(p);
367e1e1437fSschwarze break;
368e1e1437fSschwarze default:
369e1e1437fSschwarze break;
370e1e1437fSschwarze }
371e1e1437fSschwarze }
372c4b0939cSschwarze if (NODE_DELIMC & n->flags)
373a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
3749a456f81Sschwarze term_word(p, n->string);
375c4b0939cSschwarze if (NODE_DELIMO & n->flags)
376a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
3779a456f81Sschwarze break;
378d1982c71Sschwarze case ROFFT_EQN:
379c4b0939cSschwarze if ( ! (n->flags & NODE_LINE))
38096e1823cSschwarze p->flags |= TERMP_NOSPACE;
381f8618d99Sschwarze term_eqn(p, n->eqn);
382c4b0939cSschwarze if (n->next != NULL && ! (n->next->flags & NODE_LINE))
383e2624e2fSschwarze p->flags |= TERMP_NOSPACE;
3848d973ab1Sschwarze break;
385d1982c71Sschwarze case ROFFT_TBL:
386945de12cSschwarze if (p->tbl.cols == NULL)
387945de12cSschwarze term_newln(p);
3889a456f81Sschwarze term_tbl(p, n->span);
3899a456f81Sschwarze break;
3909a456f81Sschwarze default:
39129478532Sschwarze if (n->tok < ROFF_MAX) {
39296a5de47Sschwarze roff_term_pre(p, n);
393644b390bSschwarze return;
39429478532Sschwarze }
39529478532Sschwarze assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
39616fe0cfcSschwarze act = mdoc_term_acts + (n->tok - MDOC_Dd);
39716fe0cfcSschwarze if (act->pre != NULL &&
39830e5ee06Sschwarze (n->end == ENDBODY_NOT || n->child != NULL))
39916fe0cfcSschwarze chld = (*act->pre)(p, &npair, meta, n);
4009a456f81Sschwarze break;
4019a456f81Sschwarze }
4029a456f81Sschwarze
4038c089adfSschwarze if (chld && n->child)
4047ead8a4eSschwarze print_mdoc_nodelist(p, &npair, meta, n->child);
405f73abda9Skristaps
406d0f586adSschwarze term_fontpopq(p,
407ae2efdd8Sschwarze (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
408f73abda9Skristaps
4092791bd1cSschwarze switch (n->type) {
410d1982c71Sschwarze case ROFFT_TEXT:
4112791bd1cSschwarze break;
412d1982c71Sschwarze case ROFFT_TBL:
4132791bd1cSschwarze break;
414d1982c71Sschwarze case ROFFT_EQN:
4158d973ab1Sschwarze break;
4162791bd1cSschwarze default:
41716fe0cfcSschwarze if (act->post == NULL || n->flags & NODE_ENDED)
4182791bd1cSschwarze break;
41916fe0cfcSschwarze (void)(*act->post)(p, &npair, meta, n);
420c5509750Sschwarze
42132dadbacSschwarze /*
42232dadbacSschwarze * Explicit end tokens not only call the post
42332dadbacSschwarze * handler, but also tell the respective block
42432dadbacSschwarze * that it must not call the post handler again.
42532dadbacSschwarze */
426769ee804Sschwarze if (ENDBODY_NOT != n->end)
427c4b0939cSschwarze n->body->flags |= NODE_ENDED;
4282791bd1cSschwarze break;
42932dadbacSschwarze }
43032dadbacSschwarze
431c4b0939cSschwarze if (NODE_EOS & n->flags)
432bc49dbe1Sschwarze p->flags |= TERMP_SENTENCE;
433bc49dbe1Sschwarze
4348e935ceaSschwarze if (n->type != ROFFT_TEXT)
435e93ea447Sschwarze p->tcol->offset = offset;
436e93ea447Sschwarze p->tcol->rmargin = rmargin;
437f73abda9Skristaps }
438f73abda9Skristaps
439f73abda9Skristaps static void
print_mdoc_foot(struct termp * p,const struct roff_meta * meta)4402a238f45Sschwarze print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
441f73abda9Skristaps {
44201e00683Sschwarze size_t sz;
443f95d589eSschwarze
444fa70b73eSschwarze term_fontrepl(p, TERMFONT_NONE);
445fa70b73eSschwarze
4466422bc17Sschwarze /*
4476422bc17Sschwarze * Output the footer in new-groff style, that is, three columns
4486422bc17Sschwarze * with the middle being the manual date and flanking columns
4496422bc17Sschwarze * being the operating system:
4506422bc17Sschwarze *
4516422bc17Sschwarze * SYSTEM DATE SYSTEM
4526422bc17Sschwarze */
4536422bc17Sschwarze
454f73abda9Skristaps term_vspace(p);
455f73abda9Skristaps
456e93ea447Sschwarze p->tcol->offset = 0;
45701e00683Sschwarze sz = term_strlen(p, meta->date);
458e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin > sz ?
45901e00683Sschwarze (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
460d42cab1cSschwarze p->trailspace = 1;
4616422bc17Sschwarze p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
462f73abda9Skristaps
4637ead8a4eSschwarze term_word(p, meta->os);
464f73abda9Skristaps term_flushln(p);
465f73abda9Skristaps
466e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
46701e00683Sschwarze sz = term_strlen(p, meta->os);
468e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
4696b4eabafSschwarze p->flags |= TERMP_NOSPACE;
470f73abda9Skristaps
4717ead8a4eSschwarze term_word(p, meta->date);
472f73abda9Skristaps term_flushln(p);
473f73abda9Skristaps
474e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
475e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
476d42cab1cSschwarze p->trailspace = 0;
4776422bc17Sschwarze p->flags &= ~TERMP_NOBREAK;
4786b4eabafSschwarze p->flags |= TERMP_NOSPACE;
4796422bc17Sschwarze
4807ead8a4eSschwarze term_word(p, meta->os);
4816422bc17Sschwarze term_flushln(p);
4826422bc17Sschwarze
483e93ea447Sschwarze p->tcol->offset = 0;
484e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
4856422bc17Sschwarze p->flags = 0;
486f73abda9Skristaps }
487f73abda9Skristaps
488f73abda9Skristaps static void
print_mdoc_head(struct termp * p,const struct roff_meta * meta)4892a238f45Sschwarze print_mdoc_head(struct termp *p, const struct roff_meta *meta)
490f73abda9Skristaps {
4910b2f1307Sschwarze char *volume, *title;
4920b2f1307Sschwarze size_t vollen, titlen;
493f95d589eSschwarze
494f73abda9Skristaps /*
495f73abda9Skristaps * The header is strange. It has three components, which are
496f73abda9Skristaps * really two with the first duplicated. It goes like this:
497f73abda9Skristaps *
498f73abda9Skristaps * IDENTIFIER TITLE IDENTIFIER
499f73abda9Skristaps *
500f73abda9Skristaps * The IDENTIFIER is NAME(SECTION), which is the command-name
501f73abda9Skristaps * (if given, or "unknown" if not) followed by the manual page
502f73abda9Skristaps * section. These are given in `Dt'. The TITLE is a free-form
503f73abda9Skristaps * string depending on the manual volume. If not specified, it
504f73abda9Skristaps * switches on the manual section.
505f73abda9Skristaps */
506f73abda9Skristaps
5077ead8a4eSschwarze assert(meta->vol);
5080b2f1307Sschwarze if (NULL == meta->arch)
5090b2f1307Sschwarze volume = mandoc_strdup(meta->vol);
5100b2f1307Sschwarze else
5110b2f1307Sschwarze mandoc_asprintf(&volume, "%s (%s)",
5120b2f1307Sschwarze meta->vol, meta->arch);
5130b2f1307Sschwarze vollen = term_strlen(p, volume);
514f73abda9Skristaps
5153fdead0cSschwarze if (NULL == meta->msec)
5163fdead0cSschwarze title = mandoc_strdup(meta->title);
5173fdead0cSschwarze else
5183fdead0cSschwarze mandoc_asprintf(&title, "%s(%s)",
5193fdead0cSschwarze meta->title, meta->msec);
52004bba6bcSschwarze titlen = term_strlen(p, title);
521f73abda9Skristaps
522f73abda9Skristaps p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
523d42cab1cSschwarze p->trailspace = 1;
524e93ea447Sschwarze p->tcol->offset = 0;
525e93ea447Sschwarze p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
5260b2f1307Sschwarze (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
52701e00683Sschwarze vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
528f73abda9Skristaps
529f73abda9Skristaps term_word(p, title);
530f73abda9Skristaps term_flushln(p);
531f73abda9Skristaps
5326b4eabafSschwarze p->flags |= TERMP_NOSPACE;
533e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
534e93ea447Sschwarze p->tcol->rmargin = p->tcol->offset + vollen + titlen <
535e93ea447Sschwarze p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
536f73abda9Skristaps
5370b2f1307Sschwarze term_word(p, volume);
538f73abda9Skristaps term_flushln(p);
539f73abda9Skristaps
54004bba6bcSschwarze p->flags &= ~TERMP_NOBREAK;
541d42cab1cSschwarze p->trailspace = 0;
542e93ea447Sschwarze if (p->tcol->rmargin + titlen <= p->maxrmargin) {
54304bba6bcSschwarze p->flags |= TERMP_NOSPACE;
544e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
545e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
546f73abda9Skristaps term_word(p, title);
547f73abda9Skristaps term_flushln(p);
54804bba6bcSschwarze }
549f73abda9Skristaps
55004bba6bcSschwarze p->flags &= ~TERMP_NOSPACE;
551e93ea447Sschwarze p->tcol->offset = 0;
552e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
553a92c1cd8Sschwarze free(title);
5540b2f1307Sschwarze free(volume);
555f73abda9Skristaps }
556f73abda9Skristaps
55725da2733Sschwarze static int
a2width(const struct termp * p,const char * v)5583ebeb861Sschwarze a2width(const struct termp *p, const char *v)
5594175bdabSschwarze {
5604175bdabSschwarze struct roffsu su;
561ecd22486Sschwarze const char *end;
5624175bdabSschwarze
563ecd22486Sschwarze end = a2roffsu(v, &su, SCALE_MAX);
564ecd22486Sschwarze if (end == NULL || *end != '\0') {
565f463ffc6Sschwarze su.unit = SCALE_EN;
566f463ffc6Sschwarze su.scale = term_strlen(p, v) / term_strlen(p, "0");
56725da2733Sschwarze }
568f4692b45Sschwarze return term_hen(p, &su);
5694175bdabSschwarze }
5704175bdabSschwarze
5715d7d1836Sschwarze /*
5725d7d1836Sschwarze * Determine how much space to print out before block elements of `It'
5735d7d1836Sschwarze * (and thus `Bl') and `Bd'. And then go ahead and print that space,
5745d7d1836Sschwarze * too.
5755d7d1836Sschwarze */
576152a4d05Sschwarze static void
print_bvspace(struct termp * p,struct roff_node * bl,struct roff_node * n)5777ebbefbeSschwarze print_bvspace(struct termp *p, struct roff_node *bl, struct roff_node *n)
578f73abda9Skristaps {
5797ebbefbeSschwarze struct roff_node *nn;
5801cdbf331Sschwarze
581f73abda9Skristaps term_newln(p);
58231e23753Sschwarze
5837ebbefbeSschwarze if ((bl->tok == MDOC_Bd && bl->norm->Bd.comp) ||
5847ebbefbeSschwarze (bl->tok == MDOC_Bl && bl->norm->Bl.comp))
585152a4d05Sschwarze return;
586152a4d05Sschwarze
5878c089adfSschwarze /* Do not vspace directly after Ss/Sh. */
588f73abda9Skristaps
589831618f8Sschwarze nn = n;
5907ebbefbeSschwarze while (roff_node_prev(nn) == NULL) {
591831618f8Sschwarze do {
592831618f8Sschwarze nn = nn->parent;
593d1982c71Sschwarze if (nn->type == ROFFT_ROOT)
594152a4d05Sschwarze return;
595d1982c71Sschwarze } while (nn->type != ROFFT_BLOCK);
596831618f8Sschwarze if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
597152a4d05Sschwarze return;
598831618f8Sschwarze if (nn->tok == MDOC_It &&
599831618f8Sschwarze nn->parent->parent->norm->Bl.type != LIST_item)
600f73abda9Skristaps break;
601f73abda9Skristaps }
602f73abda9Skristaps
6037ebbefbeSschwarze /*
6047ebbefbeSschwarze * No vertical space after:
6057ebbefbeSschwarze * items in .Bl -column
6067ebbefbeSschwarze * items without a body in .Bl -diag
6077ebbefbeSschwarze */
608152a4d05Sschwarze
6097ebbefbeSschwarze if (bl->tok != MDOC_Bl ||
6107ebbefbeSschwarze n->prev == NULL || n->prev->tok != MDOC_It ||
6117ebbefbeSschwarze (bl->norm->Bl.type != LIST_column &&
6127ebbefbeSschwarze (bl->norm->Bl.type != LIST_diag ||
6137ebbefbeSschwarze n->prev->body->child != NULL)))
614152a4d05Sschwarze term_vspace(p);
615f73abda9Skristaps }
616f73abda9Skristaps
617f73abda9Skristaps
618f73abda9Skristaps static int
termp_it_pre(DECL_ARGS)619f73abda9Skristaps termp_it_pre(DECL_ARGS)
620f73abda9Skristaps {
621d43cc8c2Sschwarze struct roffsu su;
62247813146Sschwarze char buf[24];
6233a0d07afSschwarze const struct roff_node *bl, *nn;
62425da2733Sschwarze size_t ncols, dcol;
62525da2733Sschwarze int i, offset, width;
62650e63e03Sschwarze enum mdoc_list type;
627f73abda9Skristaps
628d1982c71Sschwarze if (n->type == ROFFT_BLOCK) {
6294175bdabSschwarze print_bvspace(p, n->parent->parent, n);
630e053e0fdSschwarze if (n->flags & NODE_ID)
631e053e0fdSschwarze term_tag_write(n, p->line);
632526e306bSschwarze return 1;
633152a4d05Sschwarze }
634f73abda9Skristaps
6358c089adfSschwarze bl = n->parent->parent->parent;
6368c62fbf5Sschwarze type = bl->norm->Bl.type;
637f73abda9Skristaps
6385d7d1836Sschwarze /*
63925da2733Sschwarze * Defaults for specific list types.
64025da2733Sschwarze */
64125da2733Sschwarze
64225da2733Sschwarze switch (type) {
64325da2733Sschwarze case LIST_bullet:
64425da2733Sschwarze case LIST_dash:
64525da2733Sschwarze case LIST_hyphen:
64625da2733Sschwarze case LIST_enum:
64725da2733Sschwarze width = term_len(p, 2);
64825da2733Sschwarze break;
64925da2733Sschwarze case LIST_hang:
6500ed87641Sschwarze case LIST_tag:
65125da2733Sschwarze width = term_len(p, 8);
65225da2733Sschwarze break;
65325da2733Sschwarze case LIST_column:
65425da2733Sschwarze width = term_len(p, 10);
65525da2733Sschwarze break;
65625da2733Sschwarze default:
65725da2733Sschwarze width = 0;
65825da2733Sschwarze break;
65925da2733Sschwarze }
66025da2733Sschwarze offset = 0;
66125da2733Sschwarze
66225da2733Sschwarze /*
6635d7d1836Sschwarze * First calculate width and offset. This is pretty easy unless
6645d7d1836Sschwarze * we're a -column list, in which case all prior columns must
6655d7d1836Sschwarze * be accounted for.
6665d7d1836Sschwarze */
6675d7d1836Sschwarze
66825da2733Sschwarze if (bl->norm->Bl.offs != NULL) {
66990d52a15Sschwarze offset = a2width(p, bl->norm->Bl.offs);
670e93ea447Sschwarze if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
671e93ea447Sschwarze offset = -p->tcol->offset;
67225da2733Sschwarze else if (offset > SHRT_MAX)
67325da2733Sschwarze offset = 0;
67425da2733Sschwarze }
6750ce9233aSschwarze
676f73abda9Skristaps switch (type) {
67749aff9f8Sschwarze case LIST_column:
678d1982c71Sschwarze if (n->type == ROFFT_HEAD)
679f73abda9Skristaps break;
68031e23753Sschwarze
6813189548fSschwarze /*
6825d7d1836Sschwarze * Imitate groff's column handling:
6835d7d1836Sschwarze * - For each earlier column, add its width.
6845d7d1836Sschwarze * - For less than 5 columns, add four more blanks per
6855d7d1836Sschwarze * column.
6865d7d1836Sschwarze * - For exactly 5 columns, add three more blank per
6875d7d1836Sschwarze * column.
6885d7d1836Sschwarze * - For more than 5 columns, add only one column.
6893189548fSschwarze */
6908c62fbf5Sschwarze ncols = bl->norm->Bl.ncols;
6913ebeb861Sschwarze dcol = ncols < 5 ? term_len(p, 4) :
6923ebeb861Sschwarze ncols == 5 ? term_len(p, 3) : term_len(p, 1);
6935d7d1836Sschwarze
6946093755cSschwarze /*
695d1982c71Sschwarze * Calculate the offset by applying all prior ROFFT_BODY,
696d1982c71Sschwarze * so we stop at the ROFFT_HEAD (nn->prev == NULL).
6976093755cSschwarze */
6986093755cSschwarze
6995d7d1836Sschwarze for (i = 0, nn = n->prev;
7006093755cSschwarze nn->prev && i < (int)ncols;
701d43cc8c2Sschwarze nn = nn->prev, i++) {
702f463ffc6Sschwarze su.unit = SCALE_EN;
703f463ffc6Sschwarze su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
704f463ffc6Sschwarze term_strlen(p, "0");
705f4692b45Sschwarze offset += term_hen(p, &su) + dcol;
706d43cc8c2Sschwarze }
7073189548fSschwarze
7080ce9233aSschwarze /*
7095d7d1836Sschwarze * When exceeding the declared number of columns, leave
7105d7d1836Sschwarze * the remaining widths at 0. This will later be
7115d7d1836Sschwarze * adjusted to the default width of 10, or, for the last
7125d7d1836Sschwarze * column, stretched to the right margin.
7130ce9233aSschwarze */
7145d7d1836Sschwarze if (i >= (int)ncols)
7155d7d1836Sschwarze break;
7163189548fSschwarze
7170ce9233aSschwarze /*
7185d7d1836Sschwarze * Use the declared column widths, extended as explained
7195d7d1836Sschwarze * in the preceding paragraph.
7200ce9233aSschwarze */
721f463ffc6Sschwarze su.unit = SCALE_EN;
722f463ffc6Sschwarze su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
723f463ffc6Sschwarze term_strlen(p, "0");
724f4692b45Sschwarze width = term_hen(p, &su) + dcol;
725f73abda9Skristaps break;
726f73abda9Skristaps default:
7278c62fbf5Sschwarze if (NULL == bl->norm->Bl.width)
7285d7d1836Sschwarze break;
7295d7d1836Sschwarze
7305d7d1836Sschwarze /*
7315d7d1836Sschwarze * Note: buffer the width by 2, which is groff's magic
7325d7d1836Sschwarze * number for buffering single arguments. See the above
7335d7d1836Sschwarze * handling for column for how this changes.
7345d7d1836Sschwarze */
7358c62fbf5Sschwarze width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
736e93ea447Sschwarze if (width < 0 && (size_t)(-width) > p->tcol->offset)
737e93ea447Sschwarze width = -p->tcol->offset;
73825da2733Sschwarze else if (width > SHRT_MAX)
73925da2733Sschwarze width = 0;
740f73abda9Skristaps break;
741f73abda9Skristaps }
742f73abda9Skristaps
743f73abda9Skristaps /*
744ba61e67eSschwarze * Whitespace control. Inset bodies need an initial space,
745ba61e67eSschwarze * while diagonal bodies need two.
746f73abda9Skristaps */
747f73abda9Skristaps
748bb89415cSschwarze p->flags |= TERMP_NOSPACE;
749bb89415cSschwarze
750f73abda9Skristaps switch (type) {
75149aff9f8Sschwarze case LIST_diag:
752d1982c71Sschwarze if (n->type == ROFFT_BODY)
753bb89415cSschwarze term_word(p, "\\ \\ ");
754bb89415cSschwarze break;
75549aff9f8Sschwarze case LIST_inset:
75630e5ee06Sschwarze if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
757bb89415cSschwarze term_word(p, "\\ ");
758f73abda9Skristaps break;
759f73abda9Skristaps default:
760f73abda9Skristaps break;
761f73abda9Skristaps }
762f73abda9Skristaps
763bb89415cSschwarze p->flags |= TERMP_NOSPACE;
764bb89415cSschwarze
765f73abda9Skristaps switch (type) {
76649aff9f8Sschwarze case LIST_diag:
767d1982c71Sschwarze if (n->type == ROFFT_HEAD)
768fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
769f73abda9Skristaps break;
770f73abda9Skristaps default:
771f73abda9Skristaps break;
772f73abda9Skristaps }
773f73abda9Skristaps
774f73abda9Skristaps /*
7755d7d1836Sschwarze * Pad and break control. This is the tricky part. These flags
7765d7d1836Sschwarze * are documented in term_flushln() in term.c. Note that we're
7775d7d1836Sschwarze * going to unset all of these flags in termp_it_post() when we
7785d7d1836Sschwarze * exit.
779f73abda9Skristaps */
780f73abda9Skristaps
781f73abda9Skristaps switch (type) {
78249aff9f8Sschwarze case LIST_enum:
7839630d74cSschwarze case LIST_bullet:
7849630d74cSschwarze case LIST_dash:
7859630d74cSschwarze case LIST_hyphen:
786771c54bcSschwarze if (n->type == ROFFT_HEAD) {
787771c54bcSschwarze p->flags |= TERMP_NOBREAK | TERMP_HANG;
788d42cab1cSschwarze p->trailspace = 1;
789771c54bcSschwarze } else if (width <= (int)term_len(p, 2))
790771c54bcSschwarze p->flags |= TERMP_NOPAD;
791e9723699Sschwarze break;
79249aff9f8Sschwarze case LIST_hang:
793d1982c71Sschwarze if (n->type != ROFFT_HEAD)
7947039ffb7Sschwarze break;
795eeb5fd14Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
796d42cab1cSschwarze p->trailspace = 1;
797e9723699Sschwarze break;
79849aff9f8Sschwarze case LIST_tag:
799d1982c71Sschwarze if (n->type != ROFFT_HEAD)
800e9723699Sschwarze break;
801d42cab1cSschwarze
802b8d4b727Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
803d42cab1cSschwarze p->trailspace = 2;
804d42cab1cSschwarze
8058c089adfSschwarze if (NULL == n->next || NULL == n->next->child)
806771c54bcSschwarze p->flags |= TERMP_HANG;
807f73abda9Skristaps break;
80849aff9f8Sschwarze case LIST_column:
809d1982c71Sschwarze if (n->type == ROFFT_HEAD)
8106093755cSschwarze break;
8116093755cSschwarze
812d42cab1cSschwarze if (NULL == n->next) {
813f73abda9Skristaps p->flags &= ~TERMP_NOBREAK;
814d42cab1cSschwarze p->trailspace = 0;
815d42cab1cSschwarze } else {
816f73abda9Skristaps p->flags |= TERMP_NOBREAK;
817d42cab1cSschwarze p->trailspace = 1;
818d42cab1cSschwarze }
8196093755cSschwarze
820f73abda9Skristaps break;
82149aff9f8Sschwarze case LIST_diag:
822d1982c71Sschwarze if (n->type != ROFFT_HEAD)
823d42cab1cSschwarze break;
824eeb5fd14Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRIND;
825d42cab1cSschwarze p->trailspace = 1;
826f73abda9Skristaps break;
827f73abda9Skristaps default:
828f73abda9Skristaps break;
829f73abda9Skristaps }
830f73abda9Skristaps
831f73abda9Skristaps /*
832f73abda9Skristaps * Margin control. Set-head-width lists have their right
833f73abda9Skristaps * margins shortened. The body for these lists has the offset
834f73abda9Skristaps * necessarily lengthened. Everybody gets the offset.
835f73abda9Skristaps */
836f73abda9Skristaps
837e93ea447Sschwarze p->tcol->offset += offset;
838f73abda9Skristaps
839f73abda9Skristaps switch (type) {
84049aff9f8Sschwarze case LIST_bullet:
84149aff9f8Sschwarze case LIST_dash:
84249aff9f8Sschwarze case LIST_enum:
84349aff9f8Sschwarze case LIST_hyphen:
844771c54bcSschwarze case LIST_hang:
84549aff9f8Sschwarze case LIST_tag:
846d1982c71Sschwarze if (n->type == ROFFT_HEAD)
847e93ea447Sschwarze p->tcol->rmargin = p->tcol->offset + width;
84801e00683Sschwarze else
849e93ea447Sschwarze p->tcol->offset += width;
850f73abda9Skristaps break;
85149aff9f8Sschwarze case LIST_column:
85253292e81Sschwarze assert(width);
853e93ea447Sschwarze p->tcol->rmargin = p->tcol->offset + width;
8548ae554d6Sschwarze /*
8558ae554d6Sschwarze * XXX - this behaviour is not documented: the
8568ae554d6Sschwarze * right-most column is filled to the right margin.
8578ae554d6Sschwarze */
858d1982c71Sschwarze if (n->type == ROFFT_HEAD)
8596093755cSschwarze break;
860e93ea447Sschwarze if (n->next == NULL && p->tcol->rmargin < p->maxrmargin)
861e93ea447Sschwarze p->tcol->rmargin = p->maxrmargin;
862f73abda9Skristaps break;
863f73abda9Skristaps default:
864f73abda9Skristaps break;
865f73abda9Skristaps }
866f73abda9Skristaps
867f73abda9Skristaps /*
868f73abda9Skristaps * The dash, hyphen, bullet and enum lists all have a special
8694c45d6c2Sschwarze * HEAD character (temporarily bold, in some cases).
870f73abda9Skristaps */
871f73abda9Skristaps
872d1982c71Sschwarze if (n->type == ROFFT_HEAD)
873f73abda9Skristaps switch (type) {
87449aff9f8Sschwarze case LIST_bullet:
875fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
876f73abda9Skristaps term_word(p, "\\[bu]");
877fa70b73eSschwarze term_fontpop(p);
878f73abda9Skristaps break;
87949aff9f8Sschwarze case LIST_dash:
88049aff9f8Sschwarze case LIST_hyphen:
881fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
882ad68a70fSschwarze term_word(p, "-");
883fa70b73eSschwarze term_fontpop(p);
884f73abda9Skristaps break;
88549aff9f8Sschwarze case LIST_enum:
886f73abda9Skristaps (pair->ppair->ppair->count)++;
88747813146Sschwarze (void)snprintf(buf, sizeof(buf), "%d.",
888f73abda9Skristaps pair->ppair->ppair->count);
889f73abda9Skristaps term_word(p, buf);
890f73abda9Skristaps break;
891f73abda9Skristaps default:
892f73abda9Skristaps break;
893f73abda9Skristaps }
894f73abda9Skristaps
895f73abda9Skristaps /*
896f73abda9Skristaps * If we're not going to process our children, indicate so here.
897f73abda9Skristaps */
898f73abda9Skristaps
899f73abda9Skristaps switch (type) {
90049aff9f8Sschwarze case LIST_bullet:
90149aff9f8Sschwarze case LIST_item:
90249aff9f8Sschwarze case LIST_dash:
90349aff9f8Sschwarze case LIST_hyphen:
90449aff9f8Sschwarze case LIST_enum:
905d1982c71Sschwarze if (n->type == ROFFT_HEAD)
906526e306bSschwarze return 0;
907f73abda9Skristaps break;
90849aff9f8Sschwarze case LIST_column:
909d1982c71Sschwarze if (n->type == ROFFT_HEAD)
910526e306bSschwarze return 0;
911771c54bcSschwarze p->minbl = 0;
912f73abda9Skristaps break;
913f73abda9Skristaps default:
914f73abda9Skristaps break;
915f73abda9Skristaps }
916f73abda9Skristaps
917526e306bSschwarze return 1;
918f73abda9Skristaps }
919f73abda9Skristaps
920f73abda9Skristaps static void
termp_it_post(DECL_ARGS)921f73abda9Skristaps termp_it_post(DECL_ARGS)
922f73abda9Skristaps {
92350e63e03Sschwarze enum mdoc_list type;
924f73abda9Skristaps
925d1982c71Sschwarze if (n->type == ROFFT_BLOCK)
926f73abda9Skristaps return;
927f73abda9Skristaps
9288c62fbf5Sschwarze type = n->parent->parent->parent->norm->Bl.type;
929f73abda9Skristaps
930f73abda9Skristaps switch (type) {
93149aff9f8Sschwarze case LIST_item:
93249aff9f8Sschwarze case LIST_diag:
93349aff9f8Sschwarze case LIST_inset:
934d1982c71Sschwarze if (n->type == ROFFT_BODY)
935a1dd05fdSschwarze term_newln(p);
936f73abda9Skristaps break;
93749aff9f8Sschwarze case LIST_column:
938d1982c71Sschwarze if (n->type == ROFFT_BODY)
939f73abda9Skristaps term_flushln(p);
940f73abda9Skristaps break;
941f73abda9Skristaps default:
942a1dd05fdSschwarze term_newln(p);
943f73abda9Skristaps break;
944f73abda9Skristaps }
945f73abda9Skristaps
9465d7d1836Sschwarze /*
9475d7d1836Sschwarze * Now that our output is flushed, we can reset our tags. Since
9485d7d1836Sschwarze * only `It' sets these flags, we're free to assume that nobody
9495d7d1836Sschwarze * has munged them in the meanwhile.
9505d7d1836Sschwarze */
9515d7d1836Sschwarze
952771c54bcSschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND | TERMP_HANG);
953d42cab1cSschwarze p->trailspace = 0;
954f73abda9Skristaps }
955f73abda9Skristaps
956f73abda9Skristaps static int
termp_nm_pre(DECL_ARGS)957f73abda9Skristaps termp_nm_pre(DECL_ARGS)
958f73abda9Skristaps {
959a19c2534Sschwarze const char *cp;
960f73abda9Skristaps
961d1982c71Sschwarze if (n->type == ROFFT_BLOCK) {
9624b1aedf8Sschwarze p->flags |= TERMP_PREKEEP;
963526e306bSschwarze return 1;
9644b1aedf8Sschwarze }
9656e03d529Sschwarze
966d1982c71Sschwarze if (n->type == ROFFT_BODY) {
967e93ea447Sschwarze if (n->child == NULL)
968526e306bSschwarze return 0;
9696b4eabafSschwarze p->flags |= TERMP_NOSPACE;
970a19c2534Sschwarze cp = NULL;
971a19c2534Sschwarze if (n->prev->child != NULL)
972a19c2534Sschwarze cp = n->prev->child->string;
973a19c2534Sschwarze if (cp == NULL)
974a19c2534Sschwarze cp = meta->name;
975a19c2534Sschwarze if (cp == NULL)
976e93ea447Sschwarze p->tcol->offset += term_len(p, 6);
977a19c2534Sschwarze else
978e93ea447Sschwarze p->tcol->offset += term_len(p, 1) +
979e93ea447Sschwarze term_strlen(p, cp);
980526e306bSschwarze return 1;
9812ab36204Sschwarze }
9822ab36204Sschwarze
98390957cf5Sschwarze if (n->child == NULL)
984526e306bSschwarze return 0;
9852ab36204Sschwarze
986d1982c71Sschwarze if (n->type == ROFFT_HEAD)
9872fa243a3Sschwarze synopsis_pre(p, n->parent);
988fa70b73eSschwarze
989d1982c71Sschwarze if (n->type == ROFFT_HEAD &&
990e93ea447Sschwarze n->next != NULL && n->next->child != NULL) {
991eeb5fd14Sschwarze p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
992d42cab1cSschwarze p->trailspace = 1;
993e93ea447Sschwarze p->tcol->rmargin = p->tcol->offset + term_len(p, 1);
994e93ea447Sschwarze if (n->child == NULL)
995e93ea447Sschwarze p->tcol->rmargin += term_strlen(p, meta->name);
996e93ea447Sschwarze else if (n->child->type == ROFFT_TEXT) {
997e93ea447Sschwarze p->tcol->rmargin += term_strlen(p, n->child->string);
998e93ea447Sschwarze if (n->child->next != NULL)
9999e3c1948Sschwarze p->flags |= TERMP_HANG;
10009e3c1948Sschwarze } else {
1001e93ea447Sschwarze p->tcol->rmargin += term_len(p, 5);
10029e3c1948Sschwarze p->flags |= TERMP_HANG;
10039e3c1948Sschwarze }
10042ab36204Sschwarze }
10050ac7e6ecSschwarze return termp_bold_pre(p, pair, meta, n);
1006f73abda9Skristaps }
1007f73abda9Skristaps
10082ab36204Sschwarze static void
termp_nm_post(DECL_ARGS)10092ab36204Sschwarze termp_nm_post(DECL_ARGS)
10102ab36204Sschwarze {
10110ac7e6ecSschwarze switch (n->type) {
10120ac7e6ecSschwarze case ROFFT_BLOCK:
10134b1aedf8Sschwarze p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
10140ac7e6ecSschwarze break;
10150ac7e6ecSschwarze case ROFFT_HEAD:
10160ac7e6ecSschwarze if (n->next == NULL || n->next->child == NULL)
10170ac7e6ecSschwarze break;
10182ab36204Sschwarze term_flushln(p);
1019eeb5fd14Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1020d42cab1cSschwarze p->trailspace = 0;
10210ac7e6ecSschwarze break;
10220ac7e6ecSschwarze case ROFFT_BODY:
10230ac7e6ecSschwarze if (n->child != NULL)
10242ab36204Sschwarze term_flushln(p);
10250ac7e6ecSschwarze break;
10260ac7e6ecSschwarze default:
10270ac7e6ecSschwarze break;
10280ac7e6ecSschwarze }
10292ab36204Sschwarze }
10302ab36204Sschwarze
1031f73abda9Skristaps static int
termp_fl_pre(DECL_ARGS)1032f73abda9Skristaps termp_fl_pre(DECL_ARGS)
1033f73abda9Skristaps {
10347ebbefbeSschwarze struct roff_node *nn;
1035f73abda9Skristaps
1036fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
1037f73abda9Skristaps term_word(p, "\\-");
1038e974da76Sschwarze
10397ebbefbeSschwarze if (n->child != NULL ||
10407ebbefbeSschwarze ((nn = roff_node_next(n)) != NULL &&
10417ebbefbeSschwarze nn->type != ROFFT_TEXT &&
10427ebbefbeSschwarze (nn->flags & NODE_LINE) == 0))
1043b16e7ddfSschwarze p->flags |= TERMP_NOSPACE;
1044e974da76Sschwarze
1045526e306bSschwarze return 1;
1046f73abda9Skristaps }
1047f73abda9Skristaps
1048f73abda9Skristaps static int
termp__a_pre(DECL_ARGS)1049bf782e42Sschwarze termp__a_pre(DECL_ARGS)
1050bf782e42Sschwarze {
10517ebbefbeSschwarze struct roff_node *nn;
1052bf782e42Sschwarze
10537ebbefbeSschwarze if ((nn = roff_node_prev(n)) != NULL && nn->tok == MDOC__A &&
10547ebbefbeSschwarze ((nn = roff_node_next(n)) == NULL || nn->tok != MDOC__A))
1055bf782e42Sschwarze term_word(p, "and");
1056bf782e42Sschwarze
1057526e306bSschwarze return 1;
1058bf782e42Sschwarze }
1059bf782e42Sschwarze
1060bf782e42Sschwarze static int
termp_an_pre(DECL_ARGS)10618f46e13cSschwarze termp_an_pre(DECL_ARGS)
10628f46e13cSschwarze {
10638f46e13cSschwarze
10643eeea6b8Sschwarze if (n->norm->An.auth == AUTH_split) {
10658f46e13cSschwarze p->flags &= ~TERMP_NOSPLIT;
10668f46e13cSschwarze p->flags |= TERMP_SPLIT;
1067526e306bSschwarze return 0;
10683eeea6b8Sschwarze }
10693eeea6b8Sschwarze if (n->norm->An.auth == AUTH_nosplit) {
10708f46e13cSschwarze p->flags &= ~TERMP_SPLIT;
10718f46e13cSschwarze p->flags |= TERMP_NOSPLIT;
1072526e306bSschwarze return 0;
10738f46e13cSschwarze }
10748f46e13cSschwarze
10753eeea6b8Sschwarze if (p->flags & TERMP_SPLIT)
10763eeea6b8Sschwarze term_newln(p);
10773eeea6b8Sschwarze
10783eeea6b8Sschwarze if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
10793eeea6b8Sschwarze p->flags |= TERMP_SPLIT;
10803eeea6b8Sschwarze
1081526e306bSschwarze return 1;
10828f46e13cSschwarze }
10838f46e13cSschwarze
10848f46e13cSschwarze static int
termp_ns_pre(DECL_ARGS)1085f73abda9Skristaps termp_ns_pre(DECL_ARGS)
1086f73abda9Skristaps {
1087f73abda9Skristaps
1088c4b0939cSschwarze if ( ! (NODE_LINE & n->flags))
1089f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1090526e306bSschwarze return 1;
1091f73abda9Skristaps }
1092f73abda9Skristaps
1093f73abda9Skristaps static int
termp_rs_pre(DECL_ARGS)1094f73abda9Skristaps termp_rs_pre(DECL_ARGS)
1095f73abda9Skristaps {
10968c089adfSschwarze if (SEC_SEE_ALSO != n->sec)
1097526e306bSschwarze return 1;
10987ebbefbeSschwarze if (n->type == ROFFT_BLOCK && roff_node_prev(n) != NULL)
1099f73abda9Skristaps term_vspace(p);
1100526e306bSschwarze return 1;
1101f73abda9Skristaps }
1102f73abda9Skristaps
1103f73abda9Skristaps static int
termp_ex_pre(DECL_ARGS)1104f73abda9Skristaps termp_ex_pre(DECL_ARGS)
1105f73abda9Skristaps {
1106a35fc07aSschwarze term_newln(p);
11078ccddcd3Sschwarze return 1;
1108f73abda9Skristaps }
1109f73abda9Skristaps
1110f73abda9Skristaps static int
termp_nd_pre(DECL_ARGS)1111f73abda9Skristaps termp_nd_pre(DECL_ARGS)
1112f73abda9Skristaps {
1113d1982c71Sschwarze if (n->type == ROFFT_BODY)
11144602e85cSschwarze term_word(p, "\\(en");
1115526e306bSschwarze return 1;
1116f73abda9Skristaps }
1117f73abda9Skristaps
1118b16e7ddfSschwarze static int
termp_bl_pre(DECL_ARGS)1119b16e7ddfSschwarze termp_bl_pre(DECL_ARGS)
1120b16e7ddfSschwarze {
1121e053e0fdSschwarze switch (n->type) {
1122e053e0fdSschwarze case ROFFT_BLOCK:
1123e053e0fdSschwarze term_newln(p);
1124e053e0fdSschwarze return 1;
1125e053e0fdSschwarze case ROFFT_HEAD:
1126e053e0fdSschwarze return 0;
1127e053e0fdSschwarze default:
1128e053e0fdSschwarze return 1;
1129e053e0fdSschwarze }
1130b16e7ddfSschwarze }
1131b16e7ddfSschwarze
1132f73abda9Skristaps static void
termp_bl_post(DECL_ARGS)1133f73abda9Skristaps termp_bl_post(DECL_ARGS)
1134f73abda9Skristaps {
1135f7242c43Sschwarze if (n->type != ROFFT_BLOCK)
1136f7242c43Sschwarze return;
1137f73abda9Skristaps term_newln(p);
1138f7242c43Sschwarze if (n->tok != MDOC_Bl || n->norm->Bl.type != LIST_column)
1139f7242c43Sschwarze return;
1140f7242c43Sschwarze term_tab_set(p, NULL);
1141f7242c43Sschwarze term_tab_set(p, "T");
1142f7242c43Sschwarze term_tab_set(p, ".5i");
1143f73abda9Skristaps }
1144f73abda9Skristaps
1145f73abda9Skristaps static int
termp_xr_pre(DECL_ARGS)1146f73abda9Skristaps termp_xr_pre(DECL_ARGS)
1147f73abda9Skristaps {
1148a35fc07aSschwarze if (NULL == (n = n->child))
1149526e306bSschwarze return 0;
1150b4dec12aSschwarze
1151d1982c71Sschwarze assert(n->type == ROFFT_TEXT);
1152a35fc07aSschwarze term_word(p, n->string);
11538248aaccSschwarze
1154a35fc07aSschwarze if (NULL == (n = n->next))
1155526e306bSschwarze return 0;
1156a35fc07aSschwarze
1157f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1158f73abda9Skristaps term_word(p, "(");
1159a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1160a35fc07aSschwarze
1161d1982c71Sschwarze assert(n->type == ROFFT_TEXT);
1162a35fc07aSschwarze term_word(p, n->string);
1163a35fc07aSschwarze
1164a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1165f73abda9Skristaps term_word(p, ")");
11668c089adfSschwarze
1167526e306bSschwarze return 0;
1168f73abda9Skristaps }
1169f73abda9Skristaps
11709a98b8a1Sschwarze /*
11719a98b8a1Sschwarze * This decides how to assert whitespace before any of the SYNOPSIS set
11729a98b8a1Sschwarze * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
11739a98b8a1Sschwarze * macro combos).
11749a98b8a1Sschwarze */
11759a98b8a1Sschwarze static void
synopsis_pre(struct termp * p,struct roff_node * n)11767ebbefbeSschwarze synopsis_pre(struct termp *p, struct roff_node *n)
11779a98b8a1Sschwarze {
11787ebbefbeSschwarze struct roff_node *np;
11797ebbefbeSschwarze
11807ebbefbeSschwarze if ((n->flags & NODE_SYNPRETTY) == 0 ||
11817ebbefbeSschwarze (np = roff_node_prev(n)) == NULL)
11829a98b8a1Sschwarze return;
11839a98b8a1Sschwarze
11849a98b8a1Sschwarze /*
11859a98b8a1Sschwarze * If we're the second in a pair of like elements, emit our
11869a98b8a1Sschwarze * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which
11879a98b8a1Sschwarze * case we soldier on.
11889a98b8a1Sschwarze */
11897ebbefbeSschwarze if (np->tok == n->tok &&
11909a98b8a1Sschwarze MDOC_Ft != n->tok &&
11919a98b8a1Sschwarze MDOC_Fo != n->tok &&
11929a98b8a1Sschwarze MDOC_Fn != n->tok) {
11939a98b8a1Sschwarze term_newln(p);
11949a98b8a1Sschwarze return;
11959a98b8a1Sschwarze }
11969a98b8a1Sschwarze
11979a98b8a1Sschwarze /*
11989a98b8a1Sschwarze * If we're one of the SYNOPSIS set and non-like pair-wise after
11999a98b8a1Sschwarze * another (or Fn/Fo, which we've let slip through) then assert
12009a98b8a1Sschwarze * vertical space, else only newline and move on.
12019a98b8a1Sschwarze */
12027ebbefbeSschwarze switch (np->tok) {
120349aff9f8Sschwarze case MDOC_Fd:
120449aff9f8Sschwarze case MDOC_Fn:
120549aff9f8Sschwarze case MDOC_Fo:
120649aff9f8Sschwarze case MDOC_In:
120749aff9f8Sschwarze case MDOC_Vt:
12089a98b8a1Sschwarze term_vspace(p);
12099a98b8a1Sschwarze break;
121049aff9f8Sschwarze case MDOC_Ft:
12117ebbefbeSschwarze if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
12129a98b8a1Sschwarze term_vspace(p);
12139a98b8a1Sschwarze break;
12149a98b8a1Sschwarze }
12159a98b8a1Sschwarze /* FALLTHROUGH */
12169a98b8a1Sschwarze default:
12179a98b8a1Sschwarze term_newln(p);
12189a98b8a1Sschwarze break;
12199a98b8a1Sschwarze }
12209a98b8a1Sschwarze }
12219a98b8a1Sschwarze
12228521b0bcSschwarze static int
termp_vt_pre(DECL_ARGS)12238521b0bcSschwarze termp_vt_pre(DECL_ARGS)
12248521b0bcSschwarze {
12250ac7e6ecSschwarze switch (n->type) {
12260ac7e6ecSschwarze case ROFFT_ELEM:
12270ac7e6ecSschwarze return termp_ft_pre(p, pair, meta, n);
12280ac7e6ecSschwarze case ROFFT_BLOCK:
12299a98b8a1Sschwarze synopsis_pre(p, n);
1230526e306bSschwarze return 1;
12310ac7e6ecSschwarze case ROFFT_HEAD:
1232526e306bSschwarze return 0;
12330ac7e6ecSschwarze default:
1234526e306bSschwarze return termp_under_pre(p, pair, meta, n);
12358521b0bcSschwarze }
12360ac7e6ecSschwarze }
12378521b0bcSschwarze
1238f73abda9Skristaps static int
termp_bold_pre(DECL_ARGS)1239a231c1b4Sschwarze termp_bold_pre(DECL_ARGS)
1240f73abda9Skristaps {
1241fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
1242526e306bSschwarze return 1;
1243f73abda9Skristaps }
1244f73abda9Skristaps
12459a98b8a1Sschwarze static int
termp_fd_pre(DECL_ARGS)12469a98b8a1Sschwarze termp_fd_pre(DECL_ARGS)
1247f73abda9Skristaps {
12489a98b8a1Sschwarze synopsis_pre(p, n);
1249526e306bSschwarze return termp_bold_pre(p, pair, meta, n);
1250f73abda9Skristaps }
1251f73abda9Skristaps
1252d97bd30dSschwarze static void
termp_fd_post(DECL_ARGS)1253d97bd30dSschwarze termp_fd_post(DECL_ARGS)
1254d97bd30dSschwarze {
1255d97bd30dSschwarze term_newln(p);
1256d97bd30dSschwarze }
1257d97bd30dSschwarze
1258f73abda9Skristaps static int
termp_sh_pre(DECL_ARGS)1259f73abda9Skristaps termp_sh_pre(DECL_ARGS)
1260f73abda9Skristaps {
12617ebbefbeSschwarze struct roff_node *np;
12628c089adfSschwarze
12638c089adfSschwarze switch (n->type) {
1264d1982c71Sschwarze case ROFFT_BLOCK:
12657fa9bcfaSschwarze /*
12667fa9bcfaSschwarze * Vertical space before sections, except
12677fa9bcfaSschwarze * when the previous section was empty.
12687fa9bcfaSschwarze */
12697ebbefbeSschwarze if ((np = roff_node_prev(n)) == NULL ||
12707ebbefbeSschwarze np->tok != MDOC_Sh ||
12717ebbefbeSschwarze (np->body != NULL && np->body->child != NULL))
1272f73abda9Skristaps term_vspace(p);
1273e530870eSschwarze break;
1274d1982c71Sschwarze case ROFFT_HEAD:
12750ac7e6ecSschwarze return termp_bold_pre(p, pair, meta, n);
1276d1982c71Sschwarze case ROFFT_BODY:
1277e93ea447Sschwarze p->tcol->offset = term_len(p, p->defindent);
1278f7242c43Sschwarze term_tab_set(p, NULL);
1279f7242c43Sschwarze term_tab_set(p, "T");
1280f7242c43Sschwarze term_tab_set(p, ".5i");
12810ac7e6ecSschwarze if (n->sec == SEC_AUTHORS)
12828ba5d246Sschwarze p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
1283f73abda9Skristaps break;
1284f73abda9Skristaps default:
1285f73abda9Skristaps break;
1286f73abda9Skristaps }
1287526e306bSschwarze return 1;
1288f73abda9Skristaps }
1289f73abda9Skristaps
1290f73abda9Skristaps static void
termp_sh_post(DECL_ARGS)1291f73abda9Skristaps termp_sh_post(DECL_ARGS)
1292f73abda9Skristaps {
12938c089adfSschwarze switch (n->type) {
1294d1982c71Sschwarze case ROFFT_HEAD:
1295f73abda9Skristaps term_newln(p);
1296f73abda9Skristaps break;
1297d1982c71Sschwarze case ROFFT_BODY:
1298f73abda9Skristaps term_newln(p);
1299e93ea447Sschwarze p->tcol->offset = 0;
1300f73abda9Skristaps break;
1301f73abda9Skristaps default:
1302f73abda9Skristaps break;
1303f73abda9Skristaps }
1304f73abda9Skristaps }
1305f73abda9Skristaps
1306f73abda9Skristaps static void
termp_lb_post(DECL_ARGS)1307f73abda9Skristaps termp_lb_post(DECL_ARGS)
1308f73abda9Skristaps {
13090ac7e6ecSschwarze if (n->sec == SEC_LIBRARY && n->flags & NODE_LINE)
1310f73abda9Skristaps term_newln(p);
1311f73abda9Skristaps }
1312f73abda9Skristaps
1313f73abda9Skristaps static int
termp_d1_pre(DECL_ARGS)1314f73abda9Skristaps termp_d1_pre(DECL_ARGS)
1315f73abda9Skristaps {
1316d1982c71Sschwarze if (n->type != ROFFT_BLOCK)
1317526e306bSschwarze return 1;
1318f73abda9Skristaps term_newln(p);
1319e93ea447Sschwarze p->tcol->offset += term_len(p, p->defindent + 1);
1320f7242c43Sschwarze term_tab_set(p, NULL);
1321f7242c43Sschwarze term_tab_set(p, "T");
1322f7242c43Sschwarze term_tab_set(p, ".5i");
1323526e306bSschwarze return 1;
1324f73abda9Skristaps }
1325f73abda9Skristaps
1326f73abda9Skristaps static int
termp_ft_pre(DECL_ARGS)1327f73abda9Skristaps termp_ft_pre(DECL_ARGS)
1328f73abda9Skristaps {
13299a98b8a1Sschwarze synopsis_pre(p, n);
13300ac7e6ecSschwarze return termp_under_pre(p, pair, meta, n);
1331f73abda9Skristaps }
1332f73abda9Skristaps
1333f73abda9Skristaps static int
termp_fn_pre(DECL_ARGS)1334f73abda9Skristaps termp_fn_pre(DECL_ARGS)
1335f73abda9Skristaps {
13365d8feabbSschwarze size_t rmargin = 0;
1337a35fc07aSschwarze int pretty;
1338a35fc07aSschwarze
13399a98b8a1Sschwarze synopsis_pre(p, n);
13400ac7e6ecSschwarze pretty = n->flags & NODE_SYNPRETTY;
13410ac7e6ecSschwarze if ((n = n->child) == NULL)
1342526e306bSschwarze return 0;
1343a35fc07aSschwarze
1344bcb72841Sschwarze if (pretty) {
1345e93ea447Sschwarze rmargin = p->tcol->rmargin;
1346e93ea447Sschwarze p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1347eeb5fd14Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1348bcb72841Sschwarze }
1349bcb72841Sschwarze
1350d1982c71Sschwarze assert(n->type == ROFFT_TEXT);
1351fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
1352a35fc07aSschwarze term_word(p, n->string);
1353fa70b73eSschwarze term_fontpop(p);
1354f73abda9Skristaps
1355bcb72841Sschwarze if (pretty) {
1356bcb72841Sschwarze term_flushln(p);
1357eeb5fd14Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1358771c54bcSschwarze p->flags |= TERMP_NOPAD;
1359e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
1360e93ea447Sschwarze p->tcol->rmargin = rmargin;
1361bcb72841Sschwarze }
1362bcb72841Sschwarze
1363f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1364f73abda9Skristaps term_word(p, "(");
1365a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1366f73abda9Skristaps
1367a35fc07aSschwarze for (n = n->next; n; n = n->next) {
1368d1982c71Sschwarze assert(n->type == ROFFT_TEXT);
1369fa70b73eSschwarze term_fontpush(p, TERMFONT_UNDER);
13701192b926Sschwarze if (pretty)
13711192b926Sschwarze p->flags |= TERMP_NBRWORD;
1372a35fc07aSschwarze term_word(p, n->string);
1373fa70b73eSschwarze term_fontpop(p);
1374fa70b73eSschwarze
1375a35fc07aSschwarze if (n->next) {
1376a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1377f73abda9Skristaps term_word(p, ",");
1378f73abda9Skristaps }
1379a35fc07aSschwarze }
1380f73abda9Skristaps
1381a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1382f73abda9Skristaps term_word(p, ")");
1383f73abda9Skristaps
1384a35fc07aSschwarze if (pretty) {
1385a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1386f73abda9Skristaps term_word(p, ";");
1387bcb72841Sschwarze term_flushln(p);
1388a35fc07aSschwarze }
1389526e306bSschwarze return 0;
1390f73abda9Skristaps }
1391f73abda9Skristaps
1392f73abda9Skristaps static int
termp_fa_pre(DECL_ARGS)1393f73abda9Skristaps termp_fa_pre(DECL_ARGS)
1394f73abda9Skristaps {
13953a0d07afSschwarze const struct roff_node *nn;
1396f73abda9Skristaps
13970ac7e6ecSschwarze if (n->parent->tok != MDOC_Fo)
13980ac7e6ecSschwarze return termp_under_pre(p, pair, meta, n);
13990ac7e6ecSschwarze
14007ebbefbeSschwarze for (nn = n->child; nn != NULL; nn = nn->next) {
1401fa70b73eSschwarze term_fontpush(p, TERMFONT_UNDER);
1402740f368bSschwarze p->flags |= TERMP_NBRWORD;
14038c089adfSschwarze term_word(p, nn->string);
1404fa70b73eSschwarze term_fontpop(p);
14057ebbefbeSschwarze if (nn->next != NULL) {
1406a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1407f73abda9Skristaps term_word(p, ",");
1408f73abda9Skristaps }
1409a35fc07aSschwarze }
14107ebbefbeSschwarze if (n->child != NULL &&
14117ebbefbeSschwarze (nn = roff_node_next(n)) != NULL &&
14127ebbefbeSschwarze nn->tok == MDOC_Fa) {
14137ebbefbeSschwarze p->flags |= TERMP_NOSPACE;
14147ebbefbeSschwarze term_word(p, ",");
14157ebbefbeSschwarze }
1416526e306bSschwarze return 0;
1417f73abda9Skristaps }
1418f73abda9Skristaps
1419f73abda9Skristaps static int
termp_bd_pre(DECL_ARGS)1420f73abda9Skristaps termp_bd_pre(DECL_ARGS)
1421f73abda9Skristaps {
142225da2733Sschwarze int offset;
1423f73abda9Skristaps
1424d1982c71Sschwarze if (n->type == ROFFT_BLOCK) {
14254175bdabSschwarze print_bvspace(p, n, n);
1426526e306bSschwarze return 1;
1427d1982c71Sschwarze } else if (n->type == ROFFT_HEAD)
1428526e306bSschwarze return 0;
1429f73abda9Skristaps
143090d52a15Sschwarze /* Handle the -offset argument. */
143190d52a15Sschwarze
143290d52a15Sschwarze if (n->norm->Bd.offs == NULL ||
143390d52a15Sschwarze ! strcmp(n->norm->Bd.offs, "left"))
143490d52a15Sschwarze /* nothing */;
143590d52a15Sschwarze else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1436e93ea447Sschwarze p->tcol->offset += term_len(p, p->defindent + 1);
143790d52a15Sschwarze else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1438e93ea447Sschwarze p->tcol->offset += term_len(p, (p->defindent + 1) * 2);
143925da2733Sschwarze else {
144025da2733Sschwarze offset = a2width(p, n->norm->Bd.offs);
1441e93ea447Sschwarze if (offset < 0 && (size_t)(-offset) > p->tcol->offset)
1442e93ea447Sschwarze p->tcol->offset = 0;
144325da2733Sschwarze else if (offset < SHRT_MAX)
1444e93ea447Sschwarze p->tcol->offset += offset;
144525da2733Sschwarze }
1446f73abda9Skristaps
1447e1e1437fSschwarze switch (n->norm->Bd.type) {
1448e1e1437fSschwarze case DISP_literal:
1449f7242c43Sschwarze term_tab_set(p, NULL);
1450f7242c43Sschwarze term_tab_set(p, "T");
1451f7242c43Sschwarze term_tab_set(p, "8n");
1452e1e1437fSschwarze break;
1453e1e1437fSschwarze case DISP_centered:
1454e1e1437fSschwarze p->flags |= TERMP_CENTER;
1455e1e1437fSschwarze break;
1456d39b9a9cSschwarze default:
1457d39b9a9cSschwarze break;
1458d39b9a9cSschwarze }
1459e1e1437fSschwarze return 1;
1460f73abda9Skristaps }
1461f73abda9Skristaps
1462f73abda9Skristaps static void
termp_bd_post(DECL_ARGS)1463f73abda9Skristaps termp_bd_post(DECL_ARGS)
1464f73abda9Skristaps {
1465d1982c71Sschwarze if (n->type != ROFFT_BODY)
1466f73abda9Skristaps return;
1467e1e1437fSschwarze if (n->norm->Bd.type == DISP_unfilled ||
1468e1e1437fSschwarze n->norm->Bd.type == DISP_literal)
146924f1eaadSschwarze p->flags |= TERMP_BRNEVER;
14707c1ee023Sschwarze p->flags |= TERMP_NOSPACE;
1471aa4c622fSschwarze term_newln(p);
147224f1eaadSschwarze p->flags &= ~TERMP_BRNEVER;
1473e1e1437fSschwarze if (n->norm->Bd.type == DISP_centered)
1474e1e1437fSschwarze p->flags &= ~TERMP_CENTER;
1475f73abda9Skristaps }
1476f73abda9Skristaps
1477992063deSschwarze static int
termp_xx_pre(DECL_ARGS)14787bfa2b41Sschwarze termp_xx_pre(DECL_ARGS)
1479f73abda9Skristaps {
1480816c3c54Sschwarze if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
1481816c3c54Sschwarze p->flags |= TERMP_PREKEEP;
1482816c3c54Sschwarze return 1;
1483f73abda9Skristaps }
1484f73abda9Skristaps
1485816c3c54Sschwarze static void
termp_xx_post(DECL_ARGS)1486816c3c54Sschwarze termp_xx_post(DECL_ARGS)
1487816c3c54Sschwarze {
1488816c3c54Sschwarze if (n->aux == 0)
1489816c3c54Sschwarze p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1490f73abda9Skristaps }
1491f73abda9Skristaps
1492f73abda9Skristaps static void
termp_pf_post(DECL_ARGS)1493f73abda9Skristaps termp_pf_post(DECL_ARGS)
1494f73abda9Skristaps {
14957ebbefbeSschwarze if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
1496f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1497f73abda9Skristaps }
1498f73abda9Skristaps
1499f73abda9Skristaps static int
termp_ss_pre(DECL_ARGS)1500f73abda9Skristaps termp_ss_pre(DECL_ARGS)
1501f73abda9Skristaps {
15028c089adfSschwarze switch (n->type) {
1503d1982c71Sschwarze case ROFFT_BLOCK:
15047ebbefbeSschwarze if (roff_node_prev(n) == NULL)
1505f73abda9Skristaps term_newln(p);
15067ebbefbeSschwarze else
1507f73abda9Skristaps term_vspace(p);
1508f73abda9Skristaps break;
1509d1982c71Sschwarze case ROFFT_HEAD:
1510e93ea447Sschwarze p->tcol->offset = term_len(p, (p->defindent+1)/2);
15110ac7e6ecSschwarze return termp_bold_pre(p, pair, meta, n);
1512d1982c71Sschwarze case ROFFT_BODY:
1513e93ea447Sschwarze p->tcol->offset = term_len(p, p->defindent);
1514f7242c43Sschwarze term_tab_set(p, NULL);
1515f7242c43Sschwarze term_tab_set(p, "T");
1516f7242c43Sschwarze term_tab_set(p, ".5i");
15178d5916b5Sschwarze break;
1518f73abda9Skristaps default:
1519f73abda9Skristaps break;
1520f73abda9Skristaps }
1521526e306bSschwarze return 1;
1522f73abda9Skristaps }
1523f73abda9Skristaps
1524f73abda9Skristaps static void
termp_ss_post(DECL_ARGS)1525f73abda9Skristaps termp_ss_post(DECL_ARGS)
1526f73abda9Skristaps {
1527d1982c71Sschwarze if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
1528f73abda9Skristaps term_newln(p);
1529f73abda9Skristaps }
1530f73abda9Skristaps
1531f73abda9Skristaps static int
termp_in_pre(DECL_ARGS)1532f73abda9Skristaps termp_in_pre(DECL_ARGS)
1533f73abda9Skristaps {
15349a98b8a1Sschwarze synopsis_pre(p, n);
15350ac7e6ecSschwarze if (n->flags & NODE_SYNPRETTY && n->flags & NODE_LINE) {
15366093755cSschwarze term_fontpush(p, TERMFONT_BOLD);
15376093755cSschwarze term_word(p, "#include");
1538f73abda9Skristaps term_word(p, "<");
15396093755cSschwarze } else {
15406093755cSschwarze term_word(p, "<");
15416093755cSschwarze term_fontpush(p, TERMFONT_UNDER);
15426093755cSschwarze }
1543f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1544526e306bSschwarze return 1;
1545f73abda9Skristaps }
1546f73abda9Skristaps
1547f73abda9Skristaps static void
termp_in_post(DECL_ARGS)1548f73abda9Skristaps termp_in_post(DECL_ARGS)
1549f73abda9Skristaps {
15500ac7e6ecSschwarze if (n->flags & NODE_SYNPRETTY)
1551fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
1552f8628b0fSschwarze p->flags |= TERMP_NOSPACE;
1553f73abda9Skristaps term_word(p, ">");
15540ac7e6ecSschwarze if (n->flags & NODE_SYNPRETTY)
1555fa70b73eSschwarze term_fontpop(p);
1556f73abda9Skristaps }
1557f73abda9Skristaps
1558f73abda9Skristaps static int
termp_pp_pre(DECL_ARGS)15596561cb23Sschwarze termp_pp_pre(DECL_ARGS)
1560d92dc4efSschwarze {
1561d92dc4efSschwarze term_vspace(p);
1562e053e0fdSschwarze if (n->flags & NODE_ID)
1563e053e0fdSschwarze term_tag_write(n, p->line);
1564526e306bSschwarze return 0;
1565d92dc4efSschwarze }
1566d92dc4efSschwarze
1567d92dc4efSschwarze static int
termp_skip_pre(DECL_ARGS)156878bbbab4Sschwarze termp_skip_pre(DECL_ARGS)
1569551cd4a8Sschwarze {
1570526e306bSschwarze return 0;
1571551cd4a8Sschwarze }
1572551cd4a8Sschwarze
1573551cd4a8Sschwarze static int
termp_quote_pre(DECL_ARGS)15742c3450fcSschwarze termp_quote_pre(DECL_ARGS)
1575f73abda9Skristaps {
1576d1982c71Sschwarze if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1577526e306bSschwarze return 1;
15782c3450fcSschwarze
15792c3450fcSschwarze switch (n->tok) {
158049aff9f8Sschwarze case MDOC_Ao:
158149aff9f8Sschwarze case MDOC_Aq:
158230e5ee06Sschwarze term_word(p, n->child != NULL && n->child->next == NULL &&
1583ba61527cSschwarze n->child->tok == MDOC_Mt ? "<" : "\\(la");
15842c3450fcSschwarze break;
158549aff9f8Sschwarze case MDOC_Bro:
158649aff9f8Sschwarze case MDOC_Brq:
15872c3450fcSschwarze term_word(p, "{");
15882c3450fcSschwarze break;
158949aff9f8Sschwarze case MDOC_Oo:
159049aff9f8Sschwarze case MDOC_Op:
159149aff9f8Sschwarze case MDOC_Bo:
159249aff9f8Sschwarze case MDOC_Bq:
15932c3450fcSschwarze term_word(p, "[");
15942c3450fcSschwarze break;
15951aeba4ffSschwarze case MDOC__T:
15961aeba4ffSschwarze /* FALLTHROUGH */
159749aff9f8Sschwarze case MDOC_Do:
159849aff9f8Sschwarze case MDOC_Dq:
1599965f5c87Sschwarze term_word(p, "\\(lq");
16002c3450fcSschwarze break;
1601551cd4a8Sschwarze case MDOC_En:
1602551cd4a8Sschwarze if (NULL == n->norm->Es ||
1603551cd4a8Sschwarze NULL == n->norm->Es->child)
1604526e306bSschwarze return 1;
1605551cd4a8Sschwarze term_word(p, n->norm->Es->child->string);
1606551cd4a8Sschwarze break;
160749aff9f8Sschwarze case MDOC_Po:
160849aff9f8Sschwarze case MDOC_Pq:
16092c3450fcSschwarze term_word(p, "(");
16102c3450fcSschwarze break;
161149aff9f8Sschwarze case MDOC_Qo:
161249aff9f8Sschwarze case MDOC_Qq:
16132c3450fcSschwarze term_word(p, "\"");
16142c3450fcSschwarze break;
161549aff9f8Sschwarze case MDOC_Ql:
161649aff9f8Sschwarze case MDOC_So:
161749aff9f8Sschwarze case MDOC_Sq:
16183987ac13Sschwarze term_word(p, "\\(oq");
16192c3450fcSschwarze break;
16202c3450fcSschwarze default:
16212c3450fcSschwarze abort();
16222c3450fcSschwarze }
16232c3450fcSschwarze
1624f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1625526e306bSschwarze return 1;
1626f73abda9Skristaps }
1627f73abda9Skristaps
1628f73abda9Skristaps static void
termp_quote_post(DECL_ARGS)16292c3450fcSschwarze termp_quote_post(DECL_ARGS)
1630f73abda9Skristaps {
1631f73abda9Skristaps
1632d1982c71Sschwarze if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1633f73abda9Skristaps return;
16342c3450fcSschwarze
1635f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1636f73abda9Skristaps
16372c3450fcSschwarze switch (n->tok) {
163849aff9f8Sschwarze case MDOC_Ao:
163949aff9f8Sschwarze case MDOC_Aq:
164030e5ee06Sschwarze term_word(p, n->child != NULL && n->child->next == NULL &&
1641ba61527cSschwarze n->child->tok == MDOC_Mt ? ">" : "\\(ra");
16422c3450fcSschwarze break;
164349aff9f8Sschwarze case MDOC_Bro:
164449aff9f8Sschwarze case MDOC_Brq:
16452c3450fcSschwarze term_word(p, "}");
16462c3450fcSschwarze break;
164749aff9f8Sschwarze case MDOC_Oo:
164849aff9f8Sschwarze case MDOC_Op:
164949aff9f8Sschwarze case MDOC_Bo:
165049aff9f8Sschwarze case MDOC_Bq:
16512c3450fcSschwarze term_word(p, "]");
16522c3450fcSschwarze break;
16531aeba4ffSschwarze case MDOC__T:
16541aeba4ffSschwarze /* FALLTHROUGH */
165549aff9f8Sschwarze case MDOC_Do:
165649aff9f8Sschwarze case MDOC_Dq:
1657965f5c87Sschwarze term_word(p, "\\(rq");
16582c3450fcSschwarze break;
1659551cd4a8Sschwarze case MDOC_En:
166003ed6ec9Sschwarze if (n->norm->Es == NULL ||
166103ed6ec9Sschwarze n->norm->Es->child == NULL ||
166203ed6ec9Sschwarze n->norm->Es->child->next == NULL)
166303ed6ec9Sschwarze p->flags &= ~TERMP_NOSPACE;
166403ed6ec9Sschwarze else
1665551cd4a8Sschwarze term_word(p, n->norm->Es->child->next->string);
1666a1d10d08Sschwarze break;
166749aff9f8Sschwarze case MDOC_Po:
166849aff9f8Sschwarze case MDOC_Pq:
1669f73abda9Skristaps term_word(p, ")");
16702c3450fcSschwarze break;
167149aff9f8Sschwarze case MDOC_Qo:
167249aff9f8Sschwarze case MDOC_Qq:
16732c3450fcSschwarze term_word(p, "\"");
16742c3450fcSschwarze break;
167549aff9f8Sschwarze case MDOC_Ql:
167649aff9f8Sschwarze case MDOC_So:
167749aff9f8Sschwarze case MDOC_Sq:
16783987ac13Sschwarze term_word(p, "\\(cq");
16792c3450fcSschwarze break;
16802c3450fcSschwarze default:
16812c3450fcSschwarze abort();
16822c3450fcSschwarze }
1683f73abda9Skristaps }
1684f73abda9Skristaps
1685f73abda9Skristaps static int
termp_eo_pre(DECL_ARGS)168603ed6ec9Sschwarze termp_eo_pre(DECL_ARGS)
168703ed6ec9Sschwarze {
168803ed6ec9Sschwarze
1689d1982c71Sschwarze if (n->type != ROFFT_BODY)
1690526e306bSschwarze return 1;
169103ed6ec9Sschwarze
169203ed6ec9Sschwarze if (n->end == ENDBODY_NOT &&
169303ed6ec9Sschwarze n->parent->head->child == NULL &&
169403ed6ec9Sschwarze n->child != NULL &&
169503ed6ec9Sschwarze n->child->end != ENDBODY_NOT)
169603ed6ec9Sschwarze term_word(p, "\\&");
169703ed6ec9Sschwarze else if (n->end != ENDBODY_NOT ? n->child != NULL :
1698c69b5c37Sschwarze n->parent->head->child != NULL && (n->child != NULL ||
1699c69b5c37Sschwarze (n->parent->tail != NULL && n->parent->tail->child != NULL)))
170003ed6ec9Sschwarze p->flags |= TERMP_NOSPACE;
170103ed6ec9Sschwarze
1702526e306bSschwarze return 1;
170303ed6ec9Sschwarze }
170403ed6ec9Sschwarze
170503ed6ec9Sschwarze static void
termp_eo_post(DECL_ARGS)170603ed6ec9Sschwarze termp_eo_post(DECL_ARGS)
170703ed6ec9Sschwarze {
170803ed6ec9Sschwarze int body, tail;
170903ed6ec9Sschwarze
1710d1982c71Sschwarze if (n->type != ROFFT_BODY)
171103ed6ec9Sschwarze return;
171203ed6ec9Sschwarze
171303ed6ec9Sschwarze if (n->end != ENDBODY_NOT) {
171403ed6ec9Sschwarze p->flags &= ~TERMP_NOSPACE;
171503ed6ec9Sschwarze return;
171603ed6ec9Sschwarze }
171703ed6ec9Sschwarze
171803ed6ec9Sschwarze body = n->child != NULL || n->parent->head->child != NULL;
171903ed6ec9Sschwarze tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
172003ed6ec9Sschwarze
172103ed6ec9Sschwarze if (body && tail)
172203ed6ec9Sschwarze p->flags |= TERMP_NOSPACE;
172303ed6ec9Sschwarze else if ( ! (body || tail))
172403ed6ec9Sschwarze term_word(p, "\\&");
172503ed6ec9Sschwarze else if ( ! tail)
172603ed6ec9Sschwarze p->flags &= ~TERMP_NOSPACE;
172703ed6ec9Sschwarze }
172803ed6ec9Sschwarze
172903ed6ec9Sschwarze static int
termp_fo_pre(DECL_ARGS)1730f73abda9Skristaps termp_fo_pre(DECL_ARGS)
1731f73abda9Skristaps {
17320ac7e6ecSschwarze size_t rmargin;
1733740f368bSschwarze
17340ac7e6ecSschwarze switch (n->type) {
17350ac7e6ecSschwarze case ROFFT_BLOCK:
17369a98b8a1Sschwarze synopsis_pre(p, n);
1737526e306bSschwarze return 1;
17380ac7e6ecSschwarze case ROFFT_BODY:
1739e93ea447Sschwarze rmargin = p->tcol->rmargin;
17400ac7e6ecSschwarze if (n->flags & NODE_SYNPRETTY) {
1741e93ea447Sschwarze p->tcol->rmargin = p->tcol->offset + term_len(p, 4);
1742eeb5fd14Sschwarze p->flags |= TERMP_NOBREAK | TERMP_BRIND |
1743eeb5fd14Sschwarze TERMP_HANG;
1744740f368bSschwarze }
1745997e4942Sschwarze p->flags |= TERMP_NOSPACE;
1746f73abda9Skristaps term_word(p, "(");
1747a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
17480ac7e6ecSschwarze if (n->flags & NODE_SYNPRETTY) {
1749740f368bSschwarze term_flushln(p);
1750eeb5fd14Sschwarze p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
1751eeb5fd14Sschwarze TERMP_HANG);
1752771c54bcSschwarze p->flags |= TERMP_NOPAD;
1753e93ea447Sschwarze p->tcol->offset = p->tcol->rmargin;
1754e93ea447Sschwarze p->tcol->rmargin = rmargin;
1755740f368bSschwarze }
1756526e306bSschwarze return 1;
17570ac7e6ecSschwarze default:
17580ac7e6ecSschwarze return termp_bold_pre(p, pair, meta, n);
1759f73abda9Skristaps }
1760f73abda9Skristaps }
1761f73abda9Skristaps
1762f73abda9Skristaps static void
termp_fo_post(DECL_ARGS)1763f73abda9Skristaps termp_fo_post(DECL_ARGS)
1764f73abda9Skristaps {
1765d1982c71Sschwarze if (n->type != ROFFT_BODY)
17669a98b8a1Sschwarze return;
17679a98b8a1Sschwarze
1768a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1769f73abda9Skristaps term_word(p, ")");
17709a98b8a1Sschwarze
17710ac7e6ecSschwarze if (n->flags & NODE_SYNPRETTY) {
1772a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1773f73abda9Skristaps term_word(p, ";");
1774740f368bSschwarze term_flushln(p);
17756093755cSschwarze }
1776a35fc07aSschwarze }
1777f73abda9Skristaps
1778f73abda9Skristaps static int
termp_bf_pre(DECL_ARGS)1779f73abda9Skristaps termp_bf_pre(DECL_ARGS)
1780f73abda9Skristaps {
17810ac7e6ecSschwarze switch (n->type) {
17820ac7e6ecSschwarze case ROFFT_HEAD:
1783526e306bSschwarze return 0;
17840ac7e6ecSschwarze case ROFFT_BODY:
17850ac7e6ecSschwarze break;
17860ac7e6ecSschwarze default:
1787526e306bSschwarze return 1;
17880ac7e6ecSschwarze }
17890ac7e6ecSschwarze switch (n->norm->Bf.font) {
17900ac7e6ecSschwarze case FONT_Em:
17910ac7e6ecSschwarze return termp_under_pre(p, pair, meta, n);
17920ac7e6ecSschwarze case FONT_Sy:
17930ac7e6ecSschwarze return termp_bold_pre(p, pair, meta, n);
17940ac7e6ecSschwarze default:
17950ac7e6ecSschwarze return termp_li_pre(p, pair, meta, n);
17960ac7e6ecSschwarze }
1797f73abda9Skristaps }
1798f73abda9Skristaps
1799f73abda9Skristaps static int
termp_sm_pre(DECL_ARGS)1800f73abda9Skristaps termp_sm_pre(DECL_ARGS)
1801f73abda9Skristaps {
18020ac7e6ecSschwarze if (n->child == NULL)
1803f9e7bf99Sschwarze p->flags ^= TERMP_NONOSPACE;
18040ac7e6ecSschwarze else if (strcmp(n->child->string, "on") == 0)
1805f73abda9Skristaps p->flags &= ~TERMP_NONOSPACE;
1806f9e7bf99Sschwarze else
1807f73abda9Skristaps p->flags |= TERMP_NONOSPACE;
1808f73abda9Skristaps
1809f9e7bf99Sschwarze if (p->col && ! (TERMP_NONOSPACE & p->flags))
1810f9e7bf99Sschwarze p->flags &= ~TERMP_NOSPACE;
1811f9e7bf99Sschwarze
1812526e306bSschwarze return 0;
1813f73abda9Skristaps }
1814f73abda9Skristaps
1815f73abda9Skristaps static int
termp_ap_pre(DECL_ARGS)1816f73abda9Skristaps termp_ap_pre(DECL_ARGS)
1817f73abda9Skristaps {
1818f73abda9Skristaps p->flags |= TERMP_NOSPACE;
18192c3450fcSschwarze term_word(p, "'");
1820f73abda9Skristaps p->flags |= TERMP_NOSPACE;
1821526e306bSschwarze return 1;
1822f73abda9Skristaps }
1823f73abda9Skristaps
1824f73abda9Skristaps static void
termp____post(DECL_ARGS)1825f73abda9Skristaps termp____post(DECL_ARGS)
1826f73abda9Skristaps {
18277ebbefbeSschwarze struct roff_node *nn;
1828f73abda9Skristaps
1829bf782e42Sschwarze /*
1830bf782e42Sschwarze * Handle lists of authors. In general, print each followed by
1831bf782e42Sschwarze * a comma. Don't print the comma if there are only two
1832bf782e42Sschwarze * authors.
1833bf782e42Sschwarze */
18347ebbefbeSschwarze if (n->tok == MDOC__A &&
18357ebbefbeSschwarze (nn = roff_node_next(n)) != NULL && nn->tok == MDOC__A &&
18367ebbefbeSschwarze ((nn = roff_node_next(nn)) == NULL || nn->tok != MDOC__A) &&
18377ebbefbeSschwarze ((nn = roff_node_prev(n)) == NULL || nn->tok != MDOC__A))
1838bf782e42Sschwarze return;
1839bf782e42Sschwarze
1840b822ca0dSschwarze /* TODO: %U. */
1841b822ca0dSschwarze
18427ebbefbeSschwarze if (n->parent == NULL || n->parent->tok != MDOC_Rs)
1843d967b250Sschwarze return;
1844d967b250Sschwarze
1845a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
18467ebbefbeSschwarze if (roff_node_next(n) == NULL) {
1847d967b250Sschwarze term_word(p, ".");
1848d967b250Sschwarze p->flags |= TERMP_SENTENCE;
1849d967b250Sschwarze } else
1850d967b250Sschwarze term_word(p, ",");
1851f73abda9Skristaps }
1852f73abda9Skristaps
1853f73abda9Skristaps static int
termp_li_pre(DECL_ARGS)1854fa70b73eSschwarze termp_li_pre(DECL_ARGS)
1855fa70b73eSschwarze {
1856fa70b73eSschwarze term_fontpush(p, TERMFONT_NONE);
1857526e306bSschwarze return 1;
1858fa70b73eSschwarze }
1859fa70b73eSschwarze
1860fa70b73eSschwarze static int
termp_lk_pre(DECL_ARGS)1861f73abda9Skristaps termp_lk_pre(DECL_ARGS)
1862f73abda9Skristaps {
1863eb4d0f30Sschwarze const struct roff_node *link, *descr, *punct;
1864f73abda9Skristaps
186596aebfb3Sschwarze if ((link = n->child) == NULL)
1866526e306bSschwarze return 0;
1867cfd12f23Sschwarze
1868eb4d0f30Sschwarze /* Find beginning of trailing punctuation. */
1869eb4d0f30Sschwarze punct = n->last;
1870eb4d0f30Sschwarze while (punct != link && punct->flags & NODE_DELIMC)
1871eb4d0f30Sschwarze punct = punct->prev;
1872eb4d0f30Sschwarze punct = punct->next;
1873eb4d0f30Sschwarze
187496aebfb3Sschwarze /* Link text. */
1875eb4d0f30Sschwarze if ((descr = link->next) != NULL && descr != punct) {
1876fa70b73eSschwarze term_fontpush(p, TERMFONT_UNDER);
1877eb4d0f30Sschwarze while (descr != punct) {
1878eb4d0f30Sschwarze if (descr->flags & (NODE_DELIMC | NODE_DELIMO))
1879eb4d0f30Sschwarze p->flags |= TERMP_NOSPACE;
1880cfd12f23Sschwarze term_word(p, descr->string);
1881cfd12f23Sschwarze descr = descr->next;
1882cfd12f23Sschwarze }
1883646f82abSschwarze term_fontpop(p);
1884a35fc07aSschwarze p->flags |= TERMP_NOSPACE;
1885f73abda9Skristaps term_word(p, ":");
1886cfd12f23Sschwarze }
1887f73abda9Skristaps
188896aebfb3Sschwarze /* Link target. */
1889fa70b73eSschwarze term_fontpush(p, TERMFONT_BOLD);
1890cfd12f23Sschwarze term_word(p, link->string);
1891fa70b73eSschwarze term_fontpop(p);
1892f73abda9Skristaps
189396aebfb3Sschwarze /* Trailing punctuation. */
1894eb4d0f30Sschwarze while (punct != NULL) {
189596aebfb3Sschwarze p->flags |= TERMP_NOSPACE;
1896eb4d0f30Sschwarze term_word(p, punct->string);
1897eb4d0f30Sschwarze punct = punct->next;
189896aebfb3Sschwarze }
1899526e306bSschwarze return 0;
1900f73abda9Skristaps }
1901f73abda9Skristaps
1902f73abda9Skristaps static int
termp_bk_pre(DECL_ARGS)19031eead9a6Sschwarze termp_bk_pre(DECL_ARGS)
19041eead9a6Sschwarze {
1905c861dd6fSschwarze switch (n->type) {
1906d1982c71Sschwarze case ROFFT_BLOCK:
1907769ee804Sschwarze break;
1908d1982c71Sschwarze case ROFFT_HEAD:
1909526e306bSschwarze return 0;
1910d1982c71Sschwarze case ROFFT_BODY:
191130e5ee06Sschwarze if (n->parent->args != NULL || n->prev->child == NULL)
19121eead9a6Sschwarze p->flags |= TERMP_PREKEEP;
1913769ee804Sschwarze break;
1914c861dd6fSschwarze default:
1915c861dd6fSschwarze abort();
1916c861dd6fSschwarze }
1917526e306bSschwarze return 1;
19181eead9a6Sschwarze }
19191eead9a6Sschwarze
19201eead9a6Sschwarze static void
termp_bk_post(DECL_ARGS)19211eead9a6Sschwarze termp_bk_post(DECL_ARGS)
19221eead9a6Sschwarze {
1923d1982c71Sschwarze if (n->type == ROFFT_BODY)
19241eead9a6Sschwarze p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
19251eead9a6Sschwarze }
19261eead9a6Sschwarze
19270ac7e6ecSschwarze /*
19280ac7e6ecSschwarze * If we are in an `Rs' and there is a journal present,
19290ac7e6ecSschwarze * then quote us instead of underlining us (for disambiguation).
19300ac7e6ecSschwarze */
19316f207799Sschwarze static void
termp__t_post(DECL_ARGS)19326f207799Sschwarze termp__t_post(DECL_ARGS)
19336f207799Sschwarze {
19340ac7e6ecSschwarze if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
19355d273f35Sschwarze n->parent->norm->Rs.quote_T)
19367ead8a4eSschwarze termp_quote_post(p, pair, meta, n);
19377ead8a4eSschwarze termp____post(p, pair, meta, n);
19386f207799Sschwarze }
19396f207799Sschwarze
19406f207799Sschwarze static int
termp__t_pre(DECL_ARGS)19416f207799Sschwarze termp__t_pre(DECL_ARGS)
19426f207799Sschwarze {
19430ac7e6ecSschwarze if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
19445d273f35Sschwarze n->parent->norm->Rs.quote_T)
1945526e306bSschwarze return termp_quote_pre(p, pair, meta, n);
19460ac7e6ecSschwarze else
19470ac7e6ecSschwarze return termp_under_pre(p, pair, meta, n);
19486f207799Sschwarze }
19496f207799Sschwarze
19501eead9a6Sschwarze static int
termp_under_pre(DECL_ARGS)1951a231c1b4Sschwarze termp_under_pre(DECL_ARGS)
1952f73abda9Skristaps {
1953fa70b73eSschwarze term_fontpush(p, TERMFONT_UNDER);
1954526e306bSschwarze return 1;
1955f73abda9Skristaps }
1956c0a657b3Sschwarze
1957c0a657b3Sschwarze static int
termp_abort_pre(DECL_ARGS)19587c539ecbSschwarze termp_abort_pre(DECL_ARGS)
19597c539ecbSschwarze {
19607c539ecbSschwarze abort();
19617c539ecbSschwarze }
1962