1 /* $Id: mdoc_term.c,v 1.346 2017/02/17 19:15:41 schwarze Exp $ */
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2012-2017 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
31 #include "mandoc_aux.h"
41 struct termpair *ppair;
45 #define DECL_ARGS struct termp *p, \
46 struct termpair *pair, \
47 const struct roff_meta *meta, \
51 int (*pre)(DECL_ARGS);
52 void (*post)(DECL_ARGS);
55 static int a2width(const struct termp *, const char *);
57 static void print_bvspace(struct termp *,
58 const struct roff_node *,
59 const struct roff_node *);
60 static void print_mdoc_node(DECL_ARGS);
61 static void print_mdoc_nodelist(DECL_ARGS);
62 static void print_mdoc_head(struct termp *, const struct roff_meta *);
63 static void print_mdoc_foot(struct termp *, const struct roff_meta *);
64 static void synopsis_pre(struct termp *,
65 const struct roff_node *);
67 static void termp____post(DECL_ARGS);
68 static void termp__t_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_eo_post(DECL_ARGS);
73 static void termp_fd_post(DECL_ARGS);
74 static void termp_fo_post(DECL_ARGS);
75 static void termp_in_post(DECL_ARGS);
76 static void termp_it_post(DECL_ARGS);
77 static void termp_lb_post(DECL_ARGS);
78 static void termp_nm_post(DECL_ARGS);
79 static void termp_pf_post(DECL_ARGS);
80 static void termp_quote_post(DECL_ARGS);
81 static void termp_sh_post(DECL_ARGS);
82 static void termp_ss_post(DECL_ARGS);
83 static void termp_xx_post(DECL_ARGS);
85 static int termp__a_pre(DECL_ARGS);
86 static int termp__t_pre(DECL_ARGS);
87 static int termp_an_pre(DECL_ARGS);
88 static int termp_ap_pre(DECL_ARGS);
89 static int termp_bd_pre(DECL_ARGS);
90 static int termp_bf_pre(DECL_ARGS);
91 static int termp_bk_pre(DECL_ARGS);
92 static int termp_bl_pre(DECL_ARGS);
93 static int termp_bold_pre(DECL_ARGS);
94 static int termp_cd_pre(DECL_ARGS);
95 static int termp_d1_pre(DECL_ARGS);
96 static int termp_eo_pre(DECL_ARGS);
97 static int termp_em_pre(DECL_ARGS);
98 static int termp_er_pre(DECL_ARGS);
99 static int termp_ex_pre(DECL_ARGS);
100 static int termp_fa_pre(DECL_ARGS);
101 static int termp_fd_pre(DECL_ARGS);
102 static int termp_fl_pre(DECL_ARGS);
103 static int termp_fn_pre(DECL_ARGS);
104 static int termp_fo_pre(DECL_ARGS);
105 static int termp_ft_pre(DECL_ARGS);
106 static int termp_in_pre(DECL_ARGS);
107 static int termp_it_pre(DECL_ARGS);
108 static int termp_li_pre(DECL_ARGS);
109 static int termp_ll_pre(DECL_ARGS);
110 static int termp_lk_pre(DECL_ARGS);
111 static int termp_nd_pre(DECL_ARGS);
112 static int termp_nm_pre(DECL_ARGS);
113 static int termp_ns_pre(DECL_ARGS);
114 static int termp_quote_pre(DECL_ARGS);
115 static int termp_rs_pre(DECL_ARGS);
116 static int termp_sh_pre(DECL_ARGS);
117 static int termp_skip_pre(DECL_ARGS);
118 static int termp_sm_pre(DECL_ARGS);
119 static int termp_sp_pre(DECL_ARGS);
120 static int termp_ss_pre(DECL_ARGS);
121 static int termp_sy_pre(DECL_ARGS);
122 static int termp_tag_pre(DECL_ARGS);
123 static int termp_under_pre(DECL_ARGS);
124 static int termp_vt_pre(DECL_ARGS);
125 static int termp_xr_pre(DECL_ARGS);
126 static int termp_xx_pre(DECL_ARGS);
128 static const struct termact termacts[MDOC_MAX] = {
129 { termp_ap_pre, NULL }, /* Ap */
130 { NULL, NULL }, /* Dd */
131 { NULL, NULL }, /* Dt */
132 { NULL, NULL }, /* Os */
133 { termp_sh_pre, termp_sh_post }, /* Sh */
134 { termp_ss_pre, termp_ss_post }, /* Ss */
135 { termp_sp_pre, NULL }, /* Pp */
136 { termp_d1_pre, termp_bl_post }, /* D1 */
137 { termp_d1_pre, termp_bl_post }, /* Dl */
138 { termp_bd_pre, termp_bd_post }, /* Bd */
139 { NULL, NULL }, /* Ed */
140 { termp_bl_pre, termp_bl_post }, /* Bl */
141 { NULL, NULL }, /* El */
142 { termp_it_pre, termp_it_post }, /* It */
143 { termp_under_pre, NULL }, /* Ad */
144 { termp_an_pre, NULL }, /* An */
145 { termp_under_pre, NULL }, /* Ar */
146 { termp_cd_pre, NULL }, /* Cd */
147 { termp_bold_pre, NULL }, /* Cm */
148 { termp_li_pre, NULL }, /* Dv */
149 { termp_er_pre, NULL }, /* Er */
150 { termp_tag_pre, NULL }, /* Ev */
151 { termp_ex_pre, NULL }, /* Ex */
152 { termp_fa_pre, NULL }, /* Fa */
153 { termp_fd_pre, termp_fd_post }, /* Fd */
154 { termp_fl_pre, NULL }, /* Fl */
155 { termp_fn_pre, NULL }, /* Fn */
156 { termp_ft_pre, NULL }, /* Ft */
157 { termp_bold_pre, NULL }, /* Ic */
158 { termp_in_pre, termp_in_post }, /* In */
159 { termp_li_pre, NULL }, /* Li */
160 { termp_nd_pre, NULL }, /* Nd */
161 { termp_nm_pre, termp_nm_post }, /* Nm */
162 { termp_quote_pre, termp_quote_post }, /* Op */
163 { termp_ft_pre, NULL }, /* Ot */
164 { termp_under_pre, NULL }, /* Pa */
165 { termp_ex_pre, NULL }, /* Rv */
166 { NULL, NULL }, /* St */
167 { termp_under_pre, NULL }, /* Va */
168 { termp_vt_pre, NULL }, /* Vt */
169 { termp_xr_pre, NULL }, /* Xr */
170 { termp__a_pre, termp____post }, /* %A */
171 { termp_under_pre, termp____post }, /* %B */
172 { NULL, termp____post }, /* %D */
173 { termp_under_pre, termp____post }, /* %I */
174 { termp_under_pre, termp____post }, /* %J */
175 { NULL, termp____post }, /* %N */
176 { NULL, termp____post }, /* %O */
177 { NULL, termp____post }, /* %P */
178 { NULL, termp____post }, /* %R */
179 { termp__t_pre, termp__t_post }, /* %T */
180 { NULL, termp____post }, /* %V */
181 { NULL, NULL }, /* Ac */
182 { termp_quote_pre, termp_quote_post }, /* Ao */
183 { termp_quote_pre, termp_quote_post }, /* Aq */
184 { NULL, NULL }, /* At */
185 { NULL, NULL }, /* Bc */
186 { termp_bf_pre, NULL }, /* Bf */
187 { termp_quote_pre, termp_quote_post }, /* Bo */
188 { termp_quote_pre, termp_quote_post }, /* Bq */
189 { termp_xx_pre, termp_xx_post }, /* Bsx */
190 { NULL, NULL }, /* Bx */
191 { termp_skip_pre, NULL }, /* Db */
192 { NULL, NULL }, /* Dc */
193 { termp_quote_pre, termp_quote_post }, /* Do */
194 { termp_quote_pre, termp_quote_post }, /* Dq */
195 { NULL, NULL }, /* Ec */ /* FIXME: no space */
196 { NULL, NULL }, /* Ef */
197 { termp_em_pre, NULL }, /* Em */
198 { termp_eo_pre, termp_eo_post }, /* Eo */
199 { termp_xx_pre, termp_xx_post }, /* Fx */
200 { termp_bold_pre, NULL }, /* Ms */
201 { termp_li_pre, NULL }, /* No */
202 { termp_ns_pre, NULL }, /* Ns */
203 { termp_xx_pre, termp_xx_post }, /* Nx */
204 { termp_xx_pre, termp_xx_post }, /* Ox */
205 { NULL, NULL }, /* Pc */
206 { NULL, termp_pf_post }, /* Pf */
207 { termp_quote_pre, termp_quote_post }, /* Po */
208 { termp_quote_pre, termp_quote_post }, /* Pq */
209 { NULL, NULL }, /* Qc */
210 { termp_quote_pre, termp_quote_post }, /* Ql */
211 { termp_quote_pre, termp_quote_post }, /* Qo */
212 { termp_quote_pre, termp_quote_post }, /* Qq */
213 { NULL, NULL }, /* Re */
214 { termp_rs_pre, NULL }, /* Rs */
215 { NULL, NULL }, /* Sc */
216 { termp_quote_pre, termp_quote_post }, /* So */
217 { termp_quote_pre, termp_quote_post }, /* Sq */
218 { termp_sm_pre, NULL }, /* Sm */
219 { termp_under_pre, NULL }, /* Sx */
220 { termp_sy_pre, NULL }, /* Sy */
221 { NULL, NULL }, /* Tn */
222 { termp_xx_pre, termp_xx_post }, /* Ux */
223 { NULL, NULL }, /* Xc */
224 { NULL, NULL }, /* Xo */
225 { termp_fo_pre, termp_fo_post }, /* Fo */
226 { NULL, NULL }, /* Fc */
227 { termp_quote_pre, termp_quote_post }, /* Oo */
228 { NULL, NULL }, /* Oc */
229 { termp_bk_pre, termp_bk_post }, /* Bk */
230 { NULL, NULL }, /* Ek */
231 { NULL, NULL }, /* Bt */
232 { NULL, NULL }, /* Hf */
233 { termp_under_pre, NULL }, /* Fr */
234 { NULL, NULL }, /* Ud */
235 { NULL, termp_lb_post }, /* Lb */
236 { termp_sp_pre, NULL }, /* Lp */
237 { termp_lk_pre, NULL }, /* Lk */
238 { termp_under_pre, NULL }, /* Mt */
239 { termp_quote_pre, termp_quote_post }, /* Brq */
240 { termp_quote_pre, termp_quote_post }, /* Bro */
241 { NULL, NULL }, /* Brc */
242 { NULL, termp____post }, /* %C */
243 { termp_skip_pre, NULL }, /* Es */
244 { termp_quote_pre, termp_quote_post }, /* En */
245 { termp_xx_pre, termp_xx_post }, /* Dx */
246 { NULL, termp____post }, /* %Q */
247 { termp_sp_pre, NULL }, /* br */
248 { termp_sp_pre, NULL }, /* sp */
249 { NULL, termp____post }, /* %U */
250 { NULL, NULL }, /* Ta */
251 { termp_ll_pre, NULL }, /* ll */
257 terminal_mdoc(void *arg, const struct roff_man *mdoc)
261 size_t save_defindent;
263 p = (struct termp *)arg;
265 p->rmargin = p->maxrmargin = p->defrmargin;
266 p->tabwidth = term_len(p, 5);
268 n = mdoc->first->child;
269 if (p->synopsisonly) {
271 if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) {
272 if (n->child->next->child != NULL)
273 print_mdoc_nodelist(p, NULL,
275 n->child->next->child);
282 save_defindent = p->defindent;
283 if (p->defindent == 0)
285 term_begin(p, print_mdoc_head, print_mdoc_foot,
287 while (n != NULL && n->flags & NODE_NOPRT)
290 if (n->tok != MDOC_Sh)
292 print_mdoc_nodelist(p, NULL, &mdoc->meta, n);
295 p->defindent = save_defindent;
300 print_mdoc_nodelist(DECL_ARGS)
304 print_mdoc_node(p, pair, meta, n);
310 print_mdoc_node(DECL_ARGS)
313 struct termpair npair;
314 size_t offset, rmargin;
316 if (n->flags & NODE_NOPRT)
321 rmargin = p->rmargin;
322 n->flags &= ~NODE_ENDED;
323 n->prev_font = p->fonti;
325 memset(&npair, 0, sizeof(struct termpair));
329 * Keeps only work until the end of a line. If a keep was
330 * invoked in a prior line, revert it to PREKEEP.
333 if (p->flags & TERMP_KEEP && n->flags & NODE_LINE) {
334 p->flags &= ~TERMP_KEEP;
335 p->flags |= TERMP_PREKEEP;
339 * After the keep flags have been set up, we may now
340 * produce output. Note that some pre-handlers do so.
345 if (' ' == *n->string && NODE_LINE & n->flags)
347 if (NODE_DELIMC & n->flags)
348 p->flags |= TERMP_NOSPACE;
349 term_word(p, n->string);
350 if (NODE_DELIMO & n->flags)
351 p->flags |= TERMP_NOSPACE;
354 if ( ! (n->flags & NODE_LINE))
355 p->flags |= TERMP_NOSPACE;
357 if (n->next != NULL && ! (n->next->flags & NODE_LINE))
358 p->flags |= TERMP_NOSPACE;
361 if (p->tbl.cols == NULL)
363 term_tbl(p, n->span);
366 if (termacts[n->tok].pre &&
367 (n->end == ENDBODY_NOT || n->child != NULL))
368 chld = (*termacts[n->tok].pre)
369 (p, &npair, meta, n);
373 if (chld && n->child)
374 print_mdoc_nodelist(p, &npair, meta, n->child);
377 (ENDBODY_NOT == n->end ? n : n->body)->prev_font);
387 if ( ! termacts[n->tok].post || NODE_ENDED & n->flags)
389 (void)(*termacts[n->tok].post)(p, &npair, meta, n);
392 * Explicit end tokens not only call the post
393 * handler, but also tell the respective block
394 * that it must not call the post handler again.
396 if (ENDBODY_NOT != n->end)
397 n->body->flags |= NODE_ENDED;
401 if (NODE_EOS & n->flags)
402 p->flags |= TERMP_SENTENCE;
404 if (MDOC_ll != n->tok) {
406 p->rmargin = rmargin;
411 print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
415 term_fontrepl(p, TERMFONT_NONE);
418 * Output the footer in new-groff style, that is, three columns
419 * with the middle being the manual date and flanking columns
420 * being the operating system:
428 sz = term_strlen(p, meta->date);
429 p->rmargin = p->maxrmargin > sz ?
430 (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
432 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
434 term_word(p, meta->os);
437 p->offset = p->rmargin;
438 sz = term_strlen(p, meta->os);
439 p->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
440 p->flags |= TERMP_NOSPACE;
442 term_word(p, meta->date);
445 p->offset = p->rmargin;
446 p->rmargin = p->maxrmargin;
448 p->flags &= ~TERMP_NOBREAK;
449 p->flags |= TERMP_NOSPACE;
451 term_word(p, meta->os);
455 p->rmargin = p->maxrmargin;
460 print_mdoc_head(struct termp *p, const struct roff_meta *meta)
462 char *volume, *title;
463 size_t vollen, titlen;
466 * The header is strange. It has three components, which are
467 * really two with the first duplicated. It goes like this:
469 * IDENTIFIER TITLE IDENTIFIER
471 * The IDENTIFIER is NAME(SECTION), which is the command-name
472 * (if given, or "unknown" if not) followed by the manual page
473 * section. These are given in `Dt'. The TITLE is a free-form
474 * string depending on the manual volume. If not specified, it
475 * switches on the manual section.
479 if (NULL == meta->arch)
480 volume = mandoc_strdup(meta->vol);
482 mandoc_asprintf(&volume, "%s (%s)",
483 meta->vol, meta->arch);
484 vollen = term_strlen(p, volume);
486 if (NULL == meta->msec)
487 title = mandoc_strdup(meta->title);
489 mandoc_asprintf(&title, "%s(%s)",
490 meta->title, meta->msec);
491 titlen = term_strlen(p, title);
493 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
496 p->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
497 (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
498 vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
503 p->flags |= TERMP_NOSPACE;
504 p->offset = p->rmargin;
505 p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
506 p->maxrmargin - titlen : p->maxrmargin;
508 term_word(p, volume);
511 p->flags &= ~TERMP_NOBREAK;
513 if (p->rmargin + titlen <= p->maxrmargin) {
514 p->flags |= TERMP_NOSPACE;
515 p->offset = p->rmargin;
516 p->rmargin = p->maxrmargin;
521 p->flags &= ~TERMP_NOSPACE;
523 p->rmargin = p->maxrmargin;
529 a2width(const struct termp *p, const char *v)
533 if (a2roffsu(v, &su, SCALE_MAX) < 2) {
534 SCALE_HS_INIT(&su, term_strlen(p, v));
535 su.scale /= term_strlen(p, "0");
537 return term_hspan(p, &su) / 24;
541 * Determine how much space to print out before block elements of `It'
542 * (and thus `Bl') and `Bd'. And then go ahead and print that space,
546 print_bvspace(struct termp *p,
547 const struct roff_node *bl,
548 const struct roff_node *n)
550 const struct roff_node *nn;
556 if (MDOC_Bd == bl->tok && bl->norm->Bd.comp)
558 if (MDOC_Bl == bl->tok && bl->norm->Bl.comp)
561 /* Do not vspace directly after Ss/Sh. */
564 while (nn->prev != NULL && nn->prev->flags & NODE_NOPRT)
566 while (nn->prev == NULL) {
569 if (nn->type == ROFFT_ROOT)
571 } while (nn->type != ROFFT_BLOCK);
572 if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
574 if (nn->tok == MDOC_It &&
575 nn->parent->parent->norm->Bl.type != LIST_item)
579 /* A `-column' does not assert vspace within the list. */
581 if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type)
582 if (n->prev && MDOC_It == n->prev->tok)
585 /* A `-diag' without body does not vspace. */
587 if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type)
588 if (n->prev && MDOC_It == n->prev->tok) {
589 assert(n->prev->body);
590 if (NULL == n->prev->body->child)
599 termp_ll_pre(DECL_ARGS)
602 term_setwidth(p, n->child != NULL ? n->child->string : NULL);
607 termp_it_pre(DECL_ARGS)
611 const struct roff_node *bl, *nn;
613 int i, offset, width;
616 if (n->type == ROFFT_BLOCK) {
617 print_bvspace(p, n->parent->parent, n);
621 bl = n->parent->parent->parent;
622 type = bl->norm->Bl.type;
625 * Defaults for specific list types.
633 width = term_len(p, 2);
637 width = term_len(p, 8);
640 width = term_len(p, 10);
649 * First calculate width and offset. This is pretty easy unless
650 * we're a -column list, in which case all prior columns must
654 if (bl->norm->Bl.offs != NULL) {
655 offset = a2width(p, bl->norm->Bl.offs);
656 if (offset < 0 && (size_t)(-offset) > p->offset)
658 else if (offset > SHRT_MAX)
664 if (n->type == ROFFT_HEAD)
668 * Imitate groff's column handling:
669 * - For each earlier column, add its width.
670 * - For less than 5 columns, add four more blanks per
672 * - For exactly 5 columns, add three more blank per
674 * - For more than 5 columns, add only one column.
676 ncols = bl->norm->Bl.ncols;
677 dcol = ncols < 5 ? term_len(p, 4) :
678 ncols == 5 ? term_len(p, 3) : term_len(p, 1);
681 * Calculate the offset by applying all prior ROFFT_BODY,
682 * so we stop at the ROFFT_HEAD (nn->prev == NULL).
685 for (i = 0, nn = n->prev;
686 nn->prev && i < (int)ncols;
687 nn = nn->prev, i++) {
689 term_strlen(p, bl->norm->Bl.cols[i]));
690 su.scale /= term_strlen(p, "0");
691 offset += term_hspan(p, &su) / 24 + dcol;
695 * When exceeding the declared number of columns, leave
696 * the remaining widths at 0. This will later be
697 * adjusted to the default width of 10, or, for the last
698 * column, stretched to the right margin.
704 * Use the declared column widths, extended as explained
705 * in the preceding paragraph.
707 SCALE_HS_INIT(&su, term_strlen(p, bl->norm->Bl.cols[i]));
708 su.scale /= term_strlen(p, "0");
709 width = term_hspan(p, &su) / 24 + dcol;
712 if (NULL == bl->norm->Bl.width)
716 * Note: buffer the width by 2, which is groff's magic
717 * number for buffering single arguments. See the above
718 * handling for column for how this changes.
720 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
721 if (width < 0 && (size_t)(-width) > p->offset)
723 else if (width > SHRT_MAX)
729 * Whitespace control. Inset bodies need an initial space,
730 * while diagonal bodies need two.
733 p->flags |= TERMP_NOSPACE;
737 if (n->type == ROFFT_BODY)
738 term_word(p, "\\ \\ ");
741 if (n->type == ROFFT_BODY && n->parent->head->child != NULL)
748 p->flags |= TERMP_NOSPACE;
752 if (n->type == ROFFT_HEAD)
753 term_fontpush(p, TERMFONT_BOLD);
760 * Pad and break control. This is the tricky part. These flags
761 * are documented in term_flushln() in term.c. Note that we're
762 * going to unset all of these flags in termp_it_post() when we
772 * Weird special case.
773 * Some very narrow lists actually hang.
775 if (width <= (int)term_len(p, 2))
776 p->flags |= TERMP_HANG;
777 if (n->type != ROFFT_HEAD)
779 p->flags |= TERMP_NOBREAK;
783 if (n->type != ROFFT_HEAD)
787 * This is ugly. If `-hang' is specified and the body
788 * is a `Bl' or `Bd', then we want basically to nullify
789 * the "overstep" effect in term_flushln() and treat
790 * this as a `-ohang' list instead.
792 if (NULL != n->next &&
793 NULL != n->next->child &&
794 (MDOC_Bl == n->next->child->tok ||
795 MDOC_Bd == n->next->child->tok))
798 p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
802 if (n->type != ROFFT_HEAD)
805 p->flags |= TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND;
808 if (NULL == n->next || NULL == n->next->child)
809 p->flags |= TERMP_DANGLE;
812 if (n->type == ROFFT_HEAD)
815 if (NULL == n->next) {
816 p->flags &= ~TERMP_NOBREAK;
819 p->flags |= TERMP_NOBREAK;
825 if (n->type != ROFFT_HEAD)
827 p->flags |= TERMP_NOBREAK | TERMP_BRIND;
835 * Margin control. Set-head-width lists have their right
836 * margins shortened. The body for these lists has the offset
837 * necessarily lengthened. Everybody gets the offset.
845 * Same stipulation as above, regarding `-hang'. We
846 * don't want to recalculate rmargin and offsets when
847 * using `Bd' or `Bl' within `-hang' overstep lists.
849 if (n->type == ROFFT_HEAD &&
851 NULL != n->next->child &&
852 (MDOC_Bl == n->next->child->tok ||
853 MDOC_Bd == n->next->child->tok))
861 if (n->type == ROFFT_HEAD)
862 p->rmargin = p->offset + width;
868 p->rmargin = p->offset + width;
870 * XXX - this behaviour is not documented: the
871 * right-most column is filled to the right margin.
873 if (n->type == ROFFT_HEAD)
875 if (NULL == n->next && p->rmargin < p->maxrmargin)
876 p->rmargin = p->maxrmargin;
883 * The dash, hyphen, bullet and enum lists all have a special
884 * HEAD character (temporarily bold, in some cases).
887 if (n->type == ROFFT_HEAD)
890 term_fontpush(p, TERMFONT_BOLD);
891 term_word(p, "\\[bu]");
896 term_fontpush(p, TERMFONT_BOLD);
901 (pair->ppair->ppair->count)++;
902 (void)snprintf(buf, sizeof(buf), "%d.",
903 pair->ppair->ppair->count);
911 * If we're not going to process our children, indicate so here.
920 if (n->type == ROFFT_HEAD)
924 if (n->type == ROFFT_HEAD)
935 termp_it_post(DECL_ARGS)
939 if (n->type == ROFFT_BLOCK)
942 type = n->parent->parent->parent->norm->Bl.type;
948 if (n->type == ROFFT_BODY)
952 if (n->type == ROFFT_BODY)
961 * Now that our output is flushed, we can reset our tags. Since
962 * only `It' sets these flags, we're free to assume that nobody
963 * has munged them in the meanwhile.
966 p->flags &= ~(TERMP_NOBREAK | TERMP_BRTRSP | TERMP_BRIND |
967 TERMP_DANGLE | TERMP_HANG);
972 termp_nm_pre(DECL_ARGS)
976 if (n->type == ROFFT_BLOCK) {
977 p->flags |= TERMP_PREKEEP;
981 if (n->type == ROFFT_BODY) {
982 if (NULL == n->child)
984 p->flags |= TERMP_NOSPACE;
986 if (n->prev->child != NULL)
987 cp = n->prev->child->string;
991 p->offset += term_len(p, 6);
993 p->offset += term_len(p, 1) + term_strlen(p, cp);
997 if (n->child == NULL)
1000 if (n->type == ROFFT_HEAD)
1001 synopsis_pre(p, n->parent);
1003 if (n->type == ROFFT_HEAD &&
1004 NULL != n->next && NULL != n->next->child) {
1005 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
1007 p->rmargin = p->offset + term_len(p, 1);
1008 if (NULL == n->child) {
1009 p->rmargin += term_strlen(p, meta->name);
1010 } else if (n->child->type == ROFFT_TEXT) {
1011 p->rmargin += term_strlen(p, n->child->string);
1013 p->flags |= TERMP_HANG;
1015 p->rmargin += term_len(p, 5);
1016 p->flags |= TERMP_HANG;
1020 term_fontpush(p, TERMFONT_BOLD);
1025 termp_nm_post(DECL_ARGS)
1028 if (n->type == ROFFT_BLOCK) {
1029 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1030 } else if (n->type == ROFFT_HEAD &&
1031 NULL != n->next && NULL != n->next->child) {
1033 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1035 } else if (n->type == ROFFT_BODY && n->child != NULL)
1040 termp_fl_pre(DECL_ARGS)
1043 termp_tag_pre(p, pair, meta, n);
1044 term_fontpush(p, TERMFONT_BOLD);
1045 term_word(p, "\\-");
1047 if (!(n->child == NULL &&
1049 n->next->type == ROFFT_TEXT ||
1050 n->next->flags & NODE_LINE)))
1051 p->flags |= TERMP_NOSPACE;
1057 termp__a_pre(DECL_ARGS)
1060 if (n->prev && MDOC__A == n->prev->tok)
1061 if (NULL == n->next || MDOC__A != n->next->tok)
1062 term_word(p, "and");
1068 termp_an_pre(DECL_ARGS)
1071 if (n->norm->An.auth == AUTH_split) {
1072 p->flags &= ~TERMP_NOSPLIT;
1073 p->flags |= TERMP_SPLIT;
1076 if (n->norm->An.auth == AUTH_nosplit) {
1077 p->flags &= ~TERMP_SPLIT;
1078 p->flags |= TERMP_NOSPLIT;
1082 if (p->flags & TERMP_SPLIT)
1085 if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
1086 p->flags |= TERMP_SPLIT;
1092 termp_ns_pre(DECL_ARGS)
1095 if ( ! (NODE_LINE & n->flags))
1096 p->flags |= TERMP_NOSPACE;
1101 termp_rs_pre(DECL_ARGS)
1104 if (SEC_SEE_ALSO != n->sec)
1106 if (n->type == ROFFT_BLOCK && n->prev != NULL)
1112 termp_ex_pre(DECL_ARGS)
1119 termp_nd_pre(DECL_ARGS)
1122 if (n->type == ROFFT_BODY)
1123 term_word(p, "\\(en");
1128 termp_bl_pre(DECL_ARGS)
1131 return n->type != ROFFT_HEAD;
1135 termp_bl_post(DECL_ARGS)
1138 if (n->type == ROFFT_BLOCK)
1143 termp_xr_pre(DECL_ARGS)
1146 if (NULL == (n = n->child))
1149 assert(n->type == ROFFT_TEXT);
1150 term_word(p, n->string);
1152 if (NULL == (n = n->next))
1155 p->flags |= TERMP_NOSPACE;
1157 p->flags |= TERMP_NOSPACE;
1159 assert(n->type == ROFFT_TEXT);
1160 term_word(p, n->string);
1162 p->flags |= TERMP_NOSPACE;
1169 * This decides how to assert whitespace before any of the SYNOPSIS set
1170 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
1174 synopsis_pre(struct termp *p, const struct roff_node *n)
1177 * Obviously, if we're not in a SYNOPSIS or no prior macros
1178 * exist, do nothing.
1180 if (NULL == n->prev || ! (NODE_SYNPRETTY & n->flags))
1184 * If we're the second in a pair of like elements, emit our
1185 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which
1186 * case we soldier on.
1188 if (n->prev->tok == n->tok &&
1189 MDOC_Ft != n->tok &&
1190 MDOC_Fo != n->tok &&
1191 MDOC_Fn != n->tok) {
1197 * If we're one of the SYNOPSIS set and non-like pair-wise after
1198 * another (or Fn/Fo, which we've let slip through) then assert
1199 * vertical space, else only newline and move on.
1201 switch (n->prev->tok) {
1210 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
1222 termp_vt_pre(DECL_ARGS)
1225 if (n->type == ROFFT_ELEM) {
1227 return termp_under_pre(p, pair, meta, n);
1228 } else if (n->type == ROFFT_BLOCK) {
1231 } else if (n->type == ROFFT_HEAD)
1234 return termp_under_pre(p, pair, meta, n);
1238 termp_bold_pre(DECL_ARGS)
1241 termp_tag_pre(p, pair, meta, n);
1242 term_fontpush(p, TERMFONT_BOLD);
1247 termp_fd_pre(DECL_ARGS)
1251 return termp_bold_pre(p, pair, meta, n);
1255 termp_fd_post(DECL_ARGS)
1262 termp_sh_pre(DECL_ARGS)
1268 * Vertical space before sections, except
1269 * when the previous section was empty.
1271 if (n->prev == NULL ||
1272 n->prev->tok != MDOC_Sh ||
1273 (n->prev->body != NULL &&
1274 n->prev->body->child != NULL))
1278 term_fontpush(p, TERMFONT_BOLD);
1281 p->offset = term_len(p, p->defindent);
1283 case SEC_DESCRIPTION:
1287 p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
1300 termp_sh_post(DECL_ARGS)
1317 termp_lb_post(DECL_ARGS)
1320 if (SEC_LIBRARY == n->sec && NODE_LINE & n->flags)
1325 termp_d1_pre(DECL_ARGS)
1328 if (n->type != ROFFT_BLOCK)
1331 p->offset += term_len(p, p->defindent + 1);
1336 termp_ft_pre(DECL_ARGS)
1339 /* NB: NODE_LINE does not effect this! */
1341 term_fontpush(p, TERMFONT_UNDER);
1346 termp_fn_pre(DECL_ARGS)
1351 pretty = NODE_SYNPRETTY & n->flags;
1355 if (NULL == (n = n->child))
1359 rmargin = p->rmargin;
1360 p->rmargin = p->offset + term_len(p, 4);
1361 p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
1364 assert(n->type == ROFFT_TEXT);
1365 term_fontpush(p, TERMFONT_BOLD);
1366 term_word(p, n->string);
1369 if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM)
1370 tag_put(n->string, ++fn_prio, p->line);
1374 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
1375 p->offset = p->rmargin;
1376 p->rmargin = rmargin;
1379 p->flags |= TERMP_NOSPACE;
1381 p->flags |= TERMP_NOSPACE;
1383 for (n = n->next; n; n = n->next) {
1384 assert(n->type == ROFFT_TEXT);
1385 term_fontpush(p, TERMFONT_UNDER);
1387 p->flags |= TERMP_NBRWORD;
1388 term_word(p, n->string);
1392 p->flags |= TERMP_NOSPACE;
1397 p->flags |= TERMP_NOSPACE;
1401 p->flags |= TERMP_NOSPACE;
1410 termp_fa_pre(DECL_ARGS)
1412 const struct roff_node *nn;
1414 if (n->parent->tok != MDOC_Fo) {
1415 term_fontpush(p, TERMFONT_UNDER);
1419 for (nn = n->child; nn; nn = nn->next) {
1420 term_fontpush(p, TERMFONT_UNDER);
1421 p->flags |= TERMP_NBRWORD;
1422 term_word(p, nn->string);
1425 if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
1426 p->flags |= TERMP_NOSPACE;
1435 termp_bd_pre(DECL_ARGS)
1437 size_t tabwidth, lm, len, rm, rmax;
1438 struct roff_node *nn;
1441 if (n->type == ROFFT_BLOCK) {
1442 print_bvspace(p, n, n);
1444 } else if (n->type == ROFFT_HEAD)
1447 /* Handle the -offset argument. */
1449 if (n->norm->Bd.offs == NULL ||
1450 ! strcmp(n->norm->Bd.offs, "left"))
1452 else if ( ! strcmp(n->norm->Bd.offs, "indent"))
1453 p->offset += term_len(p, p->defindent + 1);
1454 else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
1455 p->offset += term_len(p, (p->defindent + 1) * 2);
1457 offset = a2width(p, n->norm->Bd.offs);
1458 if (offset < 0 && (size_t)(-offset) > p->offset)
1460 else if (offset < SHRT_MAX)
1461 p->offset += offset;
1465 * If -ragged or -filled are specified, the block does nothing
1466 * but change the indentation. If -unfilled or -literal are
1467 * specified, text is printed exactly as entered in the display:
1468 * for macro lines, a newline is appended to the line. Blank
1469 * lines are allowed.
1472 if (DISP_literal != n->norm->Bd.type &&
1473 DISP_unfilled != n->norm->Bd.type &&
1474 DISP_centered != n->norm->Bd.type)
1477 tabwidth = p->tabwidth;
1478 if (DISP_literal == n->norm->Bd.type)
1479 p->tabwidth = term_len(p, 8);
1483 rmax = p->maxrmargin;
1484 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1486 for (nn = n->child; nn; nn = nn->next) {
1487 if (DISP_centered == n->norm->Bd.type) {
1488 if (nn->type == ROFFT_TEXT) {
1489 len = term_strlen(p, nn->string);
1490 p->offset = len >= rm ? 0 :
1491 lm + len >= rm ? rm - len :
1492 (lm + rm - len) / 2;
1496 print_mdoc_node(p, pair, meta, nn);
1498 * If the printed node flushes its own line, then we
1499 * needn't do it here as well. This is hacky, but the
1500 * notion of selective eoln whitespace is pretty dumb
1501 * anyway, so don't sweat it.
1516 if (p->flags & TERMP_NONEWLINE ||
1517 (nn->next && ! (nn->next->flags & NODE_LINE)))
1520 p->flags |= TERMP_NOSPACE;
1523 p->tabwidth = tabwidth;
1525 p->maxrmargin = rmax;
1530 termp_bd_post(DECL_ARGS)
1534 if (n->type != ROFFT_BODY)
1538 rmax = p->maxrmargin;
1540 if (DISP_literal == n->norm->Bd.type ||
1541 DISP_unfilled == n->norm->Bd.type)
1542 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1544 p->flags |= TERMP_NOSPACE;
1548 p->maxrmargin = rmax;
1552 termp_xx_pre(DECL_ARGS)
1554 if ((n->aux = p->flags & TERMP_PREKEEP) == 0)
1555 p->flags |= TERMP_PREKEEP;
1560 termp_xx_post(DECL_ARGS)
1563 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
1567 termp_pf_post(DECL_ARGS)
1570 if ( ! (n->next == NULL || n->next->flags & NODE_LINE))
1571 p->flags |= TERMP_NOSPACE;
1575 termp_ss_pre(DECL_ARGS)
1577 struct roff_node *nn;
1582 for (nn = n->prev; nn != NULL; nn = nn->prev)
1583 if ((nn->flags & NODE_NOPRT) == 0)
1589 term_fontpush(p, TERMFONT_BOLD);
1590 p->offset = term_len(p, (p->defindent+1)/2);
1593 p->offset = term_len(p, p->defindent);
1603 termp_ss_post(DECL_ARGS)
1606 if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
1611 termp_cd_pre(DECL_ARGS)
1615 term_fontpush(p, TERMFONT_BOLD);
1620 termp_in_pre(DECL_ARGS)
1625 if (NODE_SYNPRETTY & n->flags && NODE_LINE & n->flags) {
1626 term_fontpush(p, TERMFONT_BOLD);
1627 term_word(p, "#include");
1631 term_fontpush(p, TERMFONT_UNDER);
1634 p->flags |= TERMP_NOSPACE;
1639 termp_in_post(DECL_ARGS)
1642 if (NODE_SYNPRETTY & n->flags)
1643 term_fontpush(p, TERMFONT_BOLD);
1645 p->flags |= TERMP_NOSPACE;
1648 if (NODE_SYNPRETTY & n->flags)
1653 termp_sp_pre(DECL_ARGS)
1661 if ( ! a2roffsu(n->child->string, &su, SCALE_VS))
1663 len = term_vspan(p, &su);
1681 for (i = 0; i < len; i++)
1688 termp_skip_pre(DECL_ARGS)
1695 termp_quote_pre(DECL_ARGS)
1698 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1704 term_word(p, n->child != NULL && n->child->next == NULL &&
1705 n->child->tok == MDOC_Mt ? "<" : "\\(la");
1721 term_word(p, "\\(Lq");
1724 if (NULL == n->norm->Es ||
1725 NULL == n->norm->Es->child)
1727 term_word(p, n->norm->Es->child->string);
1740 term_word(p, "\\(oq");
1746 p->flags |= TERMP_NOSPACE;
1751 termp_quote_post(DECL_ARGS)
1754 if (n->type != ROFFT_BODY && n->type != ROFFT_ELEM)
1757 p->flags |= TERMP_NOSPACE;
1762 term_word(p, n->child != NULL && n->child->next == NULL &&
1763 n->child->tok == MDOC_Mt ? ">" : "\\(ra");
1779 term_word(p, "\\(Rq");
1782 if (n->norm->Es == NULL ||
1783 n->norm->Es->child == NULL ||
1784 n->norm->Es->child->next == NULL)
1785 p->flags &= ~TERMP_NOSPACE;
1787 term_word(p, n->norm->Es->child->next->string);
1800 term_word(p, "\\(cq");
1808 termp_eo_pre(DECL_ARGS)
1811 if (n->type != ROFFT_BODY)
1814 if (n->end == ENDBODY_NOT &&
1815 n->parent->head->child == NULL &&
1817 n->child->end != ENDBODY_NOT)
1818 term_word(p, "\\&");
1819 else if (n->end != ENDBODY_NOT ? n->child != NULL :
1820 n->parent->head->child != NULL && (n->child != NULL ||
1821 (n->parent->tail != NULL && n->parent->tail->child != NULL)))
1822 p->flags |= TERMP_NOSPACE;
1828 termp_eo_post(DECL_ARGS)
1832 if (n->type != ROFFT_BODY)
1835 if (n->end != ENDBODY_NOT) {
1836 p->flags &= ~TERMP_NOSPACE;
1840 body = n->child != NULL || n->parent->head->child != NULL;
1841 tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
1844 p->flags |= TERMP_NOSPACE;
1845 else if ( ! (body || tail))
1846 term_word(p, "\\&");
1848 p->flags &= ~TERMP_NOSPACE;
1852 termp_fo_pre(DECL_ARGS)
1857 pretty = NODE_SYNPRETTY & n->flags;
1859 if (n->type == ROFFT_BLOCK) {
1862 } else if (n->type == ROFFT_BODY) {
1864 rmargin = p->rmargin;
1865 p->rmargin = p->offset + term_len(p, 4);
1866 p->flags |= TERMP_NOBREAK | TERMP_BRIND |
1869 p->flags |= TERMP_NOSPACE;
1871 p->flags |= TERMP_NOSPACE;
1874 p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
1876 p->offset = p->rmargin;
1877 p->rmargin = rmargin;
1882 if (NULL == n->child)
1885 /* XXX: we drop non-initial arguments as per groff. */
1887 assert(n->child->string);
1888 term_fontpush(p, TERMFONT_BOLD);
1889 term_word(p, n->child->string);
1894 termp_fo_post(DECL_ARGS)
1897 if (n->type != ROFFT_BODY)
1900 p->flags |= TERMP_NOSPACE;
1903 if (NODE_SYNPRETTY & n->flags) {
1904 p->flags |= TERMP_NOSPACE;
1911 termp_bf_pre(DECL_ARGS)
1914 if (n->type == ROFFT_HEAD)
1916 else if (n->type != ROFFT_BODY)
1919 if (FONT_Em == n->norm->Bf.font)
1920 term_fontpush(p, TERMFONT_UNDER);
1921 else if (FONT_Sy == n->norm->Bf.font)
1922 term_fontpush(p, TERMFONT_BOLD);
1924 term_fontpush(p, TERMFONT_NONE);
1930 termp_sm_pre(DECL_ARGS)
1933 if (NULL == n->child)
1934 p->flags ^= TERMP_NONOSPACE;
1935 else if (0 == strcmp("on", n->child->string))
1936 p->flags &= ~TERMP_NONOSPACE;
1938 p->flags |= TERMP_NONOSPACE;
1940 if (p->col && ! (TERMP_NONOSPACE & p->flags))
1941 p->flags &= ~TERMP_NOSPACE;
1947 termp_ap_pre(DECL_ARGS)
1950 p->flags |= TERMP_NOSPACE;
1952 p->flags |= TERMP_NOSPACE;
1957 termp____post(DECL_ARGS)
1961 * Handle lists of authors. In general, print each followed by
1962 * a comma. Don't print the comma if there are only two
1965 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
1966 if (NULL == n->next->next || MDOC__A != n->next->next->tok)
1967 if (NULL == n->prev || MDOC__A != n->prev->tok)
1972 if (NULL == n->parent || MDOC_Rs != n->parent->tok)
1975 p->flags |= TERMP_NOSPACE;
1976 if (NULL == n->next) {
1978 p->flags |= TERMP_SENTENCE;
1984 termp_li_pre(DECL_ARGS)
1987 termp_tag_pre(p, pair, meta, n);
1988 term_fontpush(p, TERMFONT_NONE);
1993 termp_lk_pre(DECL_ARGS)
1995 const struct roff_node *link, *descr;
1997 if (NULL == (link = n->child))
2000 if (NULL != (descr = link->next)) {
2001 term_fontpush(p, TERMFONT_UNDER);
2002 while (NULL != descr) {
2003 term_word(p, descr->string);
2004 descr = descr->next;
2006 p->flags |= TERMP_NOSPACE;
2011 term_fontpush(p, TERMFONT_BOLD);
2012 term_word(p, link->string);
2019 termp_bk_pre(DECL_ARGS)
2028 if (n->parent->args != NULL || n->prev->child == NULL)
2029 p->flags |= TERMP_PREKEEP;
2039 termp_bk_post(DECL_ARGS)
2042 if (n->type == ROFFT_BODY)
2043 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2047 termp__t_post(DECL_ARGS)
2051 * If we're in an `Rs' and there's a journal present, then quote
2052 * us instead of underlining us (for disambiguation).
2054 if (n->parent && MDOC_Rs == n->parent->tok &&
2055 n->parent->norm->Rs.quote_T)
2056 termp_quote_post(p, pair, meta, n);
2058 termp____post(p, pair, meta, n);
2062 termp__t_pre(DECL_ARGS)
2066 * If we're in an `Rs' and there's a journal present, then quote
2067 * us instead of underlining us (for disambiguation).
2069 if (n->parent && MDOC_Rs == n->parent->tok &&
2070 n->parent->norm->Rs.quote_T)
2071 return termp_quote_pre(p, pair, meta, n);
2073 term_fontpush(p, TERMFONT_UNDER);
2078 termp_under_pre(DECL_ARGS)
2081 term_fontpush(p, TERMFONT_UNDER);
2086 termp_em_pre(DECL_ARGS)
2088 if (n->child != NULL &&
2089 n->child->type == ROFFT_TEXT)
2090 tag_put(n->child->string, 0, p->line);
2091 term_fontpush(p, TERMFONT_UNDER);
2096 termp_sy_pre(DECL_ARGS)
2098 if (n->child != NULL &&
2099 n->child->type == ROFFT_TEXT)
2100 tag_put(n->child->string, 0, p->line);
2101 term_fontpush(p, TERMFONT_BOLD);
2106 termp_er_pre(DECL_ARGS)
2109 if (n->sec == SEC_ERRORS &&
2110 (n->parent->tok == MDOC_It ||
2111 (n->parent->tok == MDOC_Bq &&
2112 n->parent->parent->parent->tok == MDOC_It)))
2113 tag_put(n->child->string, 1, p->line);
2118 termp_tag_pre(DECL_ARGS)
2121 if (n->child != NULL &&
2122 n->child->type == ROFFT_TEXT &&
2124 (n->prev->type == ROFFT_TEXT &&
2125 strcmp(n->prev->string, "|") == 0)) &&
2126 (n->parent->tok == MDOC_It ||
2127 (n->parent->tok == MDOC_Xo &&
2128 n->parent->parent->prev == NULL &&
2129 n->parent->parent->parent->tok == MDOC_It)))
2130 tag_put(n->child->string, 1, p->line);