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