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