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