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