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