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