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