Commit | Line | Data |
---|---|---|
070c62a6 | 1 | /* $Id: mdoc_term.c,v 1.275 2014/08/06 15:09:05 schwarze Exp $ */ |
80387638 | 2 | /* |
60e1e752 | 3 | * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> |
070c62a6 | 4 | * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze <schwarze@openbsd.org> |
7888c61d | 5 | * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> |
80387638 SW |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any | |
8 | * purpose with or without fee is hereby granted, provided that the above | |
9 | * copyright notice and this permission notice appear in all copies. | |
10 | * | |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
18 | */ | |
19 | #ifdef HAVE_CONFIG_H | |
20 | #include "config.h" | |
21 | #endif | |
22 | ||
23 | #include <sys/types.h> | |
24 | ||
25 | #include <assert.h> | |
26 | #include <ctype.h> | |
27 | #include <stdint.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <string.h> | |
31 | ||
32 | #include "mandoc.h" | |
070c62a6 | 33 | #include "mandoc_aux.h" |
80387638 SW |
34 | #include "out.h" |
35 | #include "term.h" | |
36 | #include "mdoc.h" | |
80387638 SW |
37 | #include "main.h" |
38 | ||
80387638 SW |
39 | struct termpair { |
40 | struct termpair *ppair; | |
41 | int count; | |
42 | }; | |
43 | ||
44 | #define DECL_ARGS struct termp *p, \ | |
45 | struct termpair *pair, \ | |
070c62a6 | 46 | const struct mdoc_meta *meta, \ |
f88b6c16 | 47 | struct mdoc_node *n |
80387638 SW |
48 | |
49 | struct termact { | |
50 | int (*pre)(DECL_ARGS); | |
51 | void (*post)(DECL_ARGS); | |
52 | }; | |
53 | ||
54 | static size_t a2width(const struct termp *, const char *); | |
55 | static size_t a2height(const struct termp *, const char *); | |
56 | static size_t a2offs(const struct termp *, const char *); | |
57 | ||
58 | static void print_bvspace(struct termp *, | |
59 | const struct mdoc_node *, | |
60 | const struct mdoc_node *); | |
070c62a6 | 61 | static void print_mdoc_node(DECL_ARGS); |
80387638 SW |
62 | static void print_mdoc_nodelist(DECL_ARGS); |
63 | static void print_mdoc_head(struct termp *, const void *); | |
64 | static void print_mdoc_foot(struct termp *, const void *); | |
070c62a6 | 65 | static void synopsis_pre(struct termp *, |
80387638 SW |
66 | const struct mdoc_node *); |
67 | ||
68 | static void termp____post(DECL_ARGS); | |
69 | static void termp__t_post(DECL_ARGS); | |
70 | static void termp_an_post(DECL_ARGS); | |
71 | static void termp_bd_post(DECL_ARGS); | |
72 | static void termp_bk_post(DECL_ARGS); | |
73 | static void termp_bl_post(DECL_ARGS); | |
f88b6c16 | 74 | static void termp_fd_post(DECL_ARGS); |
80387638 SW |
75 | static void termp_fo_post(DECL_ARGS); |
76 | static void termp_in_post(DECL_ARGS); | |
77 | static void termp_it_post(DECL_ARGS); | |
78 | static void termp_lb_post(DECL_ARGS); | |
79 | static void termp_nm_post(DECL_ARGS); | |
80 | static void termp_pf_post(DECL_ARGS); | |
81 | static void termp_quote_post(DECL_ARGS); | |
82 | static void termp_sh_post(DECL_ARGS); | |
83 | static void termp_ss_post(DECL_ARGS); | |
84 | ||
85 | static int termp__a_pre(DECL_ARGS); | |
86 | static int termp__t_pre(DECL_ARGS); | |
87 | static int termp_an_pre(DECL_ARGS); | |
88 | static int termp_ap_pre(DECL_ARGS); | |
89 | static int termp_bd_pre(DECL_ARGS); | |
90 | static int termp_bf_pre(DECL_ARGS); | |
91 | static int termp_bk_pre(DECL_ARGS); | |
92 | static int termp_bl_pre(DECL_ARGS); | |
93 | static int termp_bold_pre(DECL_ARGS); | |
94 | static int termp_bt_pre(DECL_ARGS); | |
60e1e752 | 95 | static int termp_bx_pre(DECL_ARGS); |
80387638 SW |
96 | static int termp_cd_pre(DECL_ARGS); |
97 | static int termp_d1_pre(DECL_ARGS); | |
070c62a6 | 98 | static int termp_es_pre(DECL_ARGS); |
80387638 SW |
99 | static int termp_ex_pre(DECL_ARGS); |
100 | static int termp_fa_pre(DECL_ARGS); | |
101 | static int termp_fd_pre(DECL_ARGS); | |
102 | static int termp_fl_pre(DECL_ARGS); | |
103 | static int termp_fn_pre(DECL_ARGS); | |
104 | static int termp_fo_pre(DECL_ARGS); | |
105 | static int termp_ft_pre(DECL_ARGS); | |
80387638 SW |
106 | static int termp_in_pre(DECL_ARGS); |
107 | static int termp_it_pre(DECL_ARGS); | |
108 | static int termp_li_pre(DECL_ARGS); | |
070c62a6 | 109 | static int termp_ll_pre(DECL_ARGS); |
80387638 SW |
110 | static int termp_lk_pre(DECL_ARGS); |
111 | static int termp_nd_pre(DECL_ARGS); | |
112 | static int termp_nm_pre(DECL_ARGS); | |
113 | static int termp_ns_pre(DECL_ARGS); | |
114 | static int termp_quote_pre(DECL_ARGS); | |
115 | static int termp_rs_pre(DECL_ARGS); | |
116 | static int termp_rv_pre(DECL_ARGS); | |
117 | static int termp_sh_pre(DECL_ARGS); | |
118 | static int termp_sm_pre(DECL_ARGS); | |
119 | static int termp_sp_pre(DECL_ARGS); | |
120 | static int termp_ss_pre(DECL_ARGS); | |
121 | static int termp_under_pre(DECL_ARGS); | |
122 | static int termp_ud_pre(DECL_ARGS); | |
123 | static int termp_vt_pre(DECL_ARGS); | |
124 | static int termp_xr_pre(DECL_ARGS); | |
125 | static int termp_xx_pre(DECL_ARGS); | |
126 | ||
127 | static const struct termact termacts[MDOC_MAX] = { | |
128 | { termp_ap_pre, NULL }, /* Ap */ | |
129 | { NULL, NULL }, /* Dd */ | |
130 | { NULL, NULL }, /* Dt */ | |
131 | { NULL, NULL }, /* Os */ | |
132 | { termp_sh_pre, termp_sh_post }, /* Sh */ | |
070c62a6 FF |
133 | { termp_ss_pre, termp_ss_post }, /* Ss */ |
134 | { termp_sp_pre, NULL }, /* Pp */ | |
f88b6c16 FF |
135 | { termp_d1_pre, termp_bl_post }, /* D1 */ |
136 | { termp_d1_pre, termp_bl_post }, /* Dl */ | |
80387638 SW |
137 | { termp_bd_pre, termp_bd_post }, /* Bd */ |
138 | { NULL, NULL }, /* Ed */ | |
139 | { termp_bl_pre, termp_bl_post }, /* Bl */ | |
140 | { NULL, NULL }, /* El */ | |
141 | { termp_it_pre, termp_it_post }, /* It */ | |
070c62a6 | 142 | { termp_under_pre, NULL }, /* Ad */ |
80387638 SW |
143 | { termp_an_pre, termp_an_post }, /* An */ |
144 | { termp_under_pre, NULL }, /* Ar */ | |
145 | { termp_cd_pre, NULL }, /* Cd */ | |
146 | { termp_bold_pre, NULL }, /* Cm */ | |
070c62a6 FF |
147 | { NULL, NULL }, /* Dv */ |
148 | { NULL, NULL }, /* Er */ | |
149 | { NULL, NULL }, /* Ev */ | |
80387638 | 150 | { termp_ex_pre, NULL }, /* Ex */ |
070c62a6 FF |
151 | { termp_fa_pre, NULL }, /* Fa */ |
152 | { termp_fd_pre, termp_fd_post }, /* Fd */ | |
80387638 | 153 | { termp_fl_pre, NULL }, /* Fl */ |
070c62a6 FF |
154 | { termp_fn_pre, NULL }, /* Fn */ |
155 | { termp_ft_pre, NULL }, /* Ft */ | |
156 | { termp_bold_pre, NULL }, /* Ic */ | |
157 | { termp_in_pre, termp_in_post }, /* In */ | |
80387638 | 158 | { termp_li_pre, NULL }, /* Li */ |
070c62a6 FF |
159 | { termp_nd_pre, NULL }, /* Nd */ |
160 | { termp_nm_pre, termp_nm_post }, /* Nm */ | |
80387638 | 161 | { termp_quote_pre, termp_quote_post }, /* Op */ |
070c62a6 | 162 | { termp_ft_pre, NULL }, /* Ot */ |
80387638 SW |
163 | { termp_under_pre, NULL }, /* Pa */ |
164 | { termp_rv_pre, NULL }, /* Rv */ | |
070c62a6 | 165 | { NULL, NULL }, /* St */ |
80387638 SW |
166 | { termp_under_pre, NULL }, /* Va */ |
167 | { termp_vt_pre, NULL }, /* Vt */ | |
168 | { termp_xr_pre, NULL }, /* Xr */ | |
169 | { termp__a_pre, termp____post }, /* %A */ | |
170 | { termp_under_pre, termp____post }, /* %B */ | |
171 | { NULL, termp____post }, /* %D */ | |
172 | { termp_under_pre, termp____post }, /* %I */ | |
173 | { termp_under_pre, termp____post }, /* %J */ | |
174 | { NULL, termp____post }, /* %N */ | |
175 | { NULL, termp____post }, /* %O */ | |
176 | { NULL, termp____post }, /* %P */ | |
177 | { NULL, termp____post }, /* %R */ | |
178 | { termp__t_pre, termp__t_post }, /* %T */ | |
179 | { NULL, termp____post }, /* %V */ | |
180 | { NULL, NULL }, /* Ac */ | |
181 | { termp_quote_pre, termp_quote_post }, /* Ao */ | |
182 | { termp_quote_pre, termp_quote_post }, /* Aq */ | |
183 | { NULL, NULL }, /* At */ | |
184 | { NULL, NULL }, /* Bc */ | |
070c62a6 | 185 | { termp_bf_pre, NULL }, /* Bf */ |
80387638 SW |
186 | { termp_quote_pre, termp_quote_post }, /* Bo */ |
187 | { termp_quote_pre, termp_quote_post }, /* Bq */ | |
188 | { termp_xx_pre, NULL }, /* Bsx */ | |
60e1e752 | 189 | { termp_bx_pre, NULL }, /* Bx */ |
80387638 SW |
190 | { NULL, NULL }, /* Db */ |
191 | { NULL, NULL }, /* Dc */ | |
192 | { termp_quote_pre, termp_quote_post }, /* Do */ | |
193 | { termp_quote_pre, termp_quote_post }, /* Dq */ | |
194 | { NULL, NULL }, /* Ec */ /* FIXME: no space */ | |
195 | { NULL, NULL }, /* Ef */ | |
070c62a6 | 196 | { termp_under_pre, NULL }, /* Em */ |
36342e81 | 197 | { termp_quote_pre, termp_quote_post }, /* Eo */ |
80387638 SW |
198 | { termp_xx_pre, NULL }, /* Fx */ |
199 | { termp_bold_pre, NULL }, /* Ms */ | |
7888c61d | 200 | { NULL, NULL }, /* No */ |
80387638 SW |
201 | { termp_ns_pre, NULL }, /* Ns */ |
202 | { termp_xx_pre, NULL }, /* Nx */ | |
203 | { termp_xx_pre, NULL }, /* Ox */ | |
204 | { NULL, NULL }, /* Pc */ | |
7888c61d | 205 | { NULL, termp_pf_post }, /* Pf */ |
80387638 SW |
206 | { termp_quote_pre, termp_quote_post }, /* Po */ |
207 | { termp_quote_pre, termp_quote_post }, /* Pq */ | |
208 | { NULL, NULL }, /* Qc */ | |
209 | { termp_quote_pre, termp_quote_post }, /* Ql */ | |
210 | { termp_quote_pre, termp_quote_post }, /* Qo */ | |
211 | { termp_quote_pre, termp_quote_post }, /* Qq */ | |
212 | { NULL, NULL }, /* Re */ | |
213 | { termp_rs_pre, NULL }, /* Rs */ | |
214 | { NULL, NULL }, /* Sc */ | |
215 | { termp_quote_pre, termp_quote_post }, /* So */ | |
216 | { termp_quote_pre, termp_quote_post }, /* Sq */ | |
217 | { termp_sm_pre, NULL }, /* Sm */ | |
218 | { termp_under_pre, NULL }, /* Sx */ | |
219 | { termp_bold_pre, NULL }, /* Sy */ | |
220 | { NULL, NULL }, /* Tn */ | |
221 | { termp_xx_pre, NULL }, /* Ux */ | |
222 | { NULL, NULL }, /* Xc */ | |
223 | { NULL, NULL }, /* Xo */ | |
070c62a6 FF |
224 | { termp_fo_pre, termp_fo_post }, /* Fo */ |
225 | { NULL, NULL }, /* Fc */ | |
80387638 SW |
226 | { termp_quote_pre, termp_quote_post }, /* Oo */ |
227 | { NULL, NULL }, /* Oc */ | |
228 | { termp_bk_pre, termp_bk_post }, /* Bk */ | |
229 | { NULL, NULL }, /* Ek */ | |
230 | { termp_bt_pre, NULL }, /* Bt */ | |
231 | { NULL, NULL }, /* Hf */ | |
070c62a6 | 232 | { termp_under_pre, NULL }, /* Fr */ |
80387638 SW |
233 | { termp_ud_pre, NULL }, /* Ud */ |
234 | { NULL, termp_lb_post }, /* Lb */ | |
070c62a6 FF |
235 | { termp_sp_pre, NULL }, /* Lp */ |
236 | { termp_lk_pre, NULL }, /* Lk */ | |
237 | { termp_under_pre, NULL }, /* Mt */ | |
238 | { termp_quote_pre, termp_quote_post }, /* Brq */ | |
239 | { termp_quote_pre, termp_quote_post }, /* Bro */ | |
240 | { NULL, NULL }, /* Brc */ | |
241 | { NULL, termp____post }, /* %C */ | |
242 | { termp_es_pre, NULL }, /* Es */ | |
243 | { termp_quote_pre, termp_quote_post }, /* En */ | |
244 | { termp_xx_pre, NULL }, /* Dx */ | |
245 | { NULL, termp____post }, /* %Q */ | |
80387638 | 246 | { termp_sp_pre, NULL }, /* br */ |
070c62a6 FF |
247 | { termp_sp_pre, NULL }, /* sp */ |
248 | { NULL, termp____post }, /* %U */ | |
249 | { NULL, NULL }, /* Ta */ | |
250 | { termp_ll_pre, NULL }, /* ll */ | |
80387638 SW |
251 | }; |
252 | ||
253 | ||
254 | void | |
255 | terminal_mdoc(void *arg, const struct mdoc *mdoc) | |
256 | { | |
257 | const struct mdoc_node *n; | |
f88b6c16 | 258 | const struct mdoc_meta *meta; |
80387638 SW |
259 | struct termp *p; |
260 | ||
261 | p = (struct termp *)arg; | |
262 | ||
36342e81 SW |
263 | if (0 == p->defindent) |
264 | p->defindent = 5; | |
265 | ||
80387638 SW |
266 | p->overstep = 0; |
267 | p->maxrmargin = p->defrmargin; | |
268 | p->tabwidth = term_len(p, 5); | |
269 | ||
270 | if (NULL == p->symtab) | |
a4c7eb57 | 271 | p->symtab = mchars_alloc(); |
80387638 SW |
272 | |
273 | n = mdoc_node(mdoc); | |
f88b6c16 | 274 | meta = mdoc_meta(mdoc); |
80387638 | 275 | |
f88b6c16 | 276 | term_begin(p, print_mdoc_head, print_mdoc_foot, meta); |
80387638 | 277 | |
070c62a6 FF |
278 | if (n->child) { |
279 | if (MDOC_Sh != n->child->tok) | |
280 | term_vspace(p); | |
f88b6c16 | 281 | print_mdoc_nodelist(p, NULL, meta, n->child); |
070c62a6 | 282 | } |
80387638 SW |
283 | |
284 | term_end(p); | |
285 | } | |
286 | ||
80387638 SW |
287 | static void |
288 | print_mdoc_nodelist(DECL_ARGS) | |
289 | { | |
290 | ||
f88b6c16 | 291 | print_mdoc_node(p, pair, meta, n); |
80387638 | 292 | if (n->next) |
f88b6c16 | 293 | print_mdoc_nodelist(p, pair, meta, n->next); |
80387638 SW |
294 | } |
295 | ||
80387638 SW |
296 | static void |
297 | print_mdoc_node(DECL_ARGS) | |
298 | { | |
299 | int chld; | |
80387638 SW |
300 | struct termpair npair; |
301 | size_t offset, rmargin; | |
302 | ||
303 | chld = 1; | |
304 | offset = p->offset; | |
305 | rmargin = p->rmargin; | |
f88b6c16 | 306 | n->prev_font = term_fontq(p); |
80387638 SW |
307 | |
308 | memset(&npair, 0, sizeof(struct termpair)); | |
309 | npair.ppair = pair; | |
80387638 SW |
310 | |
311 | /* | |
312 | * Keeps only work until the end of a line. If a keep was | |
313 | * invoked in a prior line, revert it to PREKEEP. | |
80387638 SW |
314 | */ |
315 | ||
7888c61d FF |
316 | if (TERMP_KEEP & p->flags) { |
317 | if (n->prev ? (n->prev->lastline != n->line) : | |
f88b6c16 | 318 | (n->parent && n->parent->line != n->line)) { |
80387638 SW |
319 | p->flags &= ~TERMP_KEEP; |
320 | p->flags |= TERMP_PREKEEP; | |
80387638 SW |
321 | } |
322 | } | |
323 | ||
60e1e752 SW |
324 | /* |
325 | * After the keep flags have been set up, we may now | |
326 | * produce output. Note that some pre-handlers do so. | |
327 | */ | |
328 | ||
329 | switch (n->type) { | |
070c62a6 | 330 | case MDOC_TEXT: |
60e1e752 SW |
331 | if (' ' == *n->string && MDOC_LINE & n->flags) |
332 | term_newln(p); | |
333 | if (MDOC_DELIMC & n->flags) | |
334 | p->flags |= TERMP_NOSPACE; | |
335 | term_word(p, n->string); | |
336 | if (MDOC_DELIMO & n->flags) | |
337 | p->flags |= TERMP_NOSPACE; | |
338 | break; | |
070c62a6 | 339 | case MDOC_EQN: |
36342e81 | 340 | term_eqn(p, n->eqn); |
60e1e752 | 341 | break; |
070c62a6 | 342 | case MDOC_TBL: |
60e1e752 SW |
343 | term_tbl(p, n->span); |
344 | break; | |
345 | default: | |
346 | if (termacts[n->tok].pre && ENDBODY_NOT == n->end) | |
347 | chld = (*termacts[n->tok].pre) | |
f88b6c16 | 348 | (p, &npair, meta, n); |
60e1e752 SW |
349 | break; |
350 | } | |
351 | ||
80387638 | 352 | if (chld && n->child) |
f88b6c16 | 353 | print_mdoc_nodelist(p, &npair, meta, n->child); |
80387638 | 354 | |
f88b6c16 FF |
355 | term_fontpopq(p, |
356 | (ENDBODY_NOT == n->end ? n : n->pending)->prev_font); | |
80387638 SW |
357 | |
358 | switch (n->type) { | |
070c62a6 | 359 | case MDOC_TEXT: |
80387638 | 360 | break; |
070c62a6 | 361 | case MDOC_TBL: |
80387638 | 362 | break; |
070c62a6 | 363 | case MDOC_EQN: |
60e1e752 | 364 | break; |
80387638 SW |
365 | default: |
366 | if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags) | |
367 | break; | |
f88b6c16 | 368 | (void)(*termacts[n->tok].post)(p, &npair, meta, n); |
80387638 SW |
369 | |
370 | /* | |
371 | * Explicit end tokens not only call the post | |
372 | * handler, but also tell the respective block | |
373 | * that it must not call the post handler again. | |
374 | */ | |
375 | if (ENDBODY_NOT != n->end) | |
376 | n->pending->flags |= MDOC_ENDED; | |
377 | ||
378 | /* | |
379 | * End of line terminating an implicit block | |
380 | * while an explicit block is still open. | |
381 | * Continue the explicit block without spacing. | |
382 | */ | |
383 | if (ENDBODY_NOSPACE == n->end) | |
384 | p->flags |= TERMP_NOSPACE; | |
385 | break; | |
386 | } | |
387 | ||
388 | if (MDOC_EOS & n->flags) | |
389 | p->flags |= TERMP_SENTENCE; | |
390 | ||
070c62a6 FF |
391 | if (MDOC_ll != n->tok) { |
392 | p->offset = offset; | |
393 | p->rmargin = rmargin; | |
394 | } | |
80387638 SW |
395 | } |
396 | ||
80387638 SW |
397 | static void |
398 | print_mdoc_foot(struct termp *p, const void *arg) | |
399 | { | |
f88b6c16 | 400 | const struct mdoc_meta *meta; |
80387638 | 401 | |
f88b6c16 | 402 | meta = (const struct mdoc_meta *)arg; |
80387638 SW |
403 | |
404 | term_fontrepl(p, TERMFONT_NONE); | |
405 | ||
070c62a6 | 406 | /* |
80387638 SW |
407 | * Output the footer in new-groff style, that is, three columns |
408 | * with the middle being the manual date and flanking columns | |
409 | * being the operating system: | |
410 | * | |
411 | * SYSTEM DATE SYSTEM | |
412 | */ | |
413 | ||
80387638 SW |
414 | term_vspace(p); |
415 | ||
416 | p->offset = 0; | |
070c62a6 FF |
417 | p->rmargin = (p->maxrmargin - |
418 | term_strlen(p, meta->date) + term_len(p, 1)) / 2; | |
7888c61d | 419 | p->trailspace = 1; |
80387638 SW |
420 | p->flags |= TERMP_NOSPACE | TERMP_NOBREAK; |
421 | ||
f88b6c16 | 422 | term_word(p, meta->os); |
80387638 SW |
423 | term_flushln(p); |
424 | ||
425 | p->offset = p->rmargin; | |
f88b6c16 | 426 | p->rmargin = p->maxrmargin - term_strlen(p, meta->os); |
36342e81 | 427 | p->flags |= TERMP_NOSPACE; |
80387638 | 428 | |
f88b6c16 | 429 | term_word(p, meta->date); |
80387638 SW |
430 | term_flushln(p); |
431 | ||
432 | p->offset = p->rmargin; | |
433 | p->rmargin = p->maxrmargin; | |
7888c61d | 434 | p->trailspace = 0; |
80387638 | 435 | p->flags &= ~TERMP_NOBREAK; |
36342e81 | 436 | p->flags |= TERMP_NOSPACE; |
80387638 | 437 | |
f88b6c16 | 438 | term_word(p, meta->os); |
80387638 SW |
439 | term_flushln(p); |
440 | ||
441 | p->offset = 0; | |
442 | p->rmargin = p->maxrmargin; | |
443 | p->flags = 0; | |
444 | } | |
445 | ||
80387638 SW |
446 | static void |
447 | print_mdoc_head(struct termp *p, const void *arg) | |
448 | { | |
070c62a6 FF |
449 | const struct mdoc_meta *meta; |
450 | char *volume, *title; | |
451 | size_t vollen, titlen; | |
80387638 | 452 | |
f88b6c16 | 453 | meta = (const struct mdoc_meta *)arg; |
80387638 | 454 | |
80387638 SW |
455 | /* |
456 | * The header is strange. It has three components, which are | |
457 | * really two with the first duplicated. It goes like this: | |
458 | * | |
459 | * IDENTIFIER TITLE IDENTIFIER | |
460 | * | |
461 | * The IDENTIFIER is NAME(SECTION), which is the command-name | |
462 | * (if given, or "unknown" if not) followed by the manual page | |
463 | * section. These are given in `Dt'. The TITLE is a free-form | |
464 | * string depending on the manual volume. If not specified, it | |
465 | * switches on the manual section. | |
466 | */ | |
467 | ||
36342e81 SW |
468 | p->offset = 0; |
469 | p->rmargin = p->maxrmargin; | |
470 | ||
f88b6c16 | 471 | assert(meta->vol); |
070c62a6 FF |
472 | if (NULL == meta->arch) |
473 | volume = mandoc_strdup(meta->vol); | |
474 | else | |
475 | mandoc_asprintf(&volume, "%s (%s)", | |
476 | meta->vol, meta->arch); | |
477 | vollen = term_strlen(p, volume); | |
80387638 | 478 | |
070c62a6 FF |
479 | if (NULL == meta->msec) |
480 | title = mandoc_strdup(meta->title); | |
481 | else | |
482 | mandoc_asprintf(&title, "%s(%s)", | |
483 | meta->title, meta->msec); | |
36342e81 | 484 | titlen = term_strlen(p, title); |
80387638 | 485 | |
80387638 | 486 | p->flags |= TERMP_NOBREAK | TERMP_NOSPACE; |
7888c61d | 487 | p->trailspace = 1; |
36342e81 | 488 | p->offset = 0; |
070c62a6 FF |
489 | p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ? |
490 | (p->maxrmargin - vollen + term_len(p, 1)) / 2 : | |
491 | p->maxrmargin - vollen; | |
80387638 SW |
492 | |
493 | term_word(p, title); | |
494 | term_flushln(p); | |
495 | ||
36342e81 | 496 | p->flags |= TERMP_NOSPACE; |
80387638 | 497 | p->offset = p->rmargin; |
070c62a6 | 498 | p->rmargin = p->offset + vollen + titlen < p->maxrmargin ? |
36342e81 | 499 | p->maxrmargin - titlen : p->maxrmargin; |
80387638 | 500 | |
070c62a6 | 501 | term_word(p, volume); |
80387638 SW |
502 | term_flushln(p); |
503 | ||
80387638 | 504 | p->flags &= ~TERMP_NOBREAK; |
7888c61d | 505 | p->trailspace = 0; |
36342e81 SW |
506 | if (p->rmargin + titlen <= p->maxrmargin) { |
507 | p->flags |= TERMP_NOSPACE; | |
508 | p->offset = p->rmargin; | |
509 | p->rmargin = p->maxrmargin; | |
510 | term_word(p, title); | |
511 | term_flushln(p); | |
512 | } | |
80387638 | 513 | |
36342e81 | 514 | p->flags &= ~TERMP_NOSPACE; |
80387638 SW |
515 | p->offset = 0; |
516 | p->rmargin = p->maxrmargin; | |
070c62a6 FF |
517 | free(title); |
518 | free(volume); | |
80387638 SW |
519 | } |
520 | ||
80387638 SW |
521 | static size_t |
522 | a2height(const struct termp *p, const char *v) | |
523 | { | |
524 | struct roffsu su; | |
525 | ||
36342e81 | 526 | |
80387638 SW |
527 | assert(v); |
528 | if ( ! a2roffsu(v, &su, SCALE_VS)) | |
36342e81 | 529 | SCALE_VS_INIT(&su, atoi(v)); |
80387638 SW |
530 | |
531 | return(term_vspan(p, &su)); | |
532 | } | |
533 | ||
80387638 SW |
534 | static size_t |
535 | a2width(const struct termp *p, const char *v) | |
536 | { | |
537 | struct roffsu su; | |
538 | ||
539 | assert(v); | |
540 | if ( ! a2roffsu(v, &su, SCALE_MAX)) | |
541 | SCALE_HS_INIT(&su, term_strlen(p, v)); | |
542 | ||
543 | return(term_hspan(p, &su)); | |
544 | } | |
545 | ||
80387638 SW |
546 | static size_t |
547 | a2offs(const struct termp *p, const char *v) | |
548 | { | |
549 | struct roffsu su; | |
550 | ||
551 | if ('\0' == *v) | |
552 | return(0); | |
553 | else if (0 == strcmp(v, "left")) | |
554 | return(0); | |
555 | else if (0 == strcmp(v, "indent")) | |
36342e81 | 556 | return(term_len(p, p->defindent + 1)); |
80387638 | 557 | else if (0 == strcmp(v, "indent-two")) |
36342e81 | 558 | return(term_len(p, (p->defindent + 1) * 2)); |
80387638 SW |
559 | else if ( ! a2roffsu(v, &su, SCALE_MAX)) |
560 | SCALE_HS_INIT(&su, term_strlen(p, v)); | |
561 | ||
562 | return(term_hspan(p, &su)); | |
563 | } | |
564 | ||
80387638 SW |
565 | /* |
566 | * Determine how much space to print out before block elements of `It' | |
567 | * (and thus `Bl') and `Bd'. And then go ahead and print that space, | |
568 | * too. | |
569 | */ | |
570 | static void | |
070c62a6 FF |
571 | print_bvspace(struct termp *p, |
572 | const struct mdoc_node *bl, | |
573 | const struct mdoc_node *n) | |
80387638 SW |
574 | { |
575 | const struct mdoc_node *nn; | |
576 | ||
36342e81 SW |
577 | assert(n); |
578 | ||
80387638 SW |
579 | term_newln(p); |
580 | ||
581 | if (MDOC_Bd == bl->tok && bl->norm->Bd.comp) | |
582 | return; | |
583 | if (MDOC_Bl == bl->tok && bl->norm->Bl.comp) | |
584 | return; | |
585 | ||
586 | /* Do not vspace directly after Ss/Sh. */ | |
587 | ||
588 | for (nn = n; nn; nn = nn->parent) { | |
589 | if (MDOC_BLOCK != nn->type) | |
590 | continue; | |
591 | if (MDOC_Ss == nn->tok) | |
592 | return; | |
593 | if (MDOC_Sh == nn->tok) | |
594 | return; | |
595 | if (NULL == nn->prev) | |
596 | continue; | |
597 | break; | |
598 | } | |
599 | ||
600 | /* A `-column' does not assert vspace within the list. */ | |
601 | ||
602 | if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type) | |
603 | if (n->prev && MDOC_It == n->prev->tok) | |
604 | return; | |
605 | ||
606 | /* A `-diag' without body does not vspace. */ | |
607 | ||
608 | if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type) | |
609 | if (n->prev && MDOC_It == n->prev->tok) { | |
610 | assert(n->prev->body); | |
611 | if (NULL == n->prev->body->child) | |
612 | return; | |
613 | } | |
614 | ||
615 | term_vspace(p); | |
616 | } | |
617 | ||
618 | ||
070c62a6 FF |
619 | static int |
620 | termp_ll_pre(DECL_ARGS) | |
621 | { | |
622 | ||
623 | term_setwidth(p, n->nchild ? n->child->string : NULL); | |
624 | return(0); | |
625 | } | |
626 | ||
80387638 SW |
627 | static int |
628 | termp_it_pre(DECL_ARGS) | |
629 | { | |
630 | const struct mdoc_node *bl, *nn; | |
070c62a6 FF |
631 | char buf[24]; |
632 | int i; | |
633 | size_t width, offset, ncols, dcol; | |
80387638 SW |
634 | enum mdoc_list type; |
635 | ||
636 | if (MDOC_BLOCK == n->type) { | |
637 | print_bvspace(p, n->parent->parent, n); | |
638 | return(1); | |
639 | } | |
640 | ||
641 | bl = n->parent->parent->parent; | |
642 | type = bl->norm->Bl.type; | |
643 | ||
070c62a6 | 644 | /* |
80387638 SW |
645 | * First calculate width and offset. This is pretty easy unless |
646 | * we're a -column list, in which case all prior columns must | |
647 | * be accounted for. | |
648 | */ | |
649 | ||
650 | width = offset = 0; | |
651 | ||
652 | if (bl->norm->Bl.offs) | |
653 | offset = a2offs(p, bl->norm->Bl.offs); | |
654 | ||
655 | switch (type) { | |
070c62a6 | 656 | case LIST_column: |
80387638 SW |
657 | if (MDOC_HEAD == n->type) |
658 | break; | |
659 | ||
660 | /* | |
661 | * Imitate groff's column handling: | |
662 | * - For each earlier column, add its width. | |
663 | * - For less than 5 columns, add four more blanks per | |
664 | * column. | |
665 | * - For exactly 5 columns, add three more blank per | |
666 | * column. | |
667 | * - For more than 5 columns, add only one column. | |
668 | */ | |
669 | ncols = bl->norm->Bl.ncols; | |
070c62a6 FF |
670 | dcol = ncols < 5 ? term_len(p, 4) : |
671 | ncols == 5 ? term_len(p, 3) : term_len(p, 1); | |
80387638 SW |
672 | |
673 | /* | |
674 | * Calculate the offset by applying all prior MDOC_BODY, | |
675 | * so we stop at the MDOC_HEAD (NULL == nn->prev). | |
676 | */ | |
677 | ||
070c62a6 FF |
678 | for (i = 0, nn = n->prev; |
679 | nn->prev && i < (int)ncols; | |
680 | nn = nn->prev, i++) | |
681 | offset += dcol + a2width(p, | |
682 | bl->norm->Bl.cols[i]); | |
80387638 SW |
683 | |
684 | /* | |
685 | * When exceeding the declared number of columns, leave | |
686 | * the remaining widths at 0. This will later be | |
687 | * adjusted to the default width of 10, or, for the last | |
688 | * column, stretched to the right margin. | |
689 | */ | |
690 | if (i >= (int)ncols) | |
691 | break; | |
692 | ||
693 | /* | |
694 | * Use the declared column widths, extended as explained | |
695 | * in the preceding paragraph. | |
696 | */ | |
697 | width = a2width(p, bl->norm->Bl.cols[i]) + dcol; | |
698 | break; | |
699 | default: | |
700 | if (NULL == bl->norm->Bl.width) | |
701 | break; | |
702 | ||
070c62a6 | 703 | /* |
80387638 SW |
704 | * Note: buffer the width by 2, which is groff's magic |
705 | * number for buffering single arguments. See the above | |
706 | * handling for column for how this changes. | |
707 | */ | |
708 | assert(bl->norm->Bl.width); | |
709 | width = a2width(p, bl->norm->Bl.width) + term_len(p, 2); | |
710 | break; | |
711 | } | |
712 | ||
070c62a6 | 713 | /* |
80387638 SW |
714 | * List-type can override the width in the case of fixed-head |
715 | * values (bullet, dash/hyphen, enum). Tags need a non-zero | |
716 | * offset. | |
717 | */ | |
718 | ||
719 | switch (type) { | |
070c62a6 | 720 | case LIST_bullet: |
80387638 | 721 | /* FALLTHROUGH */ |
070c62a6 | 722 | case LIST_dash: |
80387638 | 723 | /* FALLTHROUGH */ |
070c62a6 | 724 | case LIST_hyphen: |
f88b6c16 | 725 | /* FALLTHROUGH */ |
070c62a6 | 726 | case LIST_enum: |
f88b6c16 FF |
727 | if (width < term_len(p, 2)) |
728 | width = term_len(p, 2); | |
80387638 | 729 | break; |
070c62a6 | 730 | case LIST_hang: |
80387638 SW |
731 | if (0 == width) |
732 | width = term_len(p, 8); | |
733 | break; | |
070c62a6 | 734 | case LIST_column: |
80387638 | 735 | /* FALLTHROUGH */ |
070c62a6 | 736 | case LIST_tag: |
80387638 SW |
737 | if (0 == width) |
738 | width = term_len(p, 10); | |
739 | break; | |
740 | default: | |
741 | break; | |
742 | } | |
743 | ||
070c62a6 | 744 | /* |
80387638 SW |
745 | * Whitespace control. Inset bodies need an initial space, |
746 | * while diagonal bodies need two. | |
747 | */ | |
748 | ||
749 | p->flags |= TERMP_NOSPACE; | |
750 | ||
751 | switch (type) { | |
070c62a6 | 752 | case LIST_diag: |
80387638 SW |
753 | if (MDOC_BODY == n->type) |
754 | term_word(p, "\\ \\ "); | |
755 | break; | |
070c62a6 FF |
756 | case LIST_inset: |
757 | if (MDOC_BODY == n->type && n->parent->head->nchild) | |
80387638 SW |
758 | term_word(p, "\\ "); |
759 | break; | |
760 | default: | |
761 | break; | |
762 | } | |
763 | ||
764 | p->flags |= TERMP_NOSPACE; | |
765 | ||
766 | switch (type) { | |
070c62a6 | 767 | case LIST_diag: |
80387638 SW |
768 | if (MDOC_HEAD == n->type) |
769 | term_fontpush(p, TERMFONT_BOLD); | |
770 | break; | |
771 | default: | |
772 | break; | |
773 | } | |
774 | ||
775 | /* | |
776 | * Pad and break control. This is the tricky part. These flags | |
777 | * are documented in term_flushln() in term.c. Note that we're | |
778 | * going to unset all of these flags in termp_it_post() when we | |
779 | * exit. | |
780 | */ | |
781 | ||
782 | switch (type) { | |
070c62a6 | 783 | case LIST_enum: |
f88b6c16 FF |
784 | /* |
785 | * Weird special case. | |
786 | * Very narrow enum lists actually hang. | |
787 | */ | |
788 | if (width == term_len(p, 2)) | |
789 | p->flags |= TERMP_HANG; | |
790 | /* FALLTHROUGH */ | |
070c62a6 | 791 | case LIST_bullet: |
80387638 | 792 | /* FALLTHROUGH */ |
070c62a6 | 793 | case LIST_dash: |
80387638 | 794 | /* FALLTHROUGH */ |
070c62a6 | 795 | case LIST_hyphen: |
7888c61d FF |
796 | if (MDOC_HEAD != n->type) |
797 | break; | |
798 | p->flags |= TERMP_NOBREAK; | |
799 | p->trailspace = 1; | |
80387638 | 800 | break; |
070c62a6 | 801 | case LIST_hang: |
7888c61d | 802 | if (MDOC_HEAD != n->type) |
80387638 SW |
803 | break; |
804 | ||
805 | /* | |
806 | * This is ugly. If `-hang' is specified and the body | |
807 | * is a `Bl' or `Bd', then we want basically to nullify | |
808 | * the "overstep" effect in term_flushln() and treat | |
809 | * this as a `-ohang' list instead. | |
810 | */ | |
070c62a6 FF |
811 | if (n->next->child && |
812 | (MDOC_Bl == n->next->child->tok || | |
813 | MDOC_Bd == n->next->child->tok)) | |
7888c61d FF |
814 | break; |
815 | ||
070c62a6 | 816 | p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; |
7888c61d | 817 | p->trailspace = 1; |
80387638 | 818 | break; |
070c62a6 | 819 | case LIST_tag: |
80387638 SW |
820 | if (MDOC_HEAD != n->type) |
821 | break; | |
7888c61d | 822 | |
070c62a6 | 823 | p->flags |= TERMP_NOBREAK | TERMP_BRIND; |
7888c61d FF |
824 | p->trailspace = 2; |
825 | ||
80387638 SW |
826 | if (NULL == n->next || NULL == n->next->child) |
827 | p->flags |= TERMP_DANGLE; | |
828 | break; | |
070c62a6 | 829 | case LIST_column: |
80387638 SW |
830 | if (MDOC_HEAD == n->type) |
831 | break; | |
832 | ||
7888c61d | 833 | if (NULL == n->next) { |
80387638 | 834 | p->flags &= ~TERMP_NOBREAK; |
7888c61d FF |
835 | p->trailspace = 0; |
836 | } else { | |
80387638 | 837 | p->flags |= TERMP_NOBREAK; |
7888c61d FF |
838 | p->trailspace = 1; |
839 | } | |
80387638 | 840 | |
80387638 | 841 | break; |
070c62a6 | 842 | case LIST_diag: |
7888c61d FF |
843 | if (MDOC_HEAD != n->type) |
844 | break; | |
070c62a6 | 845 | p->flags |= TERMP_NOBREAK | TERMP_BRIND; |
7888c61d | 846 | p->trailspace = 1; |
80387638 SW |
847 | break; |
848 | default: | |
849 | break; | |
850 | } | |
851 | ||
070c62a6 | 852 | /* |
80387638 SW |
853 | * Margin control. Set-head-width lists have their right |
854 | * margins shortened. The body for these lists has the offset | |
855 | * necessarily lengthened. Everybody gets the offset. | |
856 | */ | |
857 | ||
858 | p->offset += offset; | |
859 | ||
860 | switch (type) { | |
070c62a6 | 861 | case LIST_hang: |
80387638 SW |
862 | /* |
863 | * Same stipulation as above, regarding `-hang'. We | |
864 | * don't want to recalculate rmargin and offsets when | |
865 | * using `Bd' or `Bl' within `-hang' overstep lists. | |
866 | */ | |
867 | if (MDOC_HEAD == n->type && n->next->child && | |
070c62a6 FF |
868 | (MDOC_Bl == n->next->child->tok || |
869 | MDOC_Bd == n->next->child->tok)) | |
80387638 SW |
870 | break; |
871 | /* FALLTHROUGH */ | |
070c62a6 | 872 | case LIST_bullet: |
80387638 | 873 | /* FALLTHROUGH */ |
070c62a6 | 874 | case LIST_dash: |
80387638 | 875 | /* FALLTHROUGH */ |
070c62a6 | 876 | case LIST_enum: |
80387638 | 877 | /* FALLTHROUGH */ |
070c62a6 | 878 | case LIST_hyphen: |
80387638 | 879 | /* FALLTHROUGH */ |
070c62a6 | 880 | case LIST_tag: |
80387638 SW |
881 | assert(width); |
882 | if (MDOC_HEAD == n->type) | |
883 | p->rmargin = p->offset + width; | |
070c62a6 | 884 | else { |
80387638 | 885 | p->offset += width; |
070c62a6 FF |
886 | if (p->rmargin < p->offset) |
887 | p->rmargin = p->offset; | |
888 | } | |
80387638 | 889 | break; |
070c62a6 | 890 | case LIST_column: |
80387638 SW |
891 | assert(width); |
892 | p->rmargin = p->offset + width; | |
070c62a6 | 893 | /* |
80387638 SW |
894 | * XXX - this behaviour is not documented: the |
895 | * right-most column is filled to the right margin. | |
896 | */ | |
897 | if (MDOC_HEAD == n->type) | |
898 | break; | |
899 | if (NULL == n->next && p->rmargin < p->maxrmargin) | |
900 | p->rmargin = p->maxrmargin; | |
901 | break; | |
902 | default: | |
903 | break; | |
904 | } | |
905 | ||
070c62a6 | 906 | /* |
80387638 | 907 | * The dash, hyphen, bullet and enum lists all have a special |
070c62a6 | 908 | * HEAD character (temporarily bold, in some cases). |
80387638 SW |
909 | */ |
910 | ||
911 | if (MDOC_HEAD == n->type) | |
912 | switch (type) { | |
070c62a6 | 913 | case LIST_bullet: |
80387638 SW |
914 | term_fontpush(p, TERMFONT_BOLD); |
915 | term_word(p, "\\[bu]"); | |
916 | term_fontpop(p); | |
917 | break; | |
070c62a6 | 918 | case LIST_dash: |
80387638 | 919 | /* FALLTHROUGH */ |
070c62a6 | 920 | case LIST_hyphen: |
80387638 SW |
921 | term_fontpush(p, TERMFONT_BOLD); |
922 | term_word(p, "\\(hy"); | |
923 | term_fontpop(p); | |
924 | break; | |
070c62a6 | 925 | case LIST_enum: |
80387638 | 926 | (pair->ppair->ppair->count)++; |
070c62a6 FF |
927 | (void)snprintf(buf, sizeof(buf), "%d.", |
928 | pair->ppair->ppair->count); | |
80387638 SW |
929 | term_word(p, buf); |
930 | break; | |
931 | default: | |
932 | break; | |
933 | } | |
934 | ||
070c62a6 | 935 | /* |
80387638 SW |
936 | * If we're not going to process our children, indicate so here. |
937 | */ | |
938 | ||
939 | switch (type) { | |
070c62a6 | 940 | case LIST_bullet: |
80387638 | 941 | /* FALLTHROUGH */ |
070c62a6 | 942 | case LIST_item: |
80387638 | 943 | /* FALLTHROUGH */ |
070c62a6 | 944 | case LIST_dash: |
80387638 | 945 | /* FALLTHROUGH */ |
070c62a6 | 946 | case LIST_hyphen: |
80387638 | 947 | /* FALLTHROUGH */ |
070c62a6 | 948 | case LIST_enum: |
80387638 SW |
949 | if (MDOC_HEAD == n->type) |
950 | return(0); | |
951 | break; | |
070c62a6 | 952 | case LIST_column: |
80387638 SW |
953 | if (MDOC_HEAD == n->type) |
954 | return(0); | |
955 | break; | |
956 | default: | |
957 | break; | |
958 | } | |
959 | ||
960 | return(1); | |
961 | } | |
962 | ||
80387638 SW |
963 | static void |
964 | termp_it_post(DECL_ARGS) | |
965 | { | |
966 | enum mdoc_list type; | |
967 | ||
968 | if (MDOC_BLOCK == n->type) | |
969 | return; | |
970 | ||
971 | type = n->parent->parent->parent->norm->Bl.type; | |
972 | ||
973 | switch (type) { | |
070c62a6 | 974 | case LIST_item: |
80387638 | 975 | /* FALLTHROUGH */ |
070c62a6 | 976 | case LIST_diag: |
80387638 | 977 | /* FALLTHROUGH */ |
070c62a6 | 978 | case LIST_inset: |
80387638 SW |
979 | if (MDOC_BODY == n->type) |
980 | term_newln(p); | |
981 | break; | |
070c62a6 | 982 | case LIST_column: |
80387638 SW |
983 | if (MDOC_BODY == n->type) |
984 | term_flushln(p); | |
985 | break; | |
986 | default: | |
987 | term_newln(p); | |
988 | break; | |
989 | } | |
990 | ||
070c62a6 | 991 | /* |
80387638 SW |
992 | * Now that our output is flushed, we can reset our tags. Since |
993 | * only `It' sets these flags, we're free to assume that nobody | |
994 | * has munged them in the meanwhile. | |
995 | */ | |
996 | ||
070c62a6 FF |
997 | p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | |
998 | TERMP_DANGLE | TERMP_HANG); | |
7888c61d | 999 | p->trailspace = 0; |
80387638 SW |
1000 | } |
1001 | ||
80387638 SW |
1002 | static int |
1003 | termp_nm_pre(DECL_ARGS) | |
1004 | { | |
1005 | ||
7888c61d FF |
1006 | if (MDOC_BLOCK == n->type) { |
1007 | p->flags |= TERMP_PREKEEP; | |
80387638 | 1008 | return(1); |
7888c61d | 1009 | } |
80387638 SW |
1010 | |
1011 | if (MDOC_BODY == n->type) { | |
1012 | if (NULL == n->child) | |
1013 | return(0); | |
36342e81 | 1014 | p->flags |= TERMP_NOSPACE; |
80387638 | 1015 | p->offset += term_len(p, 1) + |
f88b6c16 FF |
1016 | (NULL == n->prev->child ? |
1017 | term_strlen(p, meta->name) : | |
80387638 | 1018 | MDOC_TEXT == n->prev->child->type ? |
f88b6c16 | 1019 | term_strlen(p, n->prev->child->string) : |
80387638 | 1020 | term_len(p, 5)); |
070c62a6 FF |
1021 | if (p->rmargin < p->offset) |
1022 | p->rmargin = p->offset; | |
80387638 SW |
1023 | return(1); |
1024 | } | |
1025 | ||
f88b6c16 | 1026 | if (NULL == n->child && NULL == meta->name) |
80387638 SW |
1027 | return(0); |
1028 | ||
1029 | if (MDOC_HEAD == n->type) | |
1030 | synopsis_pre(p, n->parent); | |
1031 | ||
1032 | if (MDOC_HEAD == n->type && n->next->child) { | |
070c62a6 | 1033 | p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND; |
7888c61d | 1034 | p->trailspace = 1; |
80387638 SW |
1035 | p->rmargin = p->offset + term_len(p, 1); |
1036 | if (NULL == n->child) { | |
f88b6c16 | 1037 | p->rmargin += term_strlen(p, meta->name); |
80387638 SW |
1038 | } else if (MDOC_TEXT == n->child->type) { |
1039 | p->rmargin += term_strlen(p, n->child->string); | |
1040 | if (n->child->next) | |
1041 | p->flags |= TERMP_HANG; | |
1042 | } else { | |
1043 | p->rmargin += term_len(p, 5); | |
1044 | p->flags |= TERMP_HANG; | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | term_fontpush(p, TERMFONT_BOLD); | |
1049 | if (NULL == n->child) | |
f88b6c16 | 1050 | term_word(p, meta->name); |
80387638 SW |
1051 | return(1); |
1052 | } | |
1053 | ||
80387638 SW |
1054 | static void |
1055 | termp_nm_post(DECL_ARGS) | |
1056 | { | |
1057 | ||
7888c61d FF |
1058 | if (MDOC_BLOCK == n->type) { |
1059 | p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); | |
1060 | } else if (MDOC_HEAD == n->type && n->next->child) { | |
80387638 | 1061 | term_flushln(p); |
070c62a6 | 1062 | p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); |
7888c61d | 1063 | p->trailspace = 0; |
36342e81 | 1064 | } else if (MDOC_BODY == n->type && n->child) |
80387638 | 1065 | term_flushln(p); |
80387638 SW |
1066 | } |
1067 | ||
80387638 SW |
1068 | static int |
1069 | termp_fl_pre(DECL_ARGS) | |
1070 | { | |
1071 | ||
1072 | term_fontpush(p, TERMFONT_BOLD); | |
1073 | term_word(p, "\\-"); | |
1074 | ||
1075 | if (n->child) | |
1076 | p->flags |= TERMP_NOSPACE; | |
1077 | else if (n->next && n->next->line == n->line) | |
1078 | p->flags |= TERMP_NOSPACE; | |
1079 | ||
1080 | return(1); | |
1081 | } | |
1082 | ||
80387638 SW |
1083 | static int |
1084 | termp__a_pre(DECL_ARGS) | |
1085 | { | |
1086 | ||
1087 | if (n->prev && MDOC__A == n->prev->tok) | |
1088 | if (NULL == n->next || MDOC__A != n->next->tok) | |
1089 | term_word(p, "and"); | |
1090 | ||
1091 | return(1); | |
1092 | } | |
1093 | ||
80387638 SW |
1094 | static int |
1095 | termp_an_pre(DECL_ARGS) | |
1096 | { | |
1097 | ||
1098 | if (NULL == n->child) | |
1099 | return(1); | |
1100 | ||
1101 | /* | |
1102 | * If not in the AUTHORS section, `An -split' will cause | |
1103 | * newlines to occur before the author name. If in the AUTHORS | |
1104 | * section, by default, the first `An' invocation is nosplit, | |
1105 | * then all subsequent ones, regardless of whether interspersed | |
1106 | * with other macros/text, are split. -split, in this case, | |
1107 | * will override the condition of the implied first -nosplit. | |
1108 | */ | |
070c62a6 | 1109 | |
80387638 SW |
1110 | if (n->sec == SEC_AUTHORS) { |
1111 | if ( ! (TERMP_ANPREC & p->flags)) { | |
1112 | if (TERMP_SPLIT & p->flags) | |
1113 | term_newln(p); | |
1114 | return(1); | |
1115 | } | |
1116 | if (TERMP_NOSPLIT & p->flags) | |
1117 | return(1); | |
1118 | term_newln(p); | |
1119 | return(1); | |
1120 | } | |
1121 | ||
1122 | if (TERMP_SPLIT & p->flags) | |
1123 | term_newln(p); | |
1124 | ||
1125 | return(1); | |
1126 | } | |
1127 | ||
80387638 SW |
1128 | static void |
1129 | termp_an_post(DECL_ARGS) | |
1130 | { | |
1131 | ||
1132 | if (n->child) { | |
1133 | if (SEC_AUTHORS == n->sec) | |
1134 | p->flags |= TERMP_ANPREC; | |
1135 | return; | |
1136 | } | |
1137 | ||
1138 | if (AUTH_split == n->norm->An.auth) { | |
1139 | p->flags &= ~TERMP_NOSPLIT; | |
1140 | p->flags |= TERMP_SPLIT; | |
1141 | } else if (AUTH_nosplit == n->norm->An.auth) { | |
1142 | p->flags &= ~TERMP_SPLIT; | |
1143 | p->flags |= TERMP_NOSPLIT; | |
1144 | } | |
1145 | ||
1146 | } | |
1147 | ||
80387638 SW |
1148 | static int |
1149 | termp_ns_pre(DECL_ARGS) | |
1150 | { | |
1151 | ||
60e1e752 SW |
1152 | if ( ! (MDOC_LINE & n->flags)) |
1153 | p->flags |= TERMP_NOSPACE; | |
80387638 SW |
1154 | return(1); |
1155 | } | |
1156 | ||
80387638 SW |
1157 | static int |
1158 | termp_rs_pre(DECL_ARGS) | |
1159 | { | |
1160 | ||
1161 | if (SEC_SEE_ALSO != n->sec) | |
1162 | return(1); | |
1163 | if (MDOC_BLOCK == n->type && n->prev) | |
1164 | term_vspace(p); | |
1165 | return(1); | |
1166 | } | |
1167 | ||
80387638 SW |
1168 | static int |
1169 | termp_rv_pre(DECL_ARGS) | |
1170 | { | |
60e1e752 | 1171 | int nchild; |
80387638 SW |
1172 | |
1173 | term_newln(p); | |
80387638 | 1174 | |
60e1e752 | 1175 | nchild = n->nchild; |
070c62a6 FF |
1176 | if (nchild > 0) { |
1177 | term_word(p, "The"); | |
60e1e752 | 1178 | |
070c62a6 FF |
1179 | for (n = n->child; n; n = n->next) { |
1180 | term_fontpush(p, TERMFONT_BOLD); | |
1181 | term_word(p, n->string); | |
1182 | term_fontpop(p); | |
60e1e752 | 1183 | |
60e1e752 | 1184 | p->flags |= TERMP_NOSPACE; |
070c62a6 FF |
1185 | term_word(p, "()"); |
1186 | ||
1187 | if (n->next == NULL) | |
1188 | continue; | |
1189 | ||
1190 | if (nchild > 2) { | |
1191 | p->flags |= TERMP_NOSPACE; | |
1192 | term_word(p, ","); | |
1193 | } | |
1194 | if (n->next->next == NULL) | |
1195 | term_word(p, "and"); | |
60e1e752 SW |
1196 | } |
1197 | ||
070c62a6 FF |
1198 | if (nchild > 1) |
1199 | term_word(p, "functions return"); | |
1200 | else | |
1201 | term_word(p, "function returns"); | |
80387638 | 1202 | |
070c62a6 FF |
1203 | term_word(p, "the value\\~0 if successful;"); |
1204 | } else | |
1205 | term_word(p, "Upon successful completion," | |
1206 | " the value\\~0 is returned;"); | |
80387638 | 1207 | |
070c62a6 FF |
1208 | term_word(p, "otherwise the value\\~\\-1 is returned" |
1209 | " and the global variable"); | |
80387638 SW |
1210 | |
1211 | term_fontpush(p, TERMFONT_UNDER); | |
1212 | term_word(p, "errno"); | |
1213 | term_fontpop(p); | |
1214 | ||
070c62a6 | 1215 | term_word(p, "is set to indicate the error."); |
80387638 SW |
1216 | p->flags |= TERMP_SENTENCE; |
1217 | ||
1218 | return(0); | |
1219 | } | |
1220 | ||
80387638 SW |
1221 | static int |
1222 | termp_ex_pre(DECL_ARGS) | |
1223 | { | |
60e1e752 | 1224 | int nchild; |
80387638 | 1225 | |
60e1e752 | 1226 | term_newln(p); |
80387638 SW |
1227 | term_word(p, "The"); |
1228 | ||
60e1e752 SW |
1229 | nchild = n->nchild; |
1230 | for (n = n->child; n; n = n->next) { | |
80387638 | 1231 | term_fontpush(p, TERMFONT_BOLD); |
60e1e752 | 1232 | term_word(p, n->string); |
80387638 | 1233 | term_fontpop(p); |
60e1e752 SW |
1234 | |
1235 | if (nchild > 2 && n->next) { | |
1236 | p->flags |= TERMP_NOSPACE; | |
80387638 | 1237 | term_word(p, ","); |
60e1e752 SW |
1238 | } |
1239 | ||
1240 | if (n->next && NULL == n->next->next) | |
1241 | term_word(p, "and"); | |
80387638 SW |
1242 | } |
1243 | ||
60e1e752 | 1244 | if (nchild > 1) |
070c62a6 | 1245 | term_word(p, "utilities exit\\~0"); |
80387638 | 1246 | else |
070c62a6 | 1247 | term_word(p, "utility exits\\~0"); |
80387638 | 1248 | |
070c62a6 | 1249 | term_word(p, "on success, and\\~>0 if an error occurs."); |
80387638 | 1250 | |
60e1e752 | 1251 | p->flags |= TERMP_SENTENCE; |
80387638 SW |
1252 | return(0); |
1253 | } | |
1254 | ||
80387638 SW |
1255 | static int |
1256 | termp_nd_pre(DECL_ARGS) | |
1257 | { | |
1258 | ||
1259 | if (MDOC_BODY != n->type) | |
1260 | return(1); | |
1261 | ||
1262 | #if defined(__OpenBSD__) || defined(__linux__) | |
1263 | term_word(p, "\\(en"); | |
1264 | #else | |
1265 | term_word(p, "\\(em"); | |
1266 | #endif | |
1267 | return(1); | |
1268 | } | |
1269 | ||
80387638 SW |
1270 | static int |
1271 | termp_bl_pre(DECL_ARGS) | |
1272 | { | |
1273 | ||
1274 | return(MDOC_HEAD != n->type); | |
1275 | } | |
1276 | ||
80387638 SW |
1277 | static void |
1278 | termp_bl_post(DECL_ARGS) | |
1279 | { | |
1280 | ||
1281 | if (MDOC_BLOCK == n->type) | |
1282 | term_newln(p); | |
1283 | } | |
1284 | ||
80387638 SW |
1285 | static int |
1286 | termp_xr_pre(DECL_ARGS) | |
1287 | { | |
80387638 | 1288 | |
60e1e752 | 1289 | if (NULL == (n = n->child)) |
80387638 SW |
1290 | return(0); |
1291 | ||
60e1e752 SW |
1292 | assert(MDOC_TEXT == n->type); |
1293 | term_word(p, n->string); | |
80387638 | 1294 | |
070c62a6 | 1295 | if (NULL == (n = n->next)) |
80387638 | 1296 | return(0); |
60e1e752 | 1297 | |
80387638 SW |
1298 | p->flags |= TERMP_NOSPACE; |
1299 | term_word(p, "("); | |
60e1e752 SW |
1300 | p->flags |= TERMP_NOSPACE; |
1301 | ||
1302 | assert(MDOC_TEXT == n->type); | |
1303 | term_word(p, n->string); | |
1304 | ||
1305 | p->flags |= TERMP_NOSPACE; | |
80387638 SW |
1306 | term_word(p, ")"); |
1307 | ||
1308 | return(0); | |
1309 | } | |
1310 | ||
80387638 SW |
1311 | /* |
1312 | * This decides how to assert whitespace before any of the SYNOPSIS set | |
1313 | * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain | |
1314 | * macro combos). | |
1315 | */ | |
1316 | static void | |
1317 | synopsis_pre(struct termp *p, const struct mdoc_node *n) | |
1318 | { | |
070c62a6 | 1319 | /* |
80387638 SW |
1320 | * Obviously, if we're not in a SYNOPSIS or no prior macros |
1321 | * exist, do nothing. | |
1322 | */ | |
1323 | if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) | |
1324 | return; | |
1325 | ||
1326 | /* | |
1327 | * If we're the second in a pair of like elements, emit our | |
1328 | * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which | |
1329 | * case we soldier on. | |
1330 | */ | |
070c62a6 FF |
1331 | if (n->prev->tok == n->tok && |
1332 | MDOC_Ft != n->tok && | |
1333 | MDOC_Fo != n->tok && | |
1334 | MDOC_Fn != n->tok) { | |
80387638 SW |
1335 | term_newln(p); |
1336 | return; | |
1337 | } | |
1338 | ||
1339 | /* | |
1340 | * If we're one of the SYNOPSIS set and non-like pair-wise after | |
1341 | * another (or Fn/Fo, which we've let slip through) then assert | |
1342 | * vertical space, else only newline and move on. | |
1343 | */ | |
1344 | switch (n->prev->tok) { | |
070c62a6 | 1345 | case MDOC_Fd: |
80387638 | 1346 | /* FALLTHROUGH */ |
070c62a6 | 1347 | case MDOC_Fn: |
80387638 | 1348 | /* FALLTHROUGH */ |
070c62a6 | 1349 | case MDOC_Fo: |
80387638 | 1350 | /* FALLTHROUGH */ |
070c62a6 | 1351 | case MDOC_In: |
80387638 | 1352 | /* FALLTHROUGH */ |
070c62a6 | 1353 | case MDOC_Vt: |
80387638 SW |
1354 | term_vspace(p); |
1355 | break; | |
070c62a6 | 1356 | case MDOC_Ft: |
80387638 SW |
1357 | if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { |
1358 | term_vspace(p); | |
1359 | break; | |
1360 | } | |
1361 | /* FALLTHROUGH */ | |
1362 | default: | |
1363 | term_newln(p); | |
1364 | break; | |
1365 | } | |
1366 | } | |
1367 | ||
80387638 SW |
1368 | static int |
1369 | termp_vt_pre(DECL_ARGS) | |
1370 | { | |
1371 | ||
1372 | if (MDOC_ELEM == n->type) { | |
1373 | synopsis_pre(p, n); | |
f88b6c16 | 1374 | return(termp_under_pre(p, pair, meta, n)); |
80387638 SW |
1375 | } else if (MDOC_BLOCK == n->type) { |
1376 | synopsis_pre(p, n); | |
1377 | return(1); | |
1378 | } else if (MDOC_HEAD == n->type) | |
1379 | return(0); | |
1380 | ||
f88b6c16 | 1381 | return(termp_under_pre(p, pair, meta, n)); |
80387638 SW |
1382 | } |
1383 | ||
80387638 SW |
1384 | static int |
1385 | termp_bold_pre(DECL_ARGS) | |
1386 | { | |
1387 | ||
1388 | term_fontpush(p, TERMFONT_BOLD); | |
1389 | return(1); | |
1390 | } | |
1391 | ||
80387638 SW |
1392 | static int |
1393 | termp_fd_pre(DECL_ARGS) | |
1394 | { | |
1395 | ||
1396 | synopsis_pre(p, n); | |
f88b6c16 FF |
1397 | return(termp_bold_pre(p, pair, meta, n)); |
1398 | } | |
1399 | ||
f88b6c16 FF |
1400 | static void |
1401 | termp_fd_post(DECL_ARGS) | |
1402 | { | |
1403 | ||
1404 | term_newln(p); | |
80387638 SW |
1405 | } |
1406 | ||
80387638 SW |
1407 | static int |
1408 | termp_sh_pre(DECL_ARGS) | |
1409 | { | |
1410 | ||
1411 | /* No vspace between consecutive `Sh' calls. */ | |
1412 | ||
1413 | switch (n->type) { | |
070c62a6 | 1414 | case MDOC_BLOCK: |
80387638 SW |
1415 | if (n->prev && MDOC_Sh == n->prev->tok) |
1416 | if (NULL == n->prev->body->child) | |
1417 | break; | |
1418 | term_vspace(p); | |
1419 | break; | |
070c62a6 | 1420 | case MDOC_HEAD: |
80387638 SW |
1421 | term_fontpush(p, TERMFONT_BOLD); |
1422 | break; | |
070c62a6 | 1423 | case MDOC_BODY: |
36342e81 | 1424 | p->offset = term_len(p, p->defindent); |
f88b6c16 FF |
1425 | if (SEC_AUTHORS == n->sec) |
1426 | p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT); | |
80387638 SW |
1427 | break; |
1428 | default: | |
1429 | break; | |
1430 | } | |
1431 | return(1); | |
1432 | } | |
1433 | ||
80387638 SW |
1434 | static void |
1435 | termp_sh_post(DECL_ARGS) | |
1436 | { | |
1437 | ||
1438 | switch (n->type) { | |
070c62a6 | 1439 | case MDOC_HEAD: |
80387638 SW |
1440 | term_newln(p); |
1441 | break; | |
070c62a6 | 1442 | case MDOC_BODY: |
80387638 SW |
1443 | term_newln(p); |
1444 | p->offset = 0; | |
1445 | break; | |
1446 | default: | |
1447 | break; | |
1448 | } | |
1449 | } | |
1450 | ||
80387638 SW |
1451 | static int |
1452 | termp_bt_pre(DECL_ARGS) | |
1453 | { | |
1454 | ||
1455 | term_word(p, "is currently in beta test."); | |
1456 | p->flags |= TERMP_SENTENCE; | |
1457 | return(0); | |
1458 | } | |
1459 | ||
80387638 SW |
1460 | static void |
1461 | termp_lb_post(DECL_ARGS) | |
1462 | { | |
1463 | ||
1464 | if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags) | |
1465 | term_newln(p); | |
1466 | } | |
1467 | ||
80387638 SW |
1468 | static int |
1469 | termp_ud_pre(DECL_ARGS) | |
1470 | { | |
1471 | ||
1472 | term_word(p, "currently under development."); | |
1473 | p->flags |= TERMP_SENTENCE; | |
1474 | return(0); | |
1475 | } | |
1476 | ||
80387638 SW |
1477 | static int |
1478 | termp_d1_pre(DECL_ARGS) | |
1479 | { | |
1480 | ||
1481 | if (MDOC_BLOCK != n->type) | |
1482 | return(1); | |
1483 | term_newln(p); | |
36342e81 | 1484 | p->offset += term_len(p, p->defindent + 1); |
80387638 SW |
1485 | return(1); |
1486 | } | |
1487 | ||
80387638 SW |
1488 | static int |
1489 | termp_ft_pre(DECL_ARGS) | |
1490 | { | |
1491 | ||
1492 | /* NB: MDOC_LINE does not effect this! */ | |
1493 | synopsis_pre(p, n); | |
1494 | term_fontpush(p, TERMFONT_UNDER); | |
1495 | return(1); | |
1496 | } | |
1497 | ||
80387638 SW |
1498 | static int |
1499 | termp_fn_pre(DECL_ARGS) | |
1500 | { | |
7888c61d | 1501 | size_t rmargin = 0; |
60e1e752 SW |
1502 | int pretty; |
1503 | ||
1504 | pretty = MDOC_SYNPRETTY & n->flags; | |
80387638 SW |
1505 | |
1506 | synopsis_pre(p, n); | |
1507 | ||
60e1e752 SW |
1508 | if (NULL == (n = n->child)) |
1509 | return(0); | |
1510 | ||
7888c61d FF |
1511 | if (pretty) { |
1512 | rmargin = p->rmargin; | |
1513 | p->rmargin = p->offset + term_len(p, 4); | |
070c62a6 | 1514 | p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG; |
7888c61d FF |
1515 | } |
1516 | ||
60e1e752 | 1517 | assert(MDOC_TEXT == n->type); |
80387638 | 1518 | term_fontpush(p, TERMFONT_BOLD); |
60e1e752 | 1519 | term_word(p, n->string); |
80387638 SW |
1520 | term_fontpop(p); |
1521 | ||
7888c61d FF |
1522 | if (pretty) { |
1523 | term_flushln(p); | |
070c62a6 | 1524 | p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG); |
7888c61d FF |
1525 | p->offset = p->rmargin; |
1526 | p->rmargin = rmargin; | |
1527 | } | |
1528 | ||
80387638 SW |
1529 | p->flags |= TERMP_NOSPACE; |
1530 | term_word(p, "("); | |
60e1e752 | 1531 | p->flags |= TERMP_NOSPACE; |
80387638 | 1532 | |
60e1e752 SW |
1533 | for (n = n->next; n; n = n->next) { |
1534 | assert(MDOC_TEXT == n->type); | |
80387638 | 1535 | term_fontpush(p, TERMFONT_UNDER); |
7888c61d FF |
1536 | if (pretty) |
1537 | p->flags |= TERMP_NBRWORD; | |
60e1e752 | 1538 | term_word(p, n->string); |
80387638 SW |
1539 | term_fontpop(p); |
1540 | ||
60e1e752 SW |
1541 | if (n->next) { |
1542 | p->flags |= TERMP_NOSPACE; | |
80387638 | 1543 | term_word(p, ","); |
60e1e752 | 1544 | } |
80387638 SW |
1545 | } |
1546 | ||
60e1e752 | 1547 | p->flags |= TERMP_NOSPACE; |
80387638 SW |
1548 | term_word(p, ")"); |
1549 | ||
60e1e752 SW |
1550 | if (pretty) { |
1551 | p->flags |= TERMP_NOSPACE; | |
80387638 | 1552 | term_word(p, ";"); |
7888c61d | 1553 | term_flushln(p); |
60e1e752 | 1554 | } |
80387638 SW |
1555 | |
1556 | return(0); | |
1557 | } | |
1558 | ||
80387638 SW |
1559 | static int |
1560 | termp_fa_pre(DECL_ARGS) | |
1561 | { | |
1562 | const struct mdoc_node *nn; | |
1563 | ||
1564 | if (n->parent->tok != MDOC_Fo) { | |
1565 | term_fontpush(p, TERMFONT_UNDER); | |
1566 | return(1); | |
1567 | } | |
1568 | ||
1569 | for (nn = n->child; nn; nn = nn->next) { | |
1570 | term_fontpush(p, TERMFONT_UNDER); | |
7888c61d | 1571 | p->flags |= TERMP_NBRWORD; |
80387638 SW |
1572 | term_word(p, nn->string); |
1573 | term_fontpop(p); | |
1574 | ||
7888c61d | 1575 | if (nn->next || (n->next && n->next->tok == MDOC_Fa)) { |
60e1e752 | 1576 | p->flags |= TERMP_NOSPACE; |
80387638 | 1577 | term_word(p, ","); |
60e1e752 | 1578 | } |
80387638 SW |
1579 | } |
1580 | ||
80387638 SW |
1581 | return(0); |
1582 | } | |
1583 | ||
80387638 SW |
1584 | static int |
1585 | termp_bd_pre(DECL_ARGS) | |
1586 | { | |
070c62a6 | 1587 | size_t tabwidth, lm, len, rm, rmax; |
f88b6c16 | 1588 | struct mdoc_node *nn; |
80387638 SW |
1589 | |
1590 | if (MDOC_BLOCK == n->type) { | |
1591 | print_bvspace(p, n, n); | |
1592 | return(1); | |
1593 | } else if (MDOC_HEAD == n->type) | |
1594 | return(0); | |
1595 | ||
1596 | if (n->norm->Bd.offs) | |
1597 | p->offset += a2offs(p, n->norm->Bd.offs); | |
1598 | ||
1599 | /* | |
1600 | * If -ragged or -filled are specified, the block does nothing | |
1601 | * but change the indentation. If -unfilled or -literal are | |
1602 | * specified, text is printed exactly as entered in the display: | |
1603 | * for macro lines, a newline is appended to the line. Blank | |
1604 | * lines are allowed. | |
1605 | */ | |
070c62a6 FF |
1606 | |
1607 | if (DISP_literal != n->norm->Bd.type && | |
1608 | DISP_unfilled != n->norm->Bd.type && | |
1609 | DISP_centered != n->norm->Bd.type) | |
80387638 SW |
1610 | return(1); |
1611 | ||
1612 | tabwidth = p->tabwidth; | |
1613 | if (DISP_literal == n->norm->Bd.type) | |
1614 | p->tabwidth = term_len(p, 8); | |
1615 | ||
070c62a6 | 1616 | lm = p->offset; |
80387638 SW |
1617 | rm = p->rmargin; |
1618 | rmax = p->maxrmargin; | |
1619 | p->rmargin = p->maxrmargin = TERM_MAXMARGIN; | |
1620 | ||
1621 | for (nn = n->child; nn; nn = nn->next) { | |
070c62a6 FF |
1622 | if (DISP_centered == n->norm->Bd.type) { |
1623 | if (MDOC_TEXT == nn->type) { | |
1624 | len = term_strlen(p, nn->string); | |
1625 | p->offset = len >= rm ? 0 : | |
1626 | lm + len >= rm ? rm - len : | |
1627 | (lm + rm - len) / 2; | |
1628 | } else | |
1629 | p->offset = lm; | |
1630 | } | |
f88b6c16 | 1631 | print_mdoc_node(p, pair, meta, nn); |
80387638 SW |
1632 | /* |
1633 | * If the printed node flushes its own line, then we | |
1634 | * needn't do it here as well. This is hacky, but the | |
1635 | * notion of selective eoln whitespace is pretty dumb | |
1636 | * anyway, so don't sweat it. | |
1637 | */ | |
1638 | switch (nn->tok) { | |
070c62a6 | 1639 | case MDOC_Sm: |
80387638 | 1640 | /* FALLTHROUGH */ |
070c62a6 | 1641 | case MDOC_br: |
80387638 | 1642 | /* FALLTHROUGH */ |
070c62a6 | 1643 | case MDOC_sp: |
80387638 | 1644 | /* FALLTHROUGH */ |
070c62a6 | 1645 | case MDOC_Bl: |
80387638 | 1646 | /* FALLTHROUGH */ |
070c62a6 | 1647 | case MDOC_D1: |
80387638 | 1648 | /* FALLTHROUGH */ |
070c62a6 | 1649 | case MDOC_Dl: |
80387638 | 1650 | /* FALLTHROUGH */ |
070c62a6 | 1651 | case MDOC_Lp: |
80387638 | 1652 | /* FALLTHROUGH */ |
070c62a6 | 1653 | case MDOC_Pp: |
80387638 SW |
1654 | continue; |
1655 | default: | |
1656 | break; | |
1657 | } | |
1658 | if (nn->next && nn->next->line == nn->line) | |
1659 | continue; | |
1660 | term_flushln(p); | |
1661 | p->flags |= TERMP_NOSPACE; | |
1662 | } | |
1663 | ||
1664 | p->tabwidth = tabwidth; | |
1665 | p->rmargin = rm; | |
1666 | p->maxrmargin = rmax; | |
1667 | return(0); | |
1668 | } | |
1669 | ||
80387638 SW |
1670 | static void |
1671 | termp_bd_post(DECL_ARGS) | |
1672 | { | |
1673 | size_t rm, rmax; | |
1674 | ||
070c62a6 | 1675 | if (MDOC_BODY != n->type) |
80387638 SW |
1676 | return; |
1677 | ||
1678 | rm = p->rmargin; | |
1679 | rmax = p->maxrmargin; | |
1680 | ||
070c62a6 FF |
1681 | if (DISP_literal == n->norm->Bd.type || |
1682 | DISP_unfilled == n->norm->Bd.type) | |
80387638 SW |
1683 | p->rmargin = p->maxrmargin = TERM_MAXMARGIN; |
1684 | ||
1685 | p->flags |= TERMP_NOSPACE; | |
1686 | term_newln(p); | |
1687 | ||
1688 | p->rmargin = rm; | |
1689 | p->maxrmargin = rmax; | |
1690 | } | |
1691 | ||
60e1e752 SW |
1692 | static int |
1693 | termp_bx_pre(DECL_ARGS) | |
80387638 SW |
1694 | { |
1695 | ||
60e1e752 SW |
1696 | if (NULL != (n = n->child)) { |
1697 | term_word(p, n->string); | |
80387638 | 1698 | p->flags |= TERMP_NOSPACE; |
60e1e752 SW |
1699 | term_word(p, "BSD"); |
1700 | } else { | |
1701 | term_word(p, "BSD"); | |
1702 | return(0); | |
1703 | } | |
1704 | ||
1705 | if (NULL != (n = n->next)) { | |
1706 | p->flags |= TERMP_NOSPACE; | |
1707 | term_word(p, "-"); | |
1708 | p->flags |= TERMP_NOSPACE; | |
1709 | term_word(p, n->string); | |
1710 | } | |
1711 | ||
1712 | return(0); | |
80387638 SW |
1713 | } |
1714 | ||
80387638 SW |
1715 | static int |
1716 | termp_xx_pre(DECL_ARGS) | |
1717 | { | |
1718 | const char *pp; | |
60e1e752 | 1719 | int flags; |
80387638 SW |
1720 | |
1721 | pp = NULL; | |
1722 | switch (n->tok) { | |
070c62a6 | 1723 | case MDOC_Bsx: |
80387638 SW |
1724 | pp = "BSD/OS"; |
1725 | break; | |
070c62a6 | 1726 | case MDOC_Dx: |
80387638 SW |
1727 | pp = "DragonFly"; |
1728 | break; | |
070c62a6 | 1729 | case MDOC_Fx: |
80387638 SW |
1730 | pp = "FreeBSD"; |
1731 | break; | |
070c62a6 | 1732 | case MDOC_Nx: |
80387638 SW |
1733 | pp = "NetBSD"; |
1734 | break; | |
070c62a6 | 1735 | case MDOC_Ox: |
80387638 SW |
1736 | pp = "OpenBSD"; |
1737 | break; | |
070c62a6 | 1738 | case MDOC_Ux: |
80387638 SW |
1739 | pp = "UNIX"; |
1740 | break; | |
1741 | default: | |
f88b6c16 FF |
1742 | abort(); |
1743 | /* NOTREACHED */ | |
80387638 SW |
1744 | } |
1745 | ||
80387638 | 1746 | term_word(p, pp); |
60e1e752 SW |
1747 | if (n->child) { |
1748 | flags = p->flags; | |
1749 | p->flags |= TERMP_KEEP; | |
1750 | term_word(p, n->child->string); | |
1751 | p->flags = flags; | |
1752 | } | |
1753 | return(0); | |
80387638 SW |
1754 | } |
1755 | ||
80387638 SW |
1756 | static void |
1757 | termp_pf_post(DECL_ARGS) | |
1758 | { | |
1759 | ||
1760 | p->flags |= TERMP_NOSPACE; | |
1761 | } | |
1762 | ||
80387638 SW |
1763 | static int |
1764 | termp_ss_pre(DECL_ARGS) | |
1765 | { | |
1766 | ||
1767 | switch (n->type) { | |
070c62a6 | 1768 | case MDOC_BLOCK: |
80387638 SW |
1769 | term_newln(p); |
1770 | if (n->prev) | |
1771 | term_vspace(p); | |
1772 | break; | |
070c62a6 | 1773 | case MDOC_HEAD: |
80387638 | 1774 | term_fontpush(p, TERMFONT_BOLD); |
36342e81 | 1775 | p->offset = term_len(p, (p->defindent+1)/2); |
80387638 | 1776 | break; |
070c62a6 FF |
1777 | case MDOC_BODY: |
1778 | p->offset = term_len(p, p->defindent); | |
1779 | break; | |
80387638 SW |
1780 | default: |
1781 | break; | |
1782 | } | |
1783 | ||
1784 | return(1); | |
1785 | } | |
1786 | ||
80387638 SW |
1787 | static void |
1788 | termp_ss_post(DECL_ARGS) | |
1789 | { | |
1790 | ||
070c62a6 | 1791 | if (n->type == MDOC_HEAD || n->type == MDOC_BODY) |
80387638 SW |
1792 | term_newln(p); |
1793 | } | |
1794 | ||
80387638 SW |
1795 | static int |
1796 | termp_cd_pre(DECL_ARGS) | |
1797 | { | |
1798 | ||
1799 | synopsis_pre(p, n); | |
1800 | term_fontpush(p, TERMFONT_BOLD); | |
1801 | return(1); | |
1802 | } | |
1803 | ||
80387638 SW |
1804 | static int |
1805 | termp_in_pre(DECL_ARGS) | |
1806 | { | |
1807 | ||
1808 | synopsis_pre(p, n); | |
1809 | ||
1810 | if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) { | |
1811 | term_fontpush(p, TERMFONT_BOLD); | |
1812 | term_word(p, "#include"); | |
1813 | term_word(p, "<"); | |
1814 | } else { | |
1815 | term_word(p, "<"); | |
1816 | term_fontpush(p, TERMFONT_UNDER); | |
1817 | } | |
1818 | ||
1819 | p->flags |= TERMP_NOSPACE; | |
1820 | return(1); | |
1821 | } | |
1822 | ||
80387638 SW |
1823 | static void |
1824 | termp_in_post(DECL_ARGS) | |
1825 | { | |
1826 | ||
1827 | if (MDOC_SYNPRETTY & n->flags) | |
1828 | term_fontpush(p, TERMFONT_BOLD); | |
1829 | ||
1830 | p->flags |= TERMP_NOSPACE; | |
1831 | term_word(p, ">"); | |
1832 | ||
1833 | if (MDOC_SYNPRETTY & n->flags) | |
1834 | term_fontpop(p); | |
1835 | } | |
1836 | ||
80387638 SW |
1837 | static int |
1838 | termp_sp_pre(DECL_ARGS) | |
1839 | { | |
1840 | size_t i, len; | |
1841 | ||
1842 | switch (n->tok) { | |
070c62a6 | 1843 | case MDOC_sp: |
80387638 SW |
1844 | len = n->child ? a2height(p, n->child->string) : 1; |
1845 | break; | |
070c62a6 | 1846 | case MDOC_br: |
80387638 SW |
1847 | len = 0; |
1848 | break; | |
1849 | default: | |
1850 | len = 1; | |
1851 | break; | |
1852 | } | |
1853 | ||
1854 | if (0 == len) | |
1855 | term_newln(p); | |
1856 | for (i = 0; i < len; i++) | |
1857 | term_vspace(p); | |
1858 | ||
1859 | return(0); | |
1860 | } | |
1861 | ||
070c62a6 FF |
1862 | static int |
1863 | termp_es_pre(DECL_ARGS) | |
1864 | { | |
1865 | ||
1866 | return(0); | |
1867 | } | |
80387638 | 1868 | |
80387638 SW |
1869 | static int |
1870 | termp_quote_pre(DECL_ARGS) | |
1871 | { | |
1872 | ||
1873 | if (MDOC_BODY != n->type && MDOC_ELEM != n->type) | |
1874 | return(1); | |
1875 | ||
1876 | switch (n->tok) { | |
070c62a6 | 1877 | case MDOC_Ao: |
80387638 | 1878 | /* FALLTHROUGH */ |
070c62a6 | 1879 | case MDOC_Aq: |
80387638 SW |
1880 | term_word(p, "<"); |
1881 | break; | |
070c62a6 | 1882 | case MDOC_Bro: |
80387638 | 1883 | /* FALLTHROUGH */ |
070c62a6 | 1884 | case MDOC_Brq: |
80387638 SW |
1885 | term_word(p, "{"); |
1886 | break; | |
070c62a6 | 1887 | case MDOC_Oo: |
80387638 | 1888 | /* FALLTHROUGH */ |
070c62a6 | 1889 | case MDOC_Op: |
80387638 | 1890 | /* FALLTHROUGH */ |
070c62a6 | 1891 | case MDOC_Bo: |
80387638 | 1892 | /* FALLTHROUGH */ |
070c62a6 | 1893 | case MDOC_Bq: |
80387638 SW |
1894 | term_word(p, "["); |
1895 | break; | |
070c62a6 | 1896 | case MDOC_Do: |
80387638 | 1897 | /* FALLTHROUGH */ |
070c62a6 | 1898 | case MDOC_Dq: |
f88b6c16 | 1899 | term_word(p, "\\(lq"); |
80387638 | 1900 | break; |
070c62a6 FF |
1901 | case MDOC_En: |
1902 | if (NULL == n->norm->Es || | |
1903 | NULL == n->norm->Es->child) | |
1904 | return(1); | |
1905 | term_word(p, n->norm->Es->child->string); | |
1906 | break; | |
1907 | case MDOC_Eo: | |
36342e81 | 1908 | break; |
070c62a6 | 1909 | case MDOC_Po: |
80387638 | 1910 | /* FALLTHROUGH */ |
070c62a6 | 1911 | case MDOC_Pq: |
80387638 SW |
1912 | term_word(p, "("); |
1913 | break; | |
070c62a6 | 1914 | case MDOC__T: |
80387638 | 1915 | /* FALLTHROUGH */ |
070c62a6 | 1916 | case MDOC_Qo: |
80387638 | 1917 | /* FALLTHROUGH */ |
070c62a6 | 1918 | case MDOC_Qq: |
80387638 SW |
1919 | term_word(p, "\""); |
1920 | break; | |
070c62a6 | 1921 | case MDOC_Ql: |
80387638 | 1922 | /* FALLTHROUGH */ |
070c62a6 | 1923 | case MDOC_So: |
80387638 | 1924 | /* FALLTHROUGH */ |
070c62a6 | 1925 | case MDOC_Sq: |
f88b6c16 | 1926 | term_word(p, "\\(oq"); |
80387638 SW |
1927 | break; |
1928 | default: | |
1929 | abort(); | |
1930 | /* NOTREACHED */ | |
1931 | } | |
1932 | ||
1933 | p->flags |= TERMP_NOSPACE; | |
1934 | return(1); | |
1935 | } | |
1936 | ||
80387638 SW |
1937 | static void |
1938 | termp_quote_post(DECL_ARGS) | |
1939 | { | |
1940 | ||
1941 | if (MDOC_BODY != n->type && MDOC_ELEM != n->type) | |
1942 | return; | |
1943 | ||
070c62a6 FF |
1944 | if (MDOC_En != n->tok) |
1945 | p->flags |= TERMP_NOSPACE; | |
80387638 SW |
1946 | |
1947 | switch (n->tok) { | |
070c62a6 | 1948 | case MDOC_Ao: |
80387638 | 1949 | /* FALLTHROUGH */ |
070c62a6 | 1950 | case MDOC_Aq: |
80387638 SW |
1951 | term_word(p, ">"); |
1952 | break; | |
070c62a6 | 1953 | case MDOC_Bro: |
80387638 | 1954 | /* FALLTHROUGH */ |
070c62a6 | 1955 | case MDOC_Brq: |
80387638 SW |
1956 | term_word(p, "}"); |
1957 | break; | |
070c62a6 | 1958 | case MDOC_Oo: |
80387638 | 1959 | /* FALLTHROUGH */ |
070c62a6 | 1960 | case MDOC_Op: |
80387638 | 1961 | /* FALLTHROUGH */ |
070c62a6 | 1962 | case MDOC_Bo: |
80387638 | 1963 | /* FALLTHROUGH */ |
070c62a6 | 1964 | case MDOC_Bq: |
80387638 SW |
1965 | term_word(p, "]"); |
1966 | break; | |
070c62a6 | 1967 | case MDOC_Do: |
80387638 | 1968 | /* FALLTHROUGH */ |
070c62a6 | 1969 | case MDOC_Dq: |
f88b6c16 | 1970 | term_word(p, "\\(rq"); |
80387638 | 1971 | break; |
070c62a6 FF |
1972 | case MDOC_En: |
1973 | if (NULL != n->norm->Es && | |
1974 | NULL != n->norm->Es->child && | |
1975 | NULL != n->norm->Es->child->next) { | |
1976 | p->flags |= TERMP_NOSPACE; | |
1977 | term_word(p, n->norm->Es->child->next->string); | |
1978 | } | |
1979 | break; | |
1980 | case MDOC_Eo: | |
36342e81 | 1981 | break; |
070c62a6 | 1982 | case MDOC_Po: |
80387638 | 1983 | /* FALLTHROUGH */ |
070c62a6 | 1984 | case MDOC_Pq: |
80387638 SW |
1985 | term_word(p, ")"); |
1986 | break; | |
070c62a6 | 1987 | case MDOC__T: |
80387638 | 1988 | /* FALLTHROUGH */ |
070c62a6 | 1989 | case MDOC_Qo: |
80387638 | 1990 | /* FALLTHROUGH */ |
070c62a6 | 1991 | case MDOC_Qq: |
80387638 SW |
1992 | term_word(p, "\""); |
1993 | break; | |
070c62a6 | 1994 | case MDOC_Ql: |
80387638 | 1995 | /* FALLTHROUGH */ |
070c62a6 | 1996 | case MDOC_So: |
80387638 | 1997 | /* FALLTHROUGH */ |
070c62a6 | 1998 | case MDOC_Sq: |
f88b6c16 | 1999 | term_word(p, "\\(cq"); |
80387638 SW |
2000 | break; |
2001 | default: | |
2002 | abort(); | |
2003 | /* NOTREACHED */ | |
2004 | } | |
2005 | } | |
2006 | ||
80387638 SW |
2007 | static int |
2008 | termp_fo_pre(DECL_ARGS) | |
2009 | { | |
7888c61d FF |
2010 | size_t rmargin = 0; |
2011 | int pretty; | |
2012 | ||
2013 | pretty = MDOC_SYNPRETTY & n->flags; | |
80387638 SW |
2014 | |
2015 | if (MDOC_BLOCK == n->type) { | |
2016 | synopsis_pre(p, n); | |
2017 | return(1); | |
2018 | } else if (MDOC_BODY == n->type) { | |
7888c61d FF |
2019 | if (pretty) { |
2020 | rmargin = p->rmargin; | |
2021 | p->rmargin = p->offset + term_len(p, 4); | |
070c62a6 FF |
2022 | p->flags |= TERMP_NOBREAK | TERMP_BRIND | |
2023 | TERMP_HANG; | |
7888c61d | 2024 | } |
80387638 SW |
2025 | p->flags |= TERMP_NOSPACE; |
2026 | term_word(p, "("); | |
60e1e752 | 2027 | p->flags |= TERMP_NOSPACE; |
7888c61d FF |
2028 | if (pretty) { |
2029 | term_flushln(p); | |
070c62a6 FF |
2030 | p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | |
2031 | TERMP_HANG); | |
7888c61d FF |
2032 | p->offset = p->rmargin; |
2033 | p->rmargin = rmargin; | |
2034 | } | |
80387638 | 2035 | return(1); |
7888c61d | 2036 | } |
80387638 SW |
2037 | |
2038 | if (NULL == n->child) | |
2039 | return(0); | |
2040 | ||
2041 | /* XXX: we drop non-initial arguments as per groff. */ | |
2042 | ||
2043 | assert(n->child->string); | |
2044 | term_fontpush(p, TERMFONT_BOLD); | |
2045 | term_word(p, n->child->string); | |
2046 | return(0); | |
2047 | } | |
2048 | ||
80387638 SW |
2049 | static void |
2050 | termp_fo_post(DECL_ARGS) | |
2051 | { | |
2052 | ||
070c62a6 | 2053 | if (MDOC_BODY != n->type) |
80387638 SW |
2054 | return; |
2055 | ||
60e1e752 | 2056 | p->flags |= TERMP_NOSPACE; |
80387638 SW |
2057 | term_word(p, ")"); |
2058 | ||
60e1e752 SW |
2059 | if (MDOC_SYNPRETTY & n->flags) { |
2060 | p->flags |= TERMP_NOSPACE; | |
80387638 | 2061 | term_word(p, ";"); |
7888c61d | 2062 | term_flushln(p); |
60e1e752 | 2063 | } |
80387638 SW |
2064 | } |
2065 | ||
80387638 SW |
2066 | static int |
2067 | termp_bf_pre(DECL_ARGS) | |
2068 | { | |
2069 | ||
2070 | if (MDOC_HEAD == n->type) | |
2071 | return(0); | |
f88b6c16 | 2072 | else if (MDOC_BODY != n->type) |
80387638 SW |
2073 | return(1); |
2074 | ||
070c62a6 | 2075 | if (FONT_Em == n->norm->Bf.font) |
80387638 | 2076 | term_fontpush(p, TERMFONT_UNDER); |
070c62a6 | 2077 | else if (FONT_Sy == n->norm->Bf.font) |
80387638 | 2078 | term_fontpush(p, TERMFONT_BOLD); |
070c62a6 | 2079 | else |
80387638 SW |
2080 | term_fontpush(p, TERMFONT_NONE); |
2081 | ||
2082 | return(1); | |
2083 | } | |
2084 | ||
80387638 SW |
2085 | static int |
2086 | termp_sm_pre(DECL_ARGS) | |
2087 | { | |
2088 | ||
070c62a6 FF |
2089 | if (NULL == n->child) |
2090 | p->flags ^= TERMP_NONOSPACE; | |
2091 | else if (0 == strcmp("on", n->child->string)) | |
80387638 | 2092 | p->flags &= ~TERMP_NONOSPACE; |
070c62a6 | 2093 | else |
80387638 SW |
2094 | p->flags |= TERMP_NONOSPACE; |
2095 | ||
070c62a6 FF |
2096 | if (p->col && ! (TERMP_NONOSPACE & p->flags)) |
2097 | p->flags &= ~TERMP_NOSPACE; | |
2098 | ||
80387638 SW |
2099 | return(0); |
2100 | } | |
2101 | ||
80387638 SW |
2102 | static int |
2103 | termp_ap_pre(DECL_ARGS) | |
2104 | { | |
2105 | ||
2106 | p->flags |= TERMP_NOSPACE; | |
2107 | term_word(p, "'"); | |
2108 | p->flags |= TERMP_NOSPACE; | |
2109 | return(1); | |
2110 | } | |
2111 | ||
80387638 SW |
2112 | static void |
2113 | termp____post(DECL_ARGS) | |
2114 | { | |
2115 | ||
2116 | /* | |
2117 | * Handle lists of authors. In general, print each followed by | |
2118 | * a comma. Don't print the comma if there are only two | |
2119 | * authors. | |
2120 | */ | |
2121 | if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok) | |
2122 | if (NULL == n->next->next || MDOC__A != n->next->next->tok) | |
2123 | if (NULL == n->prev || MDOC__A != n->prev->tok) | |
2124 | return; | |
2125 | ||
2126 | /* TODO: %U. */ | |
2127 | ||
2128 | if (NULL == n->parent || MDOC_Rs != n->parent->tok) | |
2129 | return; | |
2130 | ||
60e1e752 | 2131 | p->flags |= TERMP_NOSPACE; |
80387638 SW |
2132 | if (NULL == n->next) { |
2133 | term_word(p, "."); | |
2134 | p->flags |= TERMP_SENTENCE; | |
2135 | } else | |
2136 | term_word(p, ","); | |
2137 | } | |
2138 | ||
80387638 SW |
2139 | static int |
2140 | termp_li_pre(DECL_ARGS) | |
2141 | { | |
2142 | ||
2143 | term_fontpush(p, TERMFONT_NONE); | |
2144 | return(1); | |
2145 | } | |
2146 | ||
80387638 SW |
2147 | static int |
2148 | termp_lk_pre(DECL_ARGS) | |
2149 | { | |
f88b6c16 | 2150 | const struct mdoc_node *link, *descr; |
80387638 | 2151 | |
f88b6c16 FF |
2152 | if (NULL == (link = n->child)) |
2153 | return(0); | |
80387638 | 2154 | |
f88b6c16 FF |
2155 | if (NULL != (descr = link->next)) { |
2156 | term_fontpush(p, TERMFONT_UNDER); | |
2157 | while (NULL != descr) { | |
2158 | term_word(p, descr->string); | |
2159 | descr = descr->next; | |
2160 | } | |
2161 | p->flags |= TERMP_NOSPACE; | |
2162 | term_word(p, ":"); | |
2163 | term_fontpop(p); | |
2164 | } | |
80387638 SW |
2165 | |
2166 | term_fontpush(p, TERMFONT_BOLD); | |
f88b6c16 | 2167 | term_word(p, link->string); |
80387638 SW |
2168 | term_fontpop(p); |
2169 | ||
2170 | return(0); | |
2171 | } | |
2172 | ||
80387638 SW |
2173 | static int |
2174 | termp_bk_pre(DECL_ARGS) | |
2175 | { | |
2176 | ||
2177 | switch (n->type) { | |
070c62a6 | 2178 | case MDOC_BLOCK: |
80387638 | 2179 | break; |
070c62a6 | 2180 | case MDOC_HEAD: |
80387638 | 2181 | return(0); |
070c62a6 | 2182 | case MDOC_BODY: |
80387638 SW |
2183 | if (n->parent->args || 0 == n->prev->nchild) |
2184 | p->flags |= TERMP_PREKEEP; | |
2185 | break; | |
2186 | default: | |
2187 | abort(); | |
2188 | /* NOTREACHED */ | |
2189 | } | |
2190 | ||
2191 | return(1); | |
2192 | } | |
2193 | ||
80387638 SW |
2194 | static void |
2195 | termp_bk_post(DECL_ARGS) | |
2196 | { | |
2197 | ||
7888c61d | 2198 | if (MDOC_BODY == n->type) |
80387638 SW |
2199 | p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP); |
2200 | } | |
2201 | ||
80387638 SW |
2202 | static void |
2203 | termp__t_post(DECL_ARGS) | |
2204 | { | |
2205 | ||
2206 | /* | |
2207 | * If we're in an `Rs' and there's a journal present, then quote | |
2208 | * us instead of underlining us (for disambiguation). | |
2209 | */ | |
60e1e752 | 2210 | if (n->parent && MDOC_Rs == n->parent->tok && |
070c62a6 | 2211 | n->parent->norm->Rs.quote_T) |
f88b6c16 | 2212 | termp_quote_post(p, pair, meta, n); |
80387638 | 2213 | |
f88b6c16 | 2214 | termp____post(p, pair, meta, n); |
80387638 SW |
2215 | } |
2216 | ||
80387638 SW |
2217 | static int |
2218 | termp__t_pre(DECL_ARGS) | |
2219 | { | |
2220 | ||
2221 | /* | |
2222 | * If we're in an `Rs' and there's a journal present, then quote | |
2223 | * us instead of underlining us (for disambiguation). | |
2224 | */ | |
2225 | if (n->parent && MDOC_Rs == n->parent->tok && | |
070c62a6 | 2226 | n->parent->norm->Rs.quote_T) |
f88b6c16 | 2227 | return(termp_quote_pre(p, pair, meta, n)); |
80387638 SW |
2228 | |
2229 | term_fontpush(p, TERMFONT_UNDER); | |
2230 | return(1); | |
2231 | } | |
2232 | ||
80387638 SW |
2233 | static int |
2234 | termp_under_pre(DECL_ARGS) | |
2235 | { | |
2236 | ||
2237 | term_fontpush(p, TERMFONT_UNDER); | |
2238 | return(1); | |
2239 | } |