]> git.cameronkatri.com Git - mandoc.git/blob - mdoc_term.c
After .Sm on, spacing ought to restart right away, before the next token,
[mandoc.git] / mdoc_term.c
1 /* $Id: mdoc_term.c,v 1.174 2010/07/16 00:03:37 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 { NULL, NULL }, /* Ad */
155 { termp_an_pre, termp_an_post }, /* An */
156 { termp_under_pre, NULL }, /* Ar */
157 { termp_cd_pre, NULL }, /* Cd */
158 { termp_bold_pre, NULL }, /* Cm */
159 { NULL, NULL }, /* Dv */
160 { NULL, NULL }, /* Er */
161 { NULL, NULL }, /* Ev */
162 { termp_ex_pre, NULL }, /* Ex */
163 { termp_fa_pre, NULL }, /* Fa */
164 { termp_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 */ /* FIXME: convert to symbol? */
212 { NULL, NULL }, /* No */
213 { termp_ns_pre, NULL }, /* Ns */
214 { termp_xx_pre, NULL }, /* Nx */
215 { termp_xx_pre, NULL }, /* Ox */
216 { NULL, NULL }, /* Pc */
217 { termp_pf_pre, termp_pf_post }, /* Pf */
218 { termp_pq_pre, termp_pq_post }, /* Po */
219 { termp_pq_pre, termp_pq_post }, /* Pq */
220 { NULL, NULL }, /* Qc */
221 { termp_sq_pre, termp_sq_post }, /* Ql */
222 { termp_qq_pre, termp_qq_post }, /* Qo */
223 { termp_qq_pre, termp_qq_post }, /* Qq */
224 { NULL, NULL }, /* Re */
225 { termp_rs_pre, NULL }, /* Rs */
226 { NULL, NULL }, /* Sc */
227 { termp_sq_pre, termp_sq_post }, /* So */
228 { termp_sq_pre, termp_sq_post }, /* Sq */
229 { termp_sm_pre, NULL }, /* Sm */
230 { termp_under_pre, NULL }, /* Sx */
231 { termp_bold_pre, NULL }, /* Sy */
232 { NULL, NULL }, /* Tn */
233 { termp_xx_pre, NULL }, /* Ux */
234 { NULL, NULL }, /* Xc */
235 { NULL, NULL }, /* Xo */
236 { termp_fo_pre, termp_fo_post }, /* Fo */
237 { NULL, NULL }, /* Fc */
238 { termp_op_pre, termp_op_post }, /* Oo */
239 { NULL, NULL }, /* Oc */
240 { 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 synopsis_pre(p, n);
1030
1031 if (MDOC_HEAD == n->type && n->next->child) {
1032 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_HANG;
1033 p->rmargin = p->offset + term_len(p, 1) +
1034 (NULL == n->child ? term_strlen(p, m->name) :
1035 MDOC_TEXT == n->child->type ?
1036 term_strlen(p, n->child->string) :
1037 term_len(p, 5));
1038 }
1039
1040 term_fontpush(p, TERMFONT_BOLD);
1041 if (NULL == n->child)
1042 term_word(p, m->name);
1043 return(1);
1044 }
1045
1046
1047 /* ARGSUSED */
1048 static void
1049 termp_nm_post(DECL_ARGS)
1050 {
1051
1052 if (MDOC_HEAD == n->type && n->next->child) {
1053 term_flushln(p);
1054 p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
1055 } else if (MDOC_BODY == n->type && n->child) {
1056 term_flushln(p);
1057 p->flags &= ~TERMP_NOLPAD;
1058 }
1059 }
1060
1061
1062 /* ARGSUSED */
1063 static int
1064 termp_fl_pre(DECL_ARGS)
1065 {
1066
1067 term_fontpush(p, TERMFONT_BOLD);
1068 term_word(p, "\\-");
1069
1070 if (n->child)
1071 p->flags |= TERMP_NOSPACE;
1072 else if (n->next && n->next->line == n->line)
1073 p->flags |= TERMP_NOSPACE;
1074
1075 return(1);
1076 }
1077
1078
1079 /* ARGSUSED */
1080 static int
1081 termp_an_pre(DECL_ARGS)
1082 {
1083
1084 if (NULL == n->child)
1085 return(1);
1086
1087 /*
1088 * If not in the AUTHORS section, `An -split' will cause
1089 * newlines to occur before the author name. If in the AUTHORS
1090 * section, by default, the first `An' invocation is nosplit,
1091 * then all subsequent ones, regardless of whether interspersed
1092 * with other macros/text, are split. -split, in this case,
1093 * will override the condition of the implied first -nosplit.
1094 */
1095
1096 if (n->sec == SEC_AUTHORS) {
1097 if ( ! (TERMP_ANPREC & p->flags)) {
1098 if (TERMP_SPLIT & p->flags)
1099 term_newln(p);
1100 return(1);
1101 }
1102 if (TERMP_NOSPLIT & p->flags)
1103 return(1);
1104 term_newln(p);
1105 return(1);
1106 }
1107
1108 if (TERMP_SPLIT & p->flags)
1109 term_newln(p);
1110
1111 return(1);
1112 }
1113
1114
1115 /* ARGSUSED */
1116 static void
1117 termp_an_post(DECL_ARGS)
1118 {
1119
1120 if (n->child) {
1121 if (SEC_AUTHORS == n->sec)
1122 p->flags |= TERMP_ANPREC;
1123 return;
1124 }
1125
1126 if (AUTH_split == n->data.An.auth) {
1127 p->flags &= ~TERMP_NOSPLIT;
1128 p->flags |= TERMP_SPLIT;
1129 } else if (AUTH_nosplit == n->data.An.auth) {
1130 p->flags &= ~TERMP_SPLIT;
1131 p->flags |= TERMP_NOSPLIT;
1132 }
1133
1134 }
1135
1136
1137 /* ARGSUSED */
1138 static int
1139 termp_ns_pre(DECL_ARGS)
1140 {
1141
1142 p->flags |= TERMP_NOSPACE;
1143 return(1);
1144 }
1145
1146
1147 /* ARGSUSED */
1148 static int
1149 termp_rs_pre(DECL_ARGS)
1150 {
1151
1152 if (SEC_SEE_ALSO != n->sec)
1153 return(1);
1154 if (MDOC_BLOCK == n->type && n->prev)
1155 term_vspace(p);
1156 return(1);
1157 }
1158
1159
1160 /* ARGSUSED */
1161 static int
1162 termp_rv_pre(DECL_ARGS)
1163 {
1164 const struct mdoc_node *nn;
1165
1166 term_newln(p);
1167 term_word(p, "The");
1168
1169 for (nn = n->child; nn; nn = nn->next) {
1170 term_fontpush(p, TERMFONT_BOLD);
1171 term_word(p, nn->string);
1172 term_fontpop(p);
1173 p->flags |= TERMP_NOSPACE;
1174 if (nn->next && NULL == nn->next->next)
1175 term_word(p, "(), and");
1176 else if (nn->next)
1177 term_word(p, "(),");
1178 else
1179 term_word(p, "()");
1180 }
1181
1182 if (n->child && n->child->next)
1183 term_word(p, "functions return");
1184 else
1185 term_word(p, "function returns");
1186
1187 term_word(p, "the value 0 if successful; otherwise the value "
1188 "-1 is returned and the global variable");
1189
1190 term_fontpush(p, TERMFONT_UNDER);
1191 term_word(p, "errno");
1192 term_fontpop(p);
1193
1194 term_word(p, "is set to indicate the error.");
1195 p->flags |= TERMP_SENTENCE;
1196
1197 return(0);
1198 }
1199
1200
1201 /* ARGSUSED */
1202 static int
1203 termp_ex_pre(DECL_ARGS)
1204 {
1205 const struct mdoc_node *nn;
1206
1207 term_word(p, "The");
1208
1209 for (nn = n->child; nn; nn = nn->next) {
1210 term_fontpush(p, TERMFONT_BOLD);
1211 term_word(p, nn->string);
1212 term_fontpop(p);
1213 p->flags |= TERMP_NOSPACE;
1214 if (nn->next && NULL == nn->next->next)
1215 term_word(p, ", and");
1216 else if (nn->next)
1217 term_word(p, ",");
1218 else
1219 p->flags &= ~TERMP_NOSPACE;
1220 }
1221
1222 if (n->child && n->child->next)
1223 term_word(p, "utilities exit");
1224 else
1225 term_word(p, "utility exits");
1226
1227 term_word(p, "0 on success, and >0 if an error occurs.");
1228 p->flags |= TERMP_SENTENCE;
1229
1230 return(0);
1231 }
1232
1233
1234 /* ARGSUSED */
1235 static int
1236 termp_nd_pre(DECL_ARGS)
1237 {
1238
1239 if (MDOC_BODY != n->type)
1240 return(1);
1241
1242 #if defined(__OpenBSD__) || defined(__linux__)
1243 term_word(p, "\\(en");
1244 #else
1245 term_word(p, "\\(em");
1246 #endif
1247 return(1);
1248 }
1249
1250
1251 /* ARGSUSED */
1252 static int
1253 termp_bl_pre(DECL_ARGS)
1254 {
1255
1256 return(MDOC_HEAD != n->type);
1257 }
1258
1259
1260 /* ARGSUSED */
1261 static void
1262 termp_bl_post(DECL_ARGS)
1263 {
1264
1265 if (MDOC_BLOCK == n->type)
1266 term_newln(p);
1267 }
1268
1269
1270 /* ARGSUSED */
1271 static void
1272 termp_op_post(DECL_ARGS)
1273 {
1274
1275 if (MDOC_BODY != n->type)
1276 return;
1277 p->flags |= TERMP_NOSPACE;
1278 term_word(p, "\\(rB");
1279 }
1280
1281
1282 /* ARGSUSED */
1283 static int
1284 termp_xr_pre(DECL_ARGS)
1285 {
1286 const struct mdoc_node *nn;
1287
1288 if (NULL == n->child)
1289 return(0);
1290
1291 assert(MDOC_TEXT == n->child->type);
1292 nn = n->child;
1293
1294 term_word(p, nn->string);
1295 if (NULL == (nn = nn->next))
1296 return(0);
1297 p->flags |= TERMP_NOSPACE;
1298 term_word(p, "(");
1299 p->flags |= TERMP_NOSPACE;
1300 term_word(p, nn->string);
1301 p->flags |= TERMP_NOSPACE;
1302 term_word(p, ")");
1303
1304 return(0);
1305 }
1306
1307
1308 /*
1309 * This decides how to assert whitespace before any of the SYNOPSIS set
1310 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
1311 * macro combos).
1312 */
1313 static void
1314 synopsis_pre(struct termp *p, const struct mdoc_node *n)
1315 {
1316 /*
1317 * Obviously, if we're not in a SYNOPSIS or no prior macros
1318 * exist, do nothing.
1319 */
1320 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
1321 return;
1322
1323 /*
1324 * If we're the second in a pair of like elements, emit our
1325 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which
1326 * case we soldier on.
1327 */
1328 if (n->prev->tok == n->tok &&
1329 MDOC_Ft != n->tok &&
1330 MDOC_Fo != n->tok &&
1331 MDOC_Fn != n->tok) {
1332 term_newln(p);
1333 return;
1334 }
1335
1336 /*
1337 * If we're one of the SYNOPSIS set and non-like pair-wise after
1338 * another (or Fn/Fo, which we've let slip through) then assert
1339 * vertical space, else only newline and move on.
1340 */
1341 switch (n->prev->tok) {
1342 case (MDOC_Fd):
1343 /* FALLTHROUGH */
1344 case (MDOC_Fn):
1345 /* FALLTHROUGH */
1346 case (MDOC_Fo):
1347 /* FALLTHROUGH */
1348 case (MDOC_In):
1349 /* FALLTHROUGH */
1350 case (MDOC_Vt):
1351 term_vspace(p);
1352 break;
1353 case (MDOC_Ft):
1354 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
1355 term_vspace(p);
1356 break;
1357 }
1358 /* FALLTHROUGH */
1359 default:
1360 term_newln(p);
1361 break;
1362 }
1363 }
1364
1365
1366 static int
1367 termp_vt_pre(DECL_ARGS)
1368 {
1369
1370 if (MDOC_ELEM == n->type) {
1371 synopsis_pre(p, n);
1372 return(termp_under_pre(p, pair, m, n));
1373 } else if (MDOC_BLOCK == n->type) {
1374 synopsis_pre(p, n);
1375 return(1);
1376 } else if (MDOC_HEAD == n->type)
1377 return(0);
1378
1379 return(termp_under_pre(p, pair, m, n));
1380 }
1381
1382
1383 /* ARGSUSED */
1384 static int
1385 termp_bold_pre(DECL_ARGS)
1386 {
1387
1388 term_fontpush(p, TERMFONT_BOLD);
1389 return(1);
1390 }
1391
1392
1393 /* ARGSUSED */
1394 static int
1395 termp_fd_pre(DECL_ARGS)
1396 {
1397
1398 synopsis_pre(p, n);
1399 return(termp_bold_pre(p, pair, m, n));
1400 }
1401
1402
1403 /* ARGSUSED */
1404 static int
1405 termp_sh_pre(DECL_ARGS)
1406 {
1407
1408 /* No vspace between consecutive `Sh' calls. */
1409
1410 switch (n->type) {
1411 case (MDOC_BLOCK):
1412 if (n->prev && MDOC_Sh == n->prev->tok)
1413 if (NULL == n->prev->body->child)
1414 break;
1415 term_vspace(p);
1416 break;
1417 case (MDOC_HEAD):
1418 term_fontpush(p, TERMFONT_BOLD);
1419 break;
1420 case (MDOC_BODY):
1421 p->offset = term_len(p, INDENT);
1422 break;
1423 default:
1424 break;
1425 }
1426 return(1);
1427 }
1428
1429
1430 /* ARGSUSED */
1431 static void
1432 termp_sh_post(DECL_ARGS)
1433 {
1434
1435 switch (n->type) {
1436 case (MDOC_HEAD):
1437 term_newln(p);
1438 break;
1439 case (MDOC_BODY):
1440 term_newln(p);
1441 p->offset = 0;
1442 break;
1443 default:
1444 break;
1445 }
1446 }
1447
1448
1449 /* ARGSUSED */
1450 static int
1451 termp_op_pre(DECL_ARGS)
1452 {
1453
1454 switch (n->type) {
1455 case (MDOC_BODY):
1456 term_word(p, "\\(lB");
1457 p->flags |= TERMP_NOSPACE;
1458 break;
1459 default:
1460 break;
1461 }
1462 return(1);
1463 }
1464
1465
1466 /* ARGSUSED */
1467 static int
1468 termp_bt_pre(DECL_ARGS)
1469 {
1470
1471 term_word(p, "is currently in beta test.");
1472 p->flags |= TERMP_SENTENCE;
1473 return(0);
1474 }
1475
1476
1477 /* ARGSUSED */
1478 static void
1479 termp_lb_post(DECL_ARGS)
1480 {
1481
1482 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags)
1483 term_newln(p);
1484 }
1485
1486
1487 /* ARGSUSED */
1488 static int
1489 termp_ud_pre(DECL_ARGS)
1490 {
1491
1492 term_word(p, "currently under development.");
1493 p->flags |= TERMP_SENTENCE;
1494 return(0);
1495 }
1496
1497
1498 /* ARGSUSED */
1499 static int
1500 termp_d1_pre(DECL_ARGS)
1501 {
1502
1503 if (MDOC_BLOCK != n->type)
1504 return(1);
1505 term_newln(p);
1506 p->offset += term_len(p, (INDENT + 1));
1507 return(1);
1508 }
1509
1510
1511 /* ARGSUSED */
1512 static void
1513 termp_d1_post(DECL_ARGS)
1514 {
1515
1516 if (MDOC_BLOCK != n->type)
1517 return;
1518 term_newln(p);
1519 }
1520
1521
1522 /* ARGSUSED */
1523 static int
1524 termp_aq_pre(DECL_ARGS)
1525 {
1526
1527 if (MDOC_BODY != n->type)
1528 return(1);
1529 term_word(p, "\\(la");
1530 p->flags |= TERMP_NOSPACE;
1531 return(1);
1532 }
1533
1534
1535 /* ARGSUSED */
1536 static void
1537 termp_aq_post(DECL_ARGS)
1538 {
1539
1540 if (MDOC_BODY != n->type)
1541 return;
1542 p->flags |= TERMP_NOSPACE;
1543 term_word(p, "\\(ra");
1544 }
1545
1546
1547 /* ARGSUSED */
1548 static int
1549 termp_ft_pre(DECL_ARGS)
1550 {
1551
1552 /* NB: MDOC_LINE does not effect this! */
1553 synopsis_pre(p, n);
1554 term_fontpush(p, TERMFONT_UNDER);
1555 return(1);
1556 }
1557
1558
1559 /* ARGSUSED */
1560 static int
1561 termp_fn_pre(DECL_ARGS)
1562 {
1563 const struct mdoc_node *nn;
1564
1565 synopsis_pre(p, n);
1566
1567 term_fontpush(p, TERMFONT_BOLD);
1568 term_word(p, n->child->string);
1569 term_fontpop(p);
1570
1571 p->flags |= TERMP_NOSPACE;
1572 term_word(p, "(");
1573
1574 for (nn = n->child->next; nn; nn = nn->next) {
1575 term_fontpush(p, TERMFONT_UNDER);
1576 term_word(p, nn->string);
1577 term_fontpop(p);
1578
1579 if (nn->next)
1580 term_word(p, ",");
1581 }
1582
1583 term_word(p, ")");
1584
1585 if (MDOC_SYNPRETTY & n->flags)
1586 term_word(p, ";");
1587
1588 return(0);
1589 }
1590
1591
1592 /* ARGSUSED */
1593 static int
1594 termp_fa_pre(DECL_ARGS)
1595 {
1596 const struct mdoc_node *nn;
1597
1598 if (n->parent->tok != MDOC_Fo) {
1599 term_fontpush(p, TERMFONT_UNDER);
1600 return(1);
1601 }
1602
1603 for (nn = n->child; nn; nn = nn->next) {
1604 term_fontpush(p, TERMFONT_UNDER);
1605 term_word(p, nn->string);
1606 term_fontpop(p);
1607
1608 if (nn->next)
1609 term_word(p, ",");
1610 }
1611
1612 if (n->child && n->next && n->next->tok == MDOC_Fa)
1613 term_word(p, ",");
1614
1615 return(0);
1616 }
1617
1618
1619 /* ARGSUSED */
1620 static int
1621 termp_bd_pre(DECL_ARGS)
1622 {
1623 size_t tabwidth;
1624 size_t 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 p->flags |= TERMP_NOSPACE;
1657 print_mdoc_node(p, pair, m, nn);
1658 if (NULL == nn->prev ||
1659 nn->prev->line < nn->line ||
1660 NULL == nn->next)
1661 term_flushln(p);
1662 }
1663
1664 p->tabwidth = tabwidth;
1665 p->rmargin = rm;
1666 p->maxrmargin = rmax;
1667 return(0);
1668 }
1669
1670
1671 /* ARGSUSED */
1672 static void
1673 termp_bd_post(DECL_ARGS)
1674 {
1675 size_t rm, rmax;
1676
1677 if (MDOC_BODY != n->type)
1678 return;
1679
1680 rm = p->rmargin;
1681 rmax = p->maxrmargin;
1682
1683 assert(n->data.Bd);
1684 if (DISP_literal == n->data.Bd->type ||
1685 DISP_unfilled == n->data.Bd->type)
1686 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1687
1688 p->flags |= TERMP_NOSPACE;
1689 term_newln(p);
1690
1691 p->rmargin = rm;
1692 p->maxrmargin = rmax;
1693 }
1694
1695
1696 /* ARGSUSED */
1697 static int
1698 termp_qq_pre(DECL_ARGS)
1699 {
1700
1701 if (MDOC_BODY != n->type)
1702 return(1);
1703 term_word(p, "\"");
1704 p->flags |= TERMP_NOSPACE;
1705 return(1);
1706 }
1707
1708
1709 /* ARGSUSED */
1710 static void
1711 termp_qq_post(DECL_ARGS)
1712 {
1713
1714 if (MDOC_BODY != n->type)
1715 return;
1716 p->flags |= TERMP_NOSPACE;
1717 term_word(p, "\"");
1718 }
1719
1720
1721 /* ARGSUSED */
1722 static void
1723 termp_bx_post(DECL_ARGS)
1724 {
1725
1726 if (n->child)
1727 p->flags |= TERMP_NOSPACE;
1728 term_word(p, "BSD");
1729 }
1730
1731
1732 /* ARGSUSED */
1733 static int
1734 termp_xx_pre(DECL_ARGS)
1735 {
1736 const char *pp;
1737
1738 pp = NULL;
1739 switch (n->tok) {
1740 case (MDOC_Bsx):
1741 pp = "BSDI BSD/OS";
1742 break;
1743 case (MDOC_Dx):
1744 pp = "DragonFly";
1745 break;
1746 case (MDOC_Fx):
1747 pp = "FreeBSD";
1748 break;
1749 case (MDOC_Nx):
1750 pp = "NetBSD";
1751 break;
1752 case (MDOC_Ox):
1753 pp = "OpenBSD";
1754 break;
1755 case (MDOC_Ux):
1756 pp = "UNIX";
1757 break;
1758 default:
1759 break;
1760 }
1761
1762 assert(pp);
1763 term_word(p, pp);
1764 return(1);
1765 }
1766
1767
1768 /* ARGSUSED */
1769 static int
1770 termp_sq_pre(DECL_ARGS)
1771 {
1772
1773 if (MDOC_BODY != n->type)
1774 return(1);
1775 term_word(p, "\\(oq");
1776 p->flags |= TERMP_NOSPACE;
1777 return(1);
1778 }
1779
1780
1781 /* ARGSUSED */
1782 static void
1783 termp_sq_post(DECL_ARGS)
1784 {
1785
1786 if (MDOC_BODY != n->type)
1787 return;
1788 p->flags |= TERMP_NOSPACE;
1789 term_word(p, "\\(aq");
1790 }
1791
1792
1793 /* ARGSUSED */
1794 static int
1795 termp_pf_pre(DECL_ARGS)
1796 {
1797
1798 p->flags |= TERMP_IGNDELIM;
1799 return(1);
1800 }
1801
1802
1803 /* ARGSUSED */
1804 static void
1805 termp_pf_post(DECL_ARGS)
1806 {
1807
1808 p->flags &= ~TERMP_IGNDELIM;
1809 p->flags |= TERMP_NOSPACE;
1810 }
1811
1812
1813 /* ARGSUSED */
1814 static int
1815 termp_ss_pre(DECL_ARGS)
1816 {
1817
1818 switch (n->type) {
1819 case (MDOC_BLOCK):
1820 term_newln(p);
1821 if (n->prev)
1822 term_vspace(p);
1823 break;
1824 case (MDOC_HEAD):
1825 term_fontpush(p, TERMFONT_BOLD);
1826 p->offset = term_len(p, HALFINDENT);
1827 break;
1828 default:
1829 break;
1830 }
1831
1832 return(1);
1833 }
1834
1835
1836 /* ARGSUSED */
1837 static void
1838 termp_ss_post(DECL_ARGS)
1839 {
1840
1841 if (MDOC_HEAD == n->type)
1842 term_newln(p);
1843 }
1844
1845
1846 /* ARGSUSED */
1847 static int
1848 termp_cd_pre(DECL_ARGS)
1849 {
1850
1851 synopsis_pre(p, n);
1852 term_fontpush(p, TERMFONT_BOLD);
1853 return(1);
1854 }
1855
1856
1857 /* ARGSUSED */
1858 static int
1859 termp_in_pre(DECL_ARGS)
1860 {
1861
1862 synopsis_pre(p, n);
1863
1864 if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) {
1865 term_fontpush(p, TERMFONT_BOLD);
1866 term_word(p, "#include");
1867 term_word(p, "<");
1868 } else {
1869 term_word(p, "<");
1870 term_fontpush(p, TERMFONT_UNDER);
1871 }
1872
1873 p->flags |= TERMP_NOSPACE;
1874 return(1);
1875 }
1876
1877
1878 /* ARGSUSED */
1879 static void
1880 termp_in_post(DECL_ARGS)
1881 {
1882
1883 if (MDOC_SYNPRETTY & n->flags)
1884 term_fontpush(p, TERMFONT_BOLD);
1885
1886 p->flags |= TERMP_NOSPACE;
1887 term_word(p, ">");
1888
1889 if (MDOC_SYNPRETTY & n->flags)
1890 term_fontpop(p);
1891 }
1892
1893
1894 /* ARGSUSED */
1895 static int
1896 termp_sp_pre(DECL_ARGS)
1897 {
1898 size_t i, len;
1899
1900 switch (n->tok) {
1901 case (MDOC_sp):
1902 len = n->child ? a2height(p, n->child->string) : 1;
1903 break;
1904 case (MDOC_br):
1905 len = 0;
1906 break;
1907 default:
1908 len = 1;
1909 break;
1910 }
1911
1912 if (0 == len)
1913 term_newln(p);
1914 for (i = 0; i < len; i++)
1915 term_vspace(p);
1916
1917 return(0);
1918 }
1919
1920
1921 /* ARGSUSED */
1922 static int
1923 termp_brq_pre(DECL_ARGS)
1924 {
1925
1926 if (MDOC_BODY != n->type)
1927 return(1);
1928 term_word(p, "\\(lC");
1929 p->flags |= TERMP_NOSPACE;
1930 return(1);
1931 }
1932
1933
1934 /* ARGSUSED */
1935 static void
1936 termp_brq_post(DECL_ARGS)
1937 {
1938
1939 if (MDOC_BODY != n->type)
1940 return;
1941 p->flags |= TERMP_NOSPACE;
1942 term_word(p, "\\(rC");
1943 }
1944
1945
1946 /* ARGSUSED */
1947 static int
1948 termp_bq_pre(DECL_ARGS)
1949 {
1950
1951 if (MDOC_BODY != n->type)
1952 return(1);
1953 term_word(p, "\\(lB");
1954 p->flags |= TERMP_NOSPACE;
1955 return(1);
1956 }
1957
1958
1959 /* ARGSUSED */
1960 static void
1961 termp_bq_post(DECL_ARGS)
1962 {
1963
1964 if (MDOC_BODY != n->type)
1965 return;
1966 p->flags |= TERMP_NOSPACE;
1967 term_word(p, "\\(rB");
1968 }
1969
1970
1971 /* ARGSUSED */
1972 static int
1973 termp_pq_pre(DECL_ARGS)
1974 {
1975
1976 if (MDOC_BODY != n->type)
1977 return(1);
1978 term_word(p, "\\&(");
1979 p->flags |= TERMP_NOSPACE;
1980 return(1);
1981 }
1982
1983
1984 /* ARGSUSED */
1985 static void
1986 termp_pq_post(DECL_ARGS)
1987 {
1988
1989 if (MDOC_BODY != n->type)
1990 return;
1991 term_word(p, ")");
1992 }
1993
1994
1995 /* ARGSUSED */
1996 static int
1997 termp_fo_pre(DECL_ARGS)
1998 {
1999
2000 if (MDOC_BLOCK == n->type) {
2001 synopsis_pre(p, n);
2002 return(1);
2003 } else if (MDOC_BODY == n->type) {
2004 p->flags |= TERMP_NOSPACE;
2005 term_word(p, "(");
2006 p->flags |= TERMP_NOSPACE;
2007 return(1);
2008 }
2009
2010 if (NULL == n->child)
2011 return(0);
2012
2013 /* XXX: we drop non-initial arguments as per groff. */
2014
2015 assert(n->child->string);
2016 term_fontpush(p, TERMFONT_BOLD);
2017 term_word(p, n->child->string);
2018 return(0);
2019 }
2020
2021
2022 /* ARGSUSED */
2023 static void
2024 termp_fo_post(DECL_ARGS)
2025 {
2026
2027 if (MDOC_BODY != n->type)
2028 return;
2029
2030 p->flags |= TERMP_NOSPACE;
2031 term_word(p, ")");
2032
2033 if (MDOC_SYNPRETTY & n->flags) {
2034 p->flags |= TERMP_NOSPACE;
2035 term_word(p, ";");
2036 }
2037 }
2038
2039
2040 /* ARGSUSED */
2041 static int
2042 termp_bf_pre(DECL_ARGS)
2043 {
2044
2045 if (MDOC_HEAD == n->type)
2046 return(0);
2047 else if (MDOC_BLOCK != n->type)
2048 return(1);
2049
2050 assert(n->data.Bf);
2051
2052 if (FONT_Em == n->data.Bf->font)
2053 term_fontpush(p, TERMFONT_UNDER);
2054 else if (FONT_Sy == n->data.Bf->font)
2055 term_fontpush(p, TERMFONT_BOLD);
2056 else
2057 term_fontpush(p, TERMFONT_NONE);
2058
2059 return(1);
2060 }
2061
2062
2063 /* ARGSUSED */
2064 static int
2065 termp_sm_pre(DECL_ARGS)
2066 {
2067
2068 assert(n->child && MDOC_TEXT == n->child->type);
2069 if (0 == strcmp("on", n->child->string)) {
2070 if (p->col)
2071 p->flags &= ~TERMP_NOSPACE;
2072 p->flags &= ~TERMP_NONOSPACE;
2073 } else
2074 p->flags |= TERMP_NONOSPACE;
2075
2076 return(0);
2077 }
2078
2079
2080 /* ARGSUSED */
2081 static int
2082 termp_ap_pre(DECL_ARGS)
2083 {
2084
2085 p->flags |= TERMP_NOSPACE;
2086 term_word(p, "\\(aq");
2087 p->flags |= TERMP_NOSPACE;
2088 return(1);
2089 }
2090
2091
2092 /* ARGSUSED */
2093 static void
2094 termp____post(DECL_ARGS)
2095 {
2096
2097 /* TODO: %U. */
2098
2099 p->flags |= TERMP_NOSPACE;
2100 term_word(p, n->next ? "," : ".");
2101 }
2102
2103
2104 /* ARGSUSED */
2105 static int
2106 termp_li_pre(DECL_ARGS)
2107 {
2108
2109 term_fontpush(p, TERMFONT_NONE);
2110 return(1);
2111 }
2112
2113
2114 /* ARGSUSED */
2115 static int
2116 termp_lk_pre(DECL_ARGS)
2117 {
2118 const struct mdoc_node *nn;
2119
2120 term_fontpush(p, TERMFONT_UNDER);
2121 nn = n->child;
2122
2123 if (NULL == nn->next)
2124 return(1);
2125
2126 term_word(p, nn->string);
2127 term_fontpop(p);
2128
2129 p->flags |= TERMP_NOSPACE;
2130 term_word(p, ":");
2131
2132 term_fontpush(p, TERMFONT_BOLD);
2133 for (nn = nn->next; nn; nn = nn->next)
2134 term_word(p, nn->string);
2135 term_fontpop(p);
2136
2137 return(0);
2138 }
2139
2140
2141 /* ARGSUSED */
2142 static int
2143 termp_bk_pre(DECL_ARGS)
2144 {
2145
2146 switch (n->type) {
2147 case (MDOC_BLOCK):
2148 break;
2149 case (MDOC_HEAD):
2150 return(0);
2151 case (MDOC_BODY):
2152 p->flags |= TERMP_PREKEEP;
2153 break;
2154 default:
2155 abort();
2156 /* NOTREACHED */
2157 }
2158
2159 return(1);
2160 }
2161
2162
2163 /* ARGSUSED */
2164 static void
2165 termp_bk_post(DECL_ARGS)
2166 {
2167
2168 if (MDOC_BODY == n->type)
2169 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2170 }
2171
2172 /* ARGSUSED */
2173 static int
2174 termp_under_pre(DECL_ARGS)
2175 {
2176
2177 term_fontpush(p, TERMFONT_UNDER);
2178 return(1);
2179 }