]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
Added Longsoon architecture (thanks Jason McIntyre, Ingo Schwarze).
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.110 2010/01/30 08:42:21 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_bold_pre(DECL_ARGS);
103 static int termp_bq_pre(DECL_ARGS);
104 static int termp_brq_pre(DECL_ARGS);
105 static int termp_bt_pre(DECL_ARGS);
106 static int termp_cd_pre(DECL_ARGS);
107 static int termp_d1_pre(DECL_ARGS);
108 static int termp_dq_pre(DECL_ARGS);
109 static int termp_ex_pre(DECL_ARGS);
110 static int termp_fa_pre(DECL_ARGS);
111 static int termp_fl_pre(DECL_ARGS);
112 static int termp_fn_pre(DECL_ARGS);
113 static int termp_fo_pre(DECL_ARGS);
114 static int termp_ft_pre(DECL_ARGS);
115 static int termp_in_pre(DECL_ARGS);
116 static int termp_it_pre(DECL_ARGS);
117 static int termp_li_pre(DECL_ARGS);
118 static int termp_lk_pre(DECL_ARGS);
119 static int termp_nd_pre(DECL_ARGS);
120 static int termp_nm_pre(DECL_ARGS);
121 static int termp_ns_pre(DECL_ARGS);
122 static int termp_op_pre(DECL_ARGS);
123 static int termp_pf_pre(DECL_ARGS);
124 static int termp_pq_pre(DECL_ARGS);
125 static int termp_qq_pre(DECL_ARGS);
126 static int termp_rs_pre(DECL_ARGS);
127 static int termp_rv_pre(DECL_ARGS);
128 static int termp_sh_pre(DECL_ARGS);
129 static int termp_sm_pre(DECL_ARGS);
130 static int termp_sp_pre(DECL_ARGS);
131 static int termp_sq_pre(DECL_ARGS);
132 static int termp_ss_pre(DECL_ARGS);
133 static int termp_under_pre(DECL_ARGS);
134 static int termp_ud_pre(DECL_ARGS);
135 static int termp_vt_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_vt_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 return(term_hspan(&su));
474 }
475
476
477 static int
478 arg_listtype(const struct mdoc_node *n)
479 {
480 int i, len;
481
482 assert(MDOC_BLOCK == n->type);
483
484 len = (int)(n->args ? n->args->argc : 0);
485
486 for (i = 0; i < len; i++)
487 switch (n->args->argv[i].arg) {
488 case (MDOC_Bullet):
489 /* FALLTHROUGH */
490 case (MDOC_Dash):
491 /* FALLTHROUGH */
492 case (MDOC_Enum):
493 /* FALLTHROUGH */
494 case (MDOC_Hyphen):
495 /* FALLTHROUGH */
496 case (MDOC_Tag):
497 /* FALLTHROUGH */
498 case (MDOC_Inset):
499 /* FALLTHROUGH */
500 case (MDOC_Diag):
501 /* FALLTHROUGH */
502 case (MDOC_Item):
503 /* FALLTHROUGH */
504 case (MDOC_Column):
505 /* FALLTHROUGH */
506 case (MDOC_Hang):
507 /* FALLTHROUGH */
508 case (MDOC_Ohang):
509 return(n->args->argv[i].arg);
510 default:
511 break;
512 }
513
514 return(-1);
515 }
516
517
518 static size_t
519 a2offs(const struct mdoc_argv *arg)
520 {
521 struct roffsu su;
522
523 if ('\0' == arg->value[0][0])
524 return(0);
525 else if (0 == strcmp(arg->value[0], "left"))
526 return(0);
527 else if (0 == strcmp(arg->value[0], "indent"))
528 return(INDENT + 1);
529 else if (0 == strcmp(arg->value[0], "indent-two"))
530 return((INDENT + 1) * 2);
531 else if ( ! a2roffsu(arg->value[0], &su, SCALE_MAX))
532 SCALE_HS_INIT(&su, strlen(arg->value[0]));
533
534 return(term_hspan(&su));
535 }
536
537
538 /*
539 * Return 1 if an argument has a particular argument value or 0 if it
540 * does not. See arg_getattr().
541 */
542 static int
543 arg_hasattr(int arg, const struct mdoc_node *n)
544 {
545
546 return(-1 != arg_getattr(arg, n));
547 }
548
549
550 /*
551 * Get the index of an argument in a node's argument list or -1 if it
552 * does not exist. See arg_getattrs().
553 */
554 static int
555 arg_getattr(int v, const struct mdoc_node *n)
556 {
557 int val;
558
559 return(arg_getattrs(&v, &val, 1, n) ? val : -1);
560 }
561
562
563 /*
564 * Walk through the argument list for a node and fill an array "vals"
565 * with the positions of the argument structures listed in "keys".
566 * Return the number of elements that were written into "vals", which
567 * can be zero.
568 */
569 static int
570 arg_getattrs(const int *keys, int *vals,
571 size_t sz, const struct mdoc_node *n)
572 {
573 int i, j, k;
574
575 if (NULL == n->args)
576 return(0);
577
578 for (k = i = 0; i < (int)n->args->argc; i++)
579 for (j = 0; j < (int)sz; j++)
580 if (n->args->argv[i].arg == keys[j]) {
581 vals[j] = i;
582 k++;
583 }
584 return(k);
585 }
586
587
588 /*
589 * Determine how much space to print out before block elements of `It'
590 * (and thus `Bl') and `Bd'. And then go ahead and print that space,
591 * too.
592 */
593 static void
594 print_bvspace(struct termp *p,
595 const struct mdoc_node *bl,
596 const struct mdoc_node *n)
597 {
598 const struct mdoc_node *nn;
599
600 term_newln(p);
601 if (arg_hasattr(MDOC_Compact, bl))
602 return;
603
604 /* Do not vspace directly after Ss/Sh. */
605
606 for (nn = n; nn; nn = nn->parent) {
607 if (MDOC_BLOCK != nn->type)
608 continue;
609 if (MDOC_Ss == nn->tok)
610 return;
611 if (MDOC_Sh == nn->tok)
612 return;
613 if (NULL == nn->prev)
614 continue;
615 break;
616 }
617
618 /* A `-column' does not assert vspace within the list. */
619
620 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Column, bl))
621 if (n->prev && MDOC_It == n->prev->tok)
622 return;
623
624 /* A `-diag' without body does not vspace. */
625
626 if (MDOC_Bl == bl->tok && arg_hasattr(MDOC_Diag, bl))
627 if (n->prev && MDOC_It == n->prev->tok) {
628 assert(n->prev->body);
629 if (NULL == n->prev->body->child)
630 return;
631 }
632
633 term_vspace(p);
634 }
635
636
637 /* ARGSUSED */
638 static int
639 termp_dq_pre(DECL_ARGS)
640 {
641
642 if (MDOC_BODY != n->type)
643 return(1);
644
645 term_word(p, "\\(lq");
646 p->flags |= TERMP_NOSPACE;
647 return(1);
648 }
649
650
651 /* ARGSUSED */
652 static void
653 termp_dq_post(DECL_ARGS)
654 {
655
656 if (MDOC_BODY != n->type)
657 return;
658
659 p->flags |= TERMP_NOSPACE;
660 term_word(p, "\\(rq");
661 }
662
663
664 /* ARGSUSED */
665 static int
666 termp_it_pre(DECL_ARGS)
667 {
668 const struct mdoc_node *bl, *nn;
669 char buf[7];
670 int i, type, keys[3], vals[3];
671 size_t width, offset, ncols, dcol;
672
673 if (MDOC_BLOCK == n->type) {
674 print_bvspace(p, n->parent->parent, n);
675 return(1);
676 }
677
678 bl = n->parent->parent->parent;
679
680 /* Get list width, offset, and list type from argument list. */
681
682 keys[0] = MDOC_Width;
683 keys[1] = MDOC_Offset;
684 keys[2] = MDOC_Column;
685
686 vals[0] = vals[1] = vals[2] = -1;
687
688 arg_getattrs(keys, vals, 3, bl);
689
690 type = arg_listtype(bl);
691 assert(-1 != type);
692
693 /*
694 * First calculate width and offset. This is pretty easy unless
695 * we're a -column list, in which case all prior columns must
696 * be accounted for.
697 */
698
699 width = offset = 0;
700
701 if (vals[1] >= 0)
702 offset = a2offs(&bl->args->argv[vals[1]]);
703
704 switch (type) {
705 case (MDOC_Column):
706 if (MDOC_BODY == n->type)
707 break;
708 /*
709 * Imitate groff's column handling:
710 * - For each earlier column, add its width.
711 * - For less than 5 columns, add four more blanks per
712 * column.
713 * - For exactly 5 columns, add three more blank per
714 * column.
715 * - For more than 5 columns, add only one column.
716 */
717 ncols = bl->args->argv[vals[2]].sz;
718 /* LINTED */
719 dcol = ncols < 5 ? 4 : ncols == 5 ? 3 : 1;
720
721 for (i = 0, nn = n->prev;
722 nn && i < (int)ncols;
723 nn = nn->prev, i++)
724 offset += dcol + a2width
725 (&bl->args->argv[vals[2]], i);
726
727
728 /*
729 * When exceeding the declared number of columns, leave
730 * the remaining widths at 0. This will later be
731 * adjusted to the default width of 10, or, for the last
732 * column, stretched to the right margin.
733 */
734 if (i >= (int)ncols)
735 break;
736
737 /*
738 * Use the declared column widths, extended as explained
739 * in the preceding paragraph.
740 */
741 width = a2width(&bl->args->argv[vals[2]], i) + dcol;
742 break;
743 default:
744 if (vals[0] < 0)
745 break;
746
747 /*
748 * Note: buffer the width by 2, which is groff's magic
749 * number for buffering single arguments. See the above
750 * handling for column for how this changes.
751 */
752 width = a2width(&bl->args->argv[vals[0]], 0) + 2;
753 break;
754 }
755
756 /*
757 * List-type can override the width in the case of fixed-head
758 * values (bullet, dash/hyphen, enum). Tags need a non-zero
759 * offset.
760 */
761
762 switch (type) {
763 case (MDOC_Bullet):
764 /* FALLTHROUGH */
765 case (MDOC_Dash):
766 /* FALLTHROUGH */
767 case (MDOC_Hyphen):
768 if (width < 4)
769 width = 4;
770 break;
771 case (MDOC_Enum):
772 if (width < 5)
773 width = 5;
774 break;
775 case (MDOC_Hang):
776 if (0 == width)
777 width = 8;
778 break;
779 case (MDOC_Column):
780 /* FALLTHROUGH */
781 case (MDOC_Tag):
782 if (0 == width)
783 width = 10;
784 break;
785 default:
786 break;
787 }
788
789 /*
790 * Whitespace control. Inset bodies need an initial space,
791 * while diagonal bodies need two.
792 */
793
794 p->flags |= TERMP_NOSPACE;
795
796 switch (type) {
797 case (MDOC_Diag):
798 if (MDOC_BODY == n->type)
799 term_word(p, "\\ \\ ");
800 break;
801 case (MDOC_Inset):
802 if (MDOC_BODY == n->type)
803 term_word(p, "\\ ");
804 break;
805 default:
806 break;
807 }
808
809 p->flags |= TERMP_NOSPACE;
810
811 switch (type) {
812 case (MDOC_Diag):
813 if (MDOC_HEAD == n->type)
814 term_fontpush(p, TERMFONT_BOLD);
815 break;
816 default:
817 break;
818 }
819
820 /*
821 * Pad and break control. This is the tricky part. These flags
822 * are documented in term_flushln() in term.c. Note that we're
823 * going to unset all of these flags in termp_it_post() when we
824 * exit.
825 */
826
827 switch (type) {
828 case (MDOC_Bullet):
829 /* FALLTHROUGH */
830 case (MDOC_Dash):
831 /* FALLTHROUGH */
832 case (MDOC_Enum):
833 /* FALLTHROUGH */
834 case (MDOC_Hyphen):
835 if (MDOC_HEAD == n->type)
836 p->flags |= TERMP_NOBREAK;
837 else
838 p->flags |= TERMP_NOLPAD;
839 break;
840 case (MDOC_Hang):
841 if (MDOC_HEAD == n->type)
842 p->flags |= TERMP_NOBREAK;
843 else
844 p->flags |= TERMP_NOLPAD;
845
846 if (MDOC_HEAD != n->type)
847 break;
848
849 /*
850 * This is ugly. If `-hang' is specified and the body
851 * is a `Bl' or `Bd', then we want basically to nullify
852 * the "overstep" effect in term_flushln() and treat
853 * this as a `-ohang' list instead.
854 */
855 if (n->next->child &&
856 (MDOC_Bl == n->next->child->tok ||
857 MDOC_Bd == n->next->child->tok)) {
858 p->flags &= ~TERMP_NOBREAK;
859 p->flags &= ~TERMP_NOLPAD;
860 } else
861 p->flags |= TERMP_HANG;
862 break;
863 case (MDOC_Tag):
864 if (MDOC_HEAD == n->type)
865 p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
866 else
867 p->flags |= TERMP_NOLPAD;
868
869 if (MDOC_HEAD != n->type)
870 break;
871 if (NULL == n->next || NULL == n->next->child)
872 p->flags |= TERMP_DANGLE;
873 break;
874 case (MDOC_Column):
875 if (MDOC_HEAD == n->type) {
876 assert(n->next);
877 if (MDOC_BODY == n->next->type)
878 p->flags &= ~TERMP_NOBREAK;
879 else
880 p->flags |= TERMP_NOBREAK;
881 if (n->prev)
882 p->flags |= TERMP_NOLPAD;
883 }
884 break;
885 case (MDOC_Diag):
886 if (MDOC_HEAD == n->type)
887 p->flags |= TERMP_NOBREAK;
888 break;
889 default:
890 break;
891 }
892
893 /*
894 * Margin control. Set-head-width lists have their right
895 * margins shortened. The body for these lists has the offset
896 * necessarily lengthened. Everybody gets the offset.
897 */
898
899 p->offset += offset;
900
901 switch (type) {
902 case (MDOC_Hang):
903 /*
904 * Same stipulation as above, regarding `-hang'. We
905 * don't want to recalculate rmargin and offsets when
906 * using `Bd' or `Bl' within `-hang' overstep lists.
907 */
908 if (MDOC_HEAD == n->type && n->next->child &&
909 (MDOC_Bl == n->next->child->tok ||
910 MDOC_Bd == n->next->child->tok))
911 break;
912 /* FALLTHROUGH */
913 case (MDOC_Bullet):
914 /* FALLTHROUGH */
915 case (MDOC_Dash):
916 /* FALLTHROUGH */
917 case (MDOC_Enum):
918 /* FALLTHROUGH */
919 case (MDOC_Hyphen):
920 /* FALLTHROUGH */
921 case (MDOC_Tag):
922 assert(width);
923 if (MDOC_HEAD == n->type)
924 p->rmargin = p->offset + width;
925 else
926 p->offset += width;
927 break;
928 case (MDOC_Column):
929 assert(width);
930 p->rmargin = p->offset + width;
931 /*
932 * XXX - this behaviour is not documented: the
933 * right-most column is filled to the right margin.
934 */
935 if (MDOC_HEAD == n->type &&
936 MDOC_BODY == n->next->type &&
937 p->rmargin < p->maxrmargin)
938 p->rmargin = p->maxrmargin;
939 break;
940 default:
941 break;
942 }
943
944 /*
945 * The dash, hyphen, bullet and enum lists all have a special
946 * HEAD character (temporarily bold, in some cases).
947 */
948
949 if (MDOC_HEAD == n->type)
950 switch (type) {
951 case (MDOC_Bullet):
952 term_fontpush(p, TERMFONT_BOLD);
953 term_word(p, "\\[bu]");
954 term_fontpop(p);
955 break;
956 case (MDOC_Dash):
957 /* FALLTHROUGH */
958 case (MDOC_Hyphen):
959 term_fontpush(p, TERMFONT_BOLD);
960 term_word(p, "\\(hy");
961 term_fontpop(p);
962 break;
963 case (MDOC_Enum):
964 (pair->ppair->ppair->count)++;
965 snprintf(buf, sizeof(buf), "%d.",
966 pair->ppair->ppair->count);
967 term_word(p, buf);
968 break;
969 default:
970 break;
971 }
972
973 /*
974 * If we're not going to process our children, indicate so here.
975 */
976
977 switch (type) {
978 case (MDOC_Bullet):
979 /* FALLTHROUGH */
980 case (MDOC_Item):
981 /* FALLTHROUGH */
982 case (MDOC_Dash):
983 /* FALLTHROUGH */
984 case (MDOC_Hyphen):
985 /* FALLTHROUGH */
986 case (MDOC_Enum):
987 if (MDOC_HEAD == n->type)
988 return(0);
989 break;
990 case (MDOC_Column):
991 if (MDOC_BODY == n->type)
992 return(0);
993 break;
994 default:
995 break;
996 }
997
998 return(1);
999 }
1000
1001
1002 /* ARGSUSED */
1003 static void
1004 termp_it_post(DECL_ARGS)
1005 {
1006 int type;
1007
1008 if (MDOC_BLOCK == n->type)
1009 return;
1010
1011 type = arg_listtype(n->parent->parent->parent);
1012 assert(-1 != type);
1013
1014 switch (type) {
1015 case (MDOC_Item):
1016 /* FALLTHROUGH */
1017 case (MDOC_Diag):
1018 /* FALLTHROUGH */
1019 case (MDOC_Inset):
1020 if (MDOC_BODY == n->type)
1021 term_flushln(p);
1022 break;
1023 case (MDOC_Column):
1024 if (MDOC_HEAD == n->type)
1025 term_flushln(p);
1026 break;
1027 default:
1028 term_flushln(p);
1029 break;
1030 }
1031
1032 /*
1033 * Now that our output is flushed, we can reset our tags. Since
1034 * only `It' sets these flags, we're free to assume that nobody
1035 * has munged them in the meanwhile.
1036 */
1037
1038 p->flags &= ~TERMP_DANGLE;
1039 p->flags &= ~TERMP_NOBREAK;
1040 p->flags &= ~TERMP_TWOSPACE;
1041 p->flags &= ~TERMP_NOLPAD;
1042 p->flags &= ~TERMP_HANG;
1043 }
1044
1045
1046 /* ARGSUSED */
1047 static int
1048 termp_nm_pre(DECL_ARGS)
1049 {
1050
1051 if (SEC_SYNOPSIS == n->sec)
1052 term_newln(p);
1053
1054 term_fontpush(p, TERMFONT_BOLD);
1055
1056 if (NULL == n->child)
1057 term_word(p, m->name);
1058 return(1);
1059 }
1060
1061
1062 /* ARGSUSED */
1063 static int
1064 termp_fl_pre(DECL_ARGS)
1065 {
1066
1067 term_fontpush(p, TERMFONT_BOLD);
1068 term_word(p, "\\-");
1069
1070 /* A blank `Fl' should incur a subsequent space. */
1071
1072 if (n->child)
1073 p->flags |= TERMP_NOSPACE;
1074
1075 return(1);
1076 }
1077
1078
1079 /* ARGSUSED */
1080 static int
1081 termp_an_pre(DECL_ARGS)
1082 {
1083
1084 if (NULL == n->child)
1085 return(1);
1086
1087 /*
1088 * If not in the AUTHORS section, `An -split' will cause
1089 * newlines to occur before the author name. If in the AUTHORS
1090 * section, by default, the first `An' invocation is nosplit,
1091 * then all subsequent ones, regardless of whether interspersed
1092 * with other macros/text, are split. -split, in this case,
1093 * will override the condition of the implied first -nosplit.
1094 */
1095
1096 if (n->sec == SEC_AUTHORS) {
1097 if ( ! (TERMP_ANPREC & p->flags)) {
1098 if (TERMP_SPLIT & p->flags)
1099 term_newln(p);
1100 return(1);
1101 }
1102 if (TERMP_NOSPLIT & p->flags)
1103 return(1);
1104 term_newln(p);
1105 return(1);
1106 }
1107
1108 if (TERMP_SPLIT & p->flags)
1109 term_newln(p);
1110
1111 return(1);
1112 }
1113
1114
1115 /* ARGSUSED */
1116 static void
1117 termp_an_post(DECL_ARGS)
1118 {
1119
1120 if (n->child) {
1121 if (SEC_AUTHORS == n->sec)
1122 p->flags |= TERMP_ANPREC;
1123 return;
1124 }
1125
1126 if (arg_getattr(MDOC_Split, n) > -1) {
1127 p->flags &= ~TERMP_NOSPLIT;
1128 p->flags |= TERMP_SPLIT;
1129 } else {
1130 p->flags &= ~TERMP_SPLIT;
1131 p->flags |= TERMP_NOSPLIT;
1132 }
1133
1134 }
1135
1136
1137 /* ARGSUSED */
1138 static int
1139 termp_ns_pre(DECL_ARGS)
1140 {
1141
1142 p->flags |= TERMP_NOSPACE;
1143 return(1);
1144 }
1145
1146
1147 /* ARGSUSED */
1148 static int
1149 termp_rs_pre(DECL_ARGS)
1150 {
1151
1152 if (SEC_SEE_ALSO != n->sec)
1153 return(1);
1154 if (MDOC_BLOCK == n->type && n->prev)
1155 term_vspace(p);
1156 return(1);
1157 }
1158
1159
1160 /* ARGSUSED */
1161 static int
1162 termp_rv_pre(DECL_ARGS)
1163 {
1164 const struct mdoc_node *nn;
1165
1166 term_newln(p);
1167 term_word(p, "The");
1168
1169 for (nn = n->child; nn; nn = nn->next) {
1170 term_fontpush(p, TERMFONT_BOLD);
1171 term_word(p, nn->string);
1172 term_fontpop(p);
1173 p->flags |= TERMP_NOSPACE;
1174 if (nn->next && NULL == nn->next->next)
1175 term_word(p, "(), and");
1176 else if (nn->next)
1177 term_word(p, "(),");
1178 else
1179 term_word(p, "()");
1180 }
1181
1182 if (n->child->next)
1183 term_word(p, "functions return");
1184 else
1185 term_word(p, "function returns");
1186
1187 term_word(p, "the value 0 if successful; otherwise the value "
1188 "-1 is returned and the global variable");
1189
1190 term_fontpush(p, TERMFONT_UNDER);
1191 term_word(p, "errno");
1192 term_fontpop(p);
1193
1194 term_word(p, "is set to indicate the error.");
1195
1196 return(0);
1197 }
1198
1199
1200 /* ARGSUSED */
1201 static int
1202 termp_ex_pre(DECL_ARGS)
1203 {
1204 const struct mdoc_node *nn;
1205
1206 term_word(p, "The");
1207
1208 for (nn = n->child; nn; nn = nn->next) {
1209 term_fontpush(p, TERMFONT_BOLD);
1210 term_word(p, nn->string);
1211 term_fontpop(p);
1212 p->flags |= TERMP_NOSPACE;
1213 if (nn->next && NULL == nn->next->next)
1214 term_word(p, ", and");
1215 else if (nn->next)
1216 term_word(p, ",");
1217 else
1218 p->flags &= ~TERMP_NOSPACE;
1219 }
1220
1221 if (n->child->next)
1222 term_word(p, "utilities exit");
1223 else
1224 term_word(p, "utility exits");
1225
1226 term_word(p, "0 on success, and >0 if an error occurs.");
1227
1228 return(0);
1229 }
1230
1231
1232 /* ARGSUSED */
1233 static int
1234 termp_nd_pre(DECL_ARGS)
1235 {
1236
1237 if (MDOC_BODY != n->type)
1238 return(1);
1239
1240 #if defined(__OpenBSD__) || defined(__linux__)
1241 term_word(p, "\\(en");
1242 #else
1243 term_word(p, "\\(em");
1244 #endif
1245 return(1);
1246 }
1247
1248
1249 /* ARGSUSED */
1250 static void
1251 termp_bl_post(DECL_ARGS)
1252 {
1253
1254 if (MDOC_BLOCK == n->type)
1255 term_newln(p);
1256 }
1257
1258
1259 /* ARGSUSED */
1260 static void
1261 termp_op_post(DECL_ARGS)
1262 {
1263
1264 if (MDOC_BODY != n->type)
1265 return;
1266 p->flags |= TERMP_NOSPACE;
1267 term_word(p, "\\(rB");
1268 }
1269
1270
1271 /* ARGSUSED */
1272 static int
1273 termp_xr_pre(DECL_ARGS)
1274 {
1275 const struct mdoc_node *nn;
1276
1277 assert(n->child && MDOC_TEXT == n->child->type);
1278 nn = n->child;
1279
1280 term_word(p, nn->string);
1281 if (NULL == (nn = nn->next))
1282 return(0);
1283 p->flags |= TERMP_NOSPACE;
1284 term_word(p, "(");
1285 p->flags |= TERMP_NOSPACE;
1286 term_word(p, nn->string);
1287 p->flags |= TERMP_NOSPACE;
1288 term_word(p, ")");
1289
1290 return(0);
1291 }
1292
1293
1294 static int
1295 termp_vt_pre(DECL_ARGS)
1296 {
1297
1298 if (MDOC_ELEM == n->type)
1299 return(termp_under_pre(p, pair, m, n));
1300 else if (MDOC_HEAD == n->type)
1301 return(0);
1302 else if (MDOC_BLOCK == n->type)
1303 return(1);
1304
1305 return(termp_under_pre(p, pair, m, n));
1306 }
1307
1308
1309 /* ARGSUSED */
1310 static void
1311 termp_vt_post(DECL_ARGS)
1312 {
1313
1314 if (MDOC_BLOCK != n->type)
1315 return;
1316 if (n->next && MDOC_Vt == n->next->tok)
1317 term_newln(p);
1318 else if (n->next)
1319 term_vspace(p);
1320 }
1321
1322
1323 /* ARGSUSED */
1324 static int
1325 termp_bold_pre(DECL_ARGS)
1326 {
1327
1328 term_fontpush(p, TERMFONT_BOLD);
1329 return(1);
1330 }
1331
1332
1333 /* ARGSUSED */
1334 static void
1335 termp_fd_post(DECL_ARGS)
1336 {
1337
1338 if (n->sec != SEC_SYNOPSIS)
1339 return;
1340
1341 term_newln(p);
1342 if (n->next && MDOC_Fd != n->next->tok)
1343 term_vspace(p);
1344 }
1345
1346
1347 /* ARGSUSED */
1348 static int
1349 termp_sh_pre(DECL_ARGS)
1350 {
1351
1352 /* No vspace between consecutive `Sh' calls. */
1353
1354 switch (n->type) {
1355 case (MDOC_BLOCK):
1356 if (n->prev && MDOC_Sh == n->prev->tok)
1357 if (NULL == n->prev->body->child)
1358 break;
1359 term_vspace(p);
1360 break;
1361 case (MDOC_HEAD):
1362 term_fontpush(p, TERMFONT_BOLD);
1363 break;
1364 case (MDOC_BODY):
1365 p->offset = INDENT;
1366 break;
1367 default:
1368 break;
1369 }
1370 return(1);
1371 }
1372
1373
1374 /* ARGSUSED */
1375 static void
1376 termp_sh_post(DECL_ARGS)
1377 {
1378
1379 switch (n->type) {
1380 case (MDOC_HEAD):
1381 term_newln(p);
1382 break;
1383 case (MDOC_BODY):
1384 term_newln(p);
1385 p->offset = 0;
1386 break;
1387 default:
1388 break;
1389 }
1390 }
1391
1392
1393 /* ARGSUSED */
1394 static int
1395 termp_op_pre(DECL_ARGS)
1396 {
1397
1398 switch (n->type) {
1399 case (MDOC_BODY):
1400 term_word(p, "\\(lB");
1401 p->flags |= TERMP_NOSPACE;
1402 break;
1403 default:
1404 break;
1405 }
1406 return(1);
1407 }
1408
1409
1410 /* ARGSUSED */
1411 static int
1412 termp_bt_pre(DECL_ARGS)
1413 {
1414
1415 term_word(p, "is currently in beta test.");
1416 return(0);
1417 }
1418
1419
1420 /* ARGSUSED */
1421 static void
1422 termp_lb_post(DECL_ARGS)
1423 {
1424
1425 if (SEC_LIBRARY == n->sec)
1426 term_newln(p);
1427 }
1428
1429
1430 /* ARGSUSED */
1431 static int
1432 termp_ud_pre(DECL_ARGS)
1433 {
1434
1435 term_word(p, "currently under development.");
1436 return(0);
1437 }
1438
1439
1440 /* ARGSUSED */
1441 static int
1442 termp_d1_pre(DECL_ARGS)
1443 {
1444
1445 if (MDOC_BLOCK != n->type)
1446 return(1);
1447 term_newln(p);
1448 p->offset += (INDENT + 1);
1449 return(1);
1450 }
1451
1452
1453 /* ARGSUSED */
1454 static void
1455 termp_d1_post(DECL_ARGS)
1456 {
1457
1458 if (MDOC_BLOCK != n->type)
1459 return;
1460 term_newln(p);
1461 }
1462
1463
1464 /* ARGSUSED */
1465 static int
1466 termp_aq_pre(DECL_ARGS)
1467 {
1468
1469 if (MDOC_BODY != n->type)
1470 return(1);
1471 term_word(p, "\\(la");
1472 p->flags |= TERMP_NOSPACE;
1473 return(1);
1474 }
1475
1476
1477 /* ARGSUSED */
1478 static void
1479 termp_aq_post(DECL_ARGS)
1480 {
1481
1482 if (MDOC_BODY != n->type)
1483 return;
1484 p->flags |= TERMP_NOSPACE;
1485 term_word(p, "\\(ra");
1486 }
1487
1488
1489 /* ARGSUSED */
1490 static int
1491 termp_ft_pre(DECL_ARGS)
1492 {
1493
1494 if (SEC_SYNOPSIS == n->sec)
1495 if (n->prev && MDOC_Fo == n->prev->tok)
1496 term_vspace(p);
1497
1498 term_fontpush(p, TERMFONT_UNDER);
1499 return(1);
1500 }
1501
1502
1503 /* ARGSUSED */
1504 static void
1505 termp_ft_post(DECL_ARGS)
1506 {
1507
1508 if (SEC_SYNOPSIS == n->sec)
1509 term_newln(p);
1510 }
1511
1512
1513 /* ARGSUSED */
1514 static int
1515 termp_fn_pre(DECL_ARGS)
1516 {
1517 const struct mdoc_node *nn;
1518
1519 term_fontpush(p, TERMFONT_BOLD);
1520 term_word(p, n->child->string);
1521 term_fontpop(p);
1522
1523 p->flags |= TERMP_NOSPACE;
1524 term_word(p, "(");
1525
1526 for (nn = n->child->next; nn; nn = nn->next) {
1527 term_fontpush(p, TERMFONT_UNDER);
1528 term_word(p, nn->string);
1529 term_fontpop(p);
1530
1531 if (nn->next)
1532 term_word(p, ",");
1533 }
1534
1535 term_word(p, ")");
1536
1537 if (SEC_SYNOPSIS == n->sec)
1538 term_word(p, ";");
1539
1540 return(0);
1541 }
1542
1543
1544 /* ARGSUSED */
1545 static void
1546 termp_fn_post(DECL_ARGS)
1547 {
1548
1549 if (n->sec == SEC_SYNOPSIS && n->next)
1550 term_vspace(p);
1551 }
1552
1553
1554 /* ARGSUSED */
1555 static int
1556 termp_fa_pre(DECL_ARGS)
1557 {
1558 const struct mdoc_node *nn;
1559
1560 if (n->parent->tok != MDOC_Fo) {
1561 term_fontpush(p, TERMFONT_UNDER);
1562 return(1);
1563 }
1564
1565 for (nn = n->child; nn; nn = nn->next) {
1566 term_fontpush(p, TERMFONT_UNDER);
1567 term_word(p, nn->string);
1568 term_fontpop(p);
1569
1570 if (nn->next)
1571 term_word(p, ",");
1572 }
1573
1574 if (n->child && n->next && n->next->tok == MDOC_Fa)
1575 term_word(p, ",");
1576
1577 return(0);
1578 }
1579
1580
1581 /* ARGSUSED */
1582 static int
1583 termp_bd_pre(DECL_ARGS)
1584 {
1585 int i, type;
1586 const struct mdoc_node *nn;
1587
1588 if (MDOC_BLOCK == n->type) {
1589 print_bvspace(p, n, n);
1590 return(1);
1591 } else if (MDOC_BODY != n->type)
1592 return(1);
1593
1594 nn = n->parent;
1595
1596 for (type = -1, i = 0; i < (int)nn->args->argc; i++) {
1597 switch (nn->args->argv[i].arg) {
1598 case (MDOC_Centred):
1599 /* FALLTHROUGH */
1600 case (MDOC_Ragged):
1601 /* FALLTHROUGH */
1602 case (MDOC_Filled):
1603 /* FALLTHROUGH */
1604 case (MDOC_Unfilled):
1605 /* FALLTHROUGH */
1606 case (MDOC_Literal):
1607 type = nn->args->argv[i].arg;
1608 break;
1609 case (MDOC_Offset):
1610 p->offset += a2offs(&nn->args->argv[i]);
1611 break;
1612 default:
1613 break;
1614 }
1615 }
1616
1617 /*
1618 * If -ragged or -filled are specified, the block does nothing
1619 * but change the indentation. If -unfilled or -literal are
1620 * specified, text is printed exactly as entered in the display:
1621 * for macro lines, a newline is appended to the line. Blank
1622 * lines are allowed.
1623 */
1624
1625 assert(type > -1);
1626 if (MDOC_Literal != type && MDOC_Unfilled != type)
1627 return(1);
1628
1629 for (nn = n->child; nn; nn = nn->next) {
1630 p->flags |= TERMP_NOSPACE;
1631 print_mdoc_node(p, pair, m, nn);
1632 if (NULL == nn->next)
1633 continue;
1634 if (nn->prev && nn->prev->line < nn->line)
1635 term_flushln(p);
1636 else if (NULL == nn->prev)
1637 term_flushln(p);
1638 }
1639
1640 return(0);
1641 }
1642
1643
1644 /* ARGSUSED */
1645 static void
1646 termp_bd_post(DECL_ARGS)
1647 {
1648
1649 if (MDOC_BODY != n->type)
1650 return;
1651 p->flags |= TERMP_NOSPACE;
1652 term_flushln(p);
1653 }
1654
1655
1656 /* ARGSUSED */
1657 static int
1658 termp_qq_pre(DECL_ARGS)
1659 {
1660
1661 if (MDOC_BODY != n->type)
1662 return(1);
1663 term_word(p, "\"");
1664 p->flags |= TERMP_NOSPACE;
1665 return(1);
1666 }
1667
1668
1669 /* ARGSUSED */
1670 static void
1671 termp_qq_post(DECL_ARGS)
1672 {
1673
1674 if (MDOC_BODY != n->type)
1675 return;
1676 p->flags |= TERMP_NOSPACE;
1677 term_word(p, "\"");
1678 }
1679
1680
1681 /* ARGSUSED */
1682 static void
1683 termp_bx_post(DECL_ARGS)
1684 {
1685
1686 if (n->child)
1687 p->flags |= TERMP_NOSPACE;
1688 term_word(p, "BSD");
1689 }
1690
1691
1692 /* ARGSUSED */
1693 static int
1694 termp_xx_pre(DECL_ARGS)
1695 {
1696 const char *pp;
1697
1698 pp = NULL;
1699 switch (n->tok) {
1700 case (MDOC_Bsx):
1701 pp = "BSDI BSD/OS";
1702 break;
1703 case (MDOC_Dx):
1704 pp = "DragonFly";
1705 break;
1706 case (MDOC_Fx):
1707 pp = "FreeBSD";
1708 break;
1709 case (MDOC_Nx):
1710 pp = "NetBSD";
1711 break;
1712 case (MDOC_Ox):
1713 pp = "OpenBSD";
1714 break;
1715 case (MDOC_Ux):
1716 pp = "UNIX";
1717 break;
1718 default:
1719 break;
1720 }
1721
1722 assert(pp);
1723 term_word(p, pp);
1724 return(1);
1725 }
1726
1727
1728 /* ARGSUSED */
1729 static int
1730 termp_sq_pre(DECL_ARGS)
1731 {
1732
1733 if (MDOC_BODY != n->type)
1734 return(1);
1735 term_word(p, "\\(oq");
1736 p->flags |= TERMP_NOSPACE;
1737 return(1);
1738 }
1739
1740
1741 /* ARGSUSED */
1742 static void
1743 termp_sq_post(DECL_ARGS)
1744 {
1745
1746 if (MDOC_BODY != n->type)
1747 return;
1748 p->flags |= TERMP_NOSPACE;
1749 term_word(p, "\\(aq");
1750 }
1751
1752
1753 /* ARGSUSED */
1754 static int
1755 termp_pf_pre(DECL_ARGS)
1756 {
1757
1758 p->flags |= TERMP_IGNDELIM;
1759 return(1);
1760 }
1761
1762
1763 /* ARGSUSED */
1764 static void
1765 termp_pf_post(DECL_ARGS)
1766 {
1767
1768 p->flags &= ~TERMP_IGNDELIM;
1769 p->flags |= TERMP_NOSPACE;
1770 }
1771
1772
1773 /* ARGSUSED */
1774 static int
1775 termp_ss_pre(DECL_ARGS)
1776 {
1777
1778 switch (n->type) {
1779 case (MDOC_BLOCK):
1780 term_newln(p);
1781 if (n->prev)
1782 term_vspace(p);
1783 break;
1784 case (MDOC_HEAD):
1785 term_fontpush(p, TERMFONT_BOLD);
1786 p->offset = HALFINDENT;
1787 break;
1788 default:
1789 break;
1790 }
1791
1792 return(1);
1793 }
1794
1795
1796 /* ARGSUSED */
1797 static void
1798 termp_ss_post(DECL_ARGS)
1799 {
1800
1801 if (MDOC_HEAD == n->type)
1802 term_newln(p);
1803 }
1804
1805
1806 /* ARGSUSED */
1807 static int
1808 termp_cd_pre(DECL_ARGS)
1809 {
1810
1811 term_fontpush(p, TERMFONT_BOLD);
1812 term_newln(p);
1813 return(1);
1814 }
1815
1816
1817 /* ARGSUSED */
1818 static int
1819 termp_in_pre(DECL_ARGS)
1820 {
1821
1822 term_fontpush(p, TERMFONT_BOLD);
1823 if (SEC_SYNOPSIS == n->sec)
1824 term_word(p, "#include");
1825
1826 term_word(p, "<");
1827 p->flags |= TERMP_NOSPACE;
1828 return(1);
1829 }
1830
1831
1832 /* ARGSUSED */
1833 static void
1834 termp_in_post(DECL_ARGS)
1835 {
1836
1837 term_fontpush(p, TERMFONT_BOLD);
1838 p->flags |= TERMP_NOSPACE;
1839 term_word(p, ">");
1840 term_fontpop(p);
1841
1842 if (SEC_SYNOPSIS != n->sec)
1843 return;
1844
1845 term_newln(p);
1846 /*
1847 * XXX Not entirely correct. If `.In foo bar' is specified in
1848 * the SYNOPSIS section, then it produces a single break after
1849 * the <foo>; mandoc asserts a vertical space. Since this
1850 * construction is rarely used, I think it's fine.
1851 */
1852 if (n->next && MDOC_In != n->next->tok)
1853 term_vspace(p);
1854 }
1855
1856
1857 /* ARGSUSED */
1858 static int
1859 termp_sp_pre(DECL_ARGS)
1860 {
1861 size_t i, len;
1862
1863 switch (n->tok) {
1864 case (MDOC_sp):
1865 len = n->child ? a2height(n->child) : 1;
1866 break;
1867 case (MDOC_br):
1868 len = 0;
1869 break;
1870 default:
1871 len = 1;
1872 break;
1873 }
1874
1875 if (0 == len)
1876 term_newln(p);
1877 for (i = 0; i < len; i++)
1878 term_vspace(p);
1879
1880 return(0);
1881 }
1882
1883
1884 /* ARGSUSED */
1885 static int
1886 termp_brq_pre(DECL_ARGS)
1887 {
1888
1889 if (MDOC_BODY != n->type)
1890 return(1);
1891 term_word(p, "\\(lC");
1892 p->flags |= TERMP_NOSPACE;
1893 return(1);
1894 }
1895
1896
1897 /* ARGSUSED */
1898 static void
1899 termp_brq_post(DECL_ARGS)
1900 {
1901
1902 if (MDOC_BODY != n->type)
1903 return;
1904 p->flags |= TERMP_NOSPACE;
1905 term_word(p, "\\(rC");
1906 }
1907
1908
1909 /* ARGSUSED */
1910 static int
1911 termp_bq_pre(DECL_ARGS)
1912 {
1913
1914 if (MDOC_BODY != n->type)
1915 return(1);
1916 term_word(p, "\\(lB");
1917 p->flags |= TERMP_NOSPACE;
1918 return(1);
1919 }
1920
1921
1922 /* ARGSUSED */
1923 static void
1924 termp_bq_post(DECL_ARGS)
1925 {
1926
1927 if (MDOC_BODY != n->type)
1928 return;
1929 p->flags |= TERMP_NOSPACE;
1930 term_word(p, "\\(rB");
1931 }
1932
1933
1934 /* ARGSUSED */
1935 static int
1936 termp_pq_pre(DECL_ARGS)
1937 {
1938
1939 if (MDOC_BODY != n->type)
1940 return(1);
1941 term_word(p, "\\&(");
1942 p->flags |= TERMP_NOSPACE;
1943 return(1);
1944 }
1945
1946
1947 /* ARGSUSED */
1948 static void
1949 termp_pq_post(DECL_ARGS)
1950 {
1951
1952 if (MDOC_BODY != n->type)
1953 return;
1954 term_word(p, ")");
1955 }
1956
1957
1958 /* ARGSUSED */
1959 static int
1960 termp_fo_pre(DECL_ARGS)
1961 {
1962 const struct mdoc_node *nn;
1963
1964 if (MDOC_BODY == n->type) {
1965 p->flags |= TERMP_NOSPACE;
1966 term_word(p, "(");
1967 p->flags |= TERMP_NOSPACE;
1968 return(1);
1969 } else if (MDOC_HEAD != n->type)
1970 return(1);
1971
1972 term_fontpush(p, TERMFONT_BOLD);
1973 for (nn = n->child; nn; nn = nn->next) {
1974 assert(MDOC_TEXT == nn->type);
1975 term_word(p, nn->string);
1976 }
1977 term_fontpop(p);
1978
1979 return(0);
1980 }
1981
1982
1983 /* ARGSUSED */
1984 static void
1985 termp_fo_post(DECL_ARGS)
1986 {
1987
1988 if (MDOC_BODY != n->type)
1989 return;
1990 p->flags |= TERMP_NOSPACE;
1991 term_word(p, ")");
1992 p->flags |= TERMP_NOSPACE;
1993 term_word(p, ";");
1994 term_newln(p);
1995 }
1996
1997
1998 /* ARGSUSED */
1999 static int
2000 termp_bf_pre(DECL_ARGS)
2001 {
2002 const struct mdoc_node *nn;
2003
2004 if (MDOC_HEAD == n->type)
2005 return(0);
2006 else if (MDOC_BLOCK != n->type)
2007 return(1);
2008
2009 if (NULL == (nn = n->head->child)) {
2010 if (arg_hasattr(MDOC_Emphasis, n))
2011 term_fontpush(p, TERMFONT_UNDER);
2012 else if (arg_hasattr(MDOC_Symbolic, n))
2013 term_fontpush(p, TERMFONT_BOLD);
2014 else
2015 term_fontpush(p, TERMFONT_NONE);
2016
2017 return(1);
2018 }
2019
2020 assert(MDOC_TEXT == nn->type);
2021 if (0 == strcmp("Em", nn->string))
2022 term_fontpush(p, TERMFONT_UNDER);
2023 else if (0 == strcmp("Sy", nn->string))
2024 term_fontpush(p, TERMFONT_BOLD);
2025 else
2026 term_fontpush(p, TERMFONT_NONE);
2027
2028 return(1);
2029 }
2030
2031
2032 /* ARGSUSED */
2033 static int
2034 termp_sm_pre(DECL_ARGS)
2035 {
2036
2037 assert(n->child && MDOC_TEXT == n->child->type);
2038 if (0 == strcmp("on", n->child->string))
2039 p->flags &= ~TERMP_NONOSPACE;
2040 else
2041 p->flags |= TERMP_NONOSPACE;
2042
2043 return(0);
2044 }
2045
2046
2047 /* ARGSUSED */
2048 static int
2049 termp_ap_pre(DECL_ARGS)
2050 {
2051
2052 p->flags |= TERMP_NOSPACE;
2053 term_word(p, "\\(aq");
2054 p->flags |= TERMP_NOSPACE;
2055 return(1);
2056 }
2057
2058
2059 /* ARGSUSED */
2060 static void
2061 termp____post(DECL_ARGS)
2062 {
2063
2064 /* TODO: %U. */
2065
2066 p->flags |= TERMP_NOSPACE;
2067 switch (n->tok) {
2068 case (MDOC__T):
2069 term_word(p, "\\(rq");
2070 p->flags |= TERMP_NOSPACE;
2071 break;
2072 default:
2073 break;
2074 }
2075 term_word(p, n->next ? "," : ".");
2076 }
2077
2078
2079 /* ARGSUSED */
2080 static int
2081 termp_li_pre(DECL_ARGS)
2082 {
2083
2084 term_fontpush(p, TERMFONT_NONE);
2085 return(1);
2086 }
2087
2088
2089 /* ARGSUSED */
2090 static int
2091 termp_lk_pre(DECL_ARGS)
2092 {
2093 const struct mdoc_node *nn;
2094
2095 term_fontpush(p, TERMFONT_UNDER);
2096 nn = n->child;
2097
2098 if (NULL == nn->next)
2099 return(1);
2100
2101 term_word(p, nn->string);
2102 term_fontpop(p);
2103
2104 p->flags |= TERMP_NOSPACE;
2105 term_word(p, ":");
2106
2107 term_fontpush(p, TERMFONT_BOLD);
2108 for (nn = nn->next; nn; nn = nn->next)
2109 term_word(p, nn->string);
2110 term_fontpop(p);
2111
2112 return(0);
2113 }
2114
2115
2116 /* ARGSUSED */
2117 static int
2118 termp_under_pre(DECL_ARGS)
2119 {
2120
2121 term_fontpush(p, TERMFONT_UNDER);
2122 return(1);
2123 }
2124
2125
2126 /* ARGSUSED */
2127 static int
2128 termp__t_pre(DECL_ARGS)
2129 {
2130
2131 term_word(p, "\\(lq");
2132 p->flags |= TERMP_NOSPACE;
2133 return(1);
2134 }