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