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