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