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