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