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