1 /* $Id: mdoc_term.c,v 1.208 2011/01/06 14:05:12 kristaps Exp $ */
3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
22 #include <sys/types.h>
42 struct termpair *ppair;
46 #define DECL_ARGS struct termp *p, \
47 struct termpair *pair, \
48 const struct mdoc_meta *m, \
49 const struct mdoc_node *n
52 int (*pre)(DECL_ARGS);
53 void (*post)(DECL_ARGS);
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 *);
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 *);
70 static void termp____post(DECL_ARGS);
71 static void termp__t_post(DECL_ARGS);
72 static void termp_an_post(DECL_ARGS);
73 static void termp_bd_post(DECL_ARGS);
74 static void termp_bk_post(DECL_ARGS);
75 static void termp_bl_post(DECL_ARGS);
76 static void termp_bx_post(DECL_ARGS);
77 static void termp_d1_post(DECL_ARGS);
78 static void termp_fo_post(DECL_ARGS);
79 static void termp_in_post(DECL_ARGS);
80 static void termp_it_post(DECL_ARGS);
81 static void termp_lb_post(DECL_ARGS);
82 static void termp_nm_post(DECL_ARGS);
83 static void termp_pf_post(DECL_ARGS);
84 static void termp_quote_post(DECL_ARGS);
85 static void termp_sh_post(DECL_ARGS);
86 static void termp_ss_post(DECL_ARGS);
88 static int termp__a_pre(DECL_ARGS);
89 static int termp__t_pre(DECL_ARGS);
90 static int termp_an_pre(DECL_ARGS);
91 static int termp_ap_pre(DECL_ARGS);
92 static int termp_bd_pre(DECL_ARGS);
93 static int termp_bf_pre(DECL_ARGS);
94 static int termp_bk_pre(DECL_ARGS);
95 static int termp_bl_pre(DECL_ARGS);
96 static int termp_bold_pre(DECL_ARGS);
97 static int termp_bt_pre(DECL_ARGS);
98 static int termp_cd_pre(DECL_ARGS);
99 static int termp_d1_pre(DECL_ARGS);
100 static int termp_ex_pre(DECL_ARGS);
101 static int termp_fa_pre(DECL_ARGS);
102 static int termp_fd_pre(DECL_ARGS);
103 static int termp_fl_pre(DECL_ARGS);
104 static int termp_fn_pre(DECL_ARGS);
105 static int termp_fo_pre(DECL_ARGS);
106 static int termp_ft_pre(DECL_ARGS);
107 static int termp_igndelim_pre(DECL_ARGS);
108 static int termp_in_pre(DECL_ARGS);
109 static int termp_it_pre(DECL_ARGS);
110 static int termp_li_pre(DECL_ARGS);
111 static int termp_lk_pre(DECL_ARGS);
112 static int termp_nd_pre(DECL_ARGS);
113 static int termp_nm_pre(DECL_ARGS);
114 static int termp_ns_pre(DECL_ARGS);
115 static int termp_quote_pre(DECL_ARGS);
116 static int termp_rs_pre(DECL_ARGS);
117 static int termp_rv_pre(DECL_ARGS);
118 static int termp_sh_pre(DECL_ARGS);
119 static int termp_sm_pre(DECL_ARGS);
120 static int termp_sp_pre(DECL_ARGS);
121 static int termp_ss_pre(DECL_ARGS);
122 static int termp_under_pre(DECL_ARGS);
123 static int termp_ud_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_d1_post }, /* D1 */
137 { termp_d1_pre, termp_d1_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, termp_an_post }, /* An */
145 { termp_under_pre, NULL }, /* Ar */
146 { termp_cd_pre, NULL }, /* Cd */
147 { termp_bold_pre, NULL }, /* Cm */
148 { NULL, NULL }, /* Dv */
149 { NULL, NULL }, /* Er */
150 { NULL, NULL }, /* Ev */
151 { termp_ex_pre, NULL }, /* Ex */
152 { termp_fa_pre, NULL }, /* Fa */
153 { termp_fd_pre, NULL }, /* 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 { NULL, NULL }, /* Ot */
164 { termp_under_pre, NULL }, /* Pa */
165 { termp_rv_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, NULL }, /* Bsx */
190 { NULL, termp_bx_post }, /* Bx */
191 { NULL, 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_under_pre, NULL }, /* Em */
198 { NULL, NULL }, /* Eo */
199 { termp_xx_pre, NULL }, /* Fx */
200 { termp_bold_pre, NULL }, /* Ms */
201 { termp_igndelim_pre, NULL }, /* No */
202 { termp_ns_pre, NULL }, /* Ns */
203 { termp_xx_pre, NULL }, /* Nx */
204 { termp_xx_pre, NULL }, /* Ox */
205 { NULL, NULL }, /* Pc */
206 { termp_igndelim_pre, 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_bold_pre, NULL }, /* Sy */
221 { NULL, NULL }, /* Tn */
222 { termp_xx_pre, NULL }, /* 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 { termp_bt_pre, NULL }, /* Bt */
232 { NULL, NULL }, /* Hf */
233 { NULL, NULL }, /* Fr */
234 { termp_ud_pre, 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 { NULL, NULL }, /* Es */ /* TODO */
244 { NULL, NULL }, /* En */ /* TODO */
245 { termp_xx_pre, NULL }, /* Dx */
246 { NULL, termp____post }, /* %Q */
247 { termp_sp_pre, NULL }, /* br */
248 { termp_sp_pre, NULL }, /* sp */
249 { termp_under_pre, termp____post }, /* %U */
250 { NULL, NULL }, /* Ta */
255 terminal_mdoc(void *arg, const struct mdoc *mdoc)
257 const struct mdoc_node *n;
258 const struct mdoc_meta *m;
261 p = (struct termp *)arg;
264 p->maxrmargin = p->defrmargin;
265 p->tabwidth = term_len(p, 5);
267 if (NULL == p->symtab)
269 case (TERMENC_ASCII):
270 p->symtab = chars_init(CHARS_ASCII);
280 term_begin(p, print_mdoc_head, print_mdoc_foot, m);
283 print_mdoc_nodelist(p, NULL, m, n->child);
290 print_mdoc_nodelist(DECL_ARGS)
293 print_mdoc_node(p, pair, m, n);
295 print_mdoc_nodelist(p, pair, m, n->next);
301 print_mdoc_node(DECL_ARGS)
305 struct termpair npair;
306 size_t offset, rmargin;
310 rmargin = p->rmargin;
311 font = term_fontq(p);
313 memset(&npair, 0, sizeof(struct termpair));
318 term_word(p, n->string);
321 term_tbl(p, n->span);
324 if (termacts[n->tok].pre && ENDBODY_NOT == n->end)
325 chld = (*termacts[n->tok].pre)
331 * Keeps only work until the end of a line. If a keep was
332 * invoked in a prior line, revert it to PREKEEP.
334 * Also let SYNPRETTY sections behave as if they were wrapped
338 if (TERMP_KEEP & p->flags || MDOC_SYNPRETTY & n->flags) {
339 if (n->prev && n->prev->line != n->line) {
340 p->flags &= ~TERMP_KEEP;
341 p->flags |= TERMP_PREKEEP;
342 } else if (NULL == n->prev) {
343 if (n->parent && n->parent->line != n->line) {
344 p->flags &= ~TERMP_KEEP;
345 p->flags |= TERMP_PREKEEP;
351 * Since SYNPRETTY sections aren't "turned off" with `Ek',
352 * we have to intuit whether we should disable formatting.
355 if ( ! (MDOC_SYNPRETTY & n->flags) &&
356 ((n->prev && MDOC_SYNPRETTY & n->prev->flags) ||
357 (n->parent && MDOC_SYNPRETTY & n->parent->flags)))
358 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
360 if (chld && n->child)
361 print_mdoc_nodelist(p, &npair, m, n->child);
363 term_fontpopq(p, font);
371 if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags)
373 (void)(*termacts[n->tok].post)(p, &npair, m, n);
376 * Explicit end tokens not only call the post
377 * handler, but also tell the respective block
378 * that it must not call the post handler again.
380 if (ENDBODY_NOT != n->end)
381 n->pending->flags |= MDOC_ENDED;
384 * End of line terminating an implicit block
385 * while an explicit block is still open.
386 * Continue the explicit block without spacing.
388 if (ENDBODY_NOSPACE == n->end)
389 p->flags |= TERMP_NOSPACE;
393 if (MDOC_EOS & n->flags)
394 p->flags |= TERMP_SENTENCE;
397 p->rmargin = rmargin;
402 print_mdoc_foot(struct termp *p, const void *arg)
404 char buf[DATESIZ], os[BUFSIZ];
405 const struct mdoc_meta *m;
407 m = (const struct mdoc_meta *)arg;
409 term_fontrepl(p, TERMFONT_NONE);
412 * Output the footer in new-groff style, that is, three columns
413 * with the middle being the manual date and flanking columns
414 * being the operating system:
419 time2a(m->date, buf, DATESIZ);
420 strlcpy(os, m->os, BUFSIZ);
425 p->rmargin = (p->maxrmargin -
426 term_strlen(p, buf) + term_len(p, 1)) / 2;
427 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
432 p->offset = p->rmargin;
433 p->rmargin = p->maxrmargin - term_strlen(p, os);
434 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
439 p->offset = p->rmargin;
440 p->rmargin = p->maxrmargin;
441 p->flags &= ~TERMP_NOBREAK;
442 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
448 p->rmargin = p->maxrmargin;
454 print_mdoc_head(struct termp *p, const void *arg)
456 char buf[BUFSIZ], title[BUFSIZ];
457 const struct mdoc_meta *m;
459 m = (const struct mdoc_meta *)arg;
461 p->rmargin = p->maxrmargin;
465 * The header is strange. It has three components, which are
466 * really two with the first duplicated. It goes like this:
468 * IDENTIFIER TITLE IDENTIFIER
470 * The IDENTIFIER is NAME(SECTION), which is the command-name
471 * (if given, or "unknown" if not) followed by the manual page
472 * section. These are given in `Dt'. The TITLE is a free-form
473 * string depending on the manual volume. If not specified, it
474 * switches on the manual section.
478 strlcpy(buf, m->vol, BUFSIZ);
481 strlcat(buf, " (", BUFSIZ);
482 strlcat(buf, m->arch, BUFSIZ);
483 strlcat(buf, ")", BUFSIZ);
486 snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
489 p->rmargin = (p->maxrmargin -
490 term_strlen(p, buf) + term_len(p, 1)) / 2;
491 p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
496 p->offset = p->rmargin;
497 p->rmargin = p->maxrmargin - term_strlen(p, title);
498 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
503 p->offset = p->rmargin;
504 p->rmargin = p->maxrmargin;
505 p->flags &= ~TERMP_NOBREAK;
506 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
512 p->rmargin = p->maxrmargin;
513 p->flags &= ~TERMP_NOSPACE;
518 a2height(const struct termp *p, const char *v)
523 if ( ! a2roffsu(v, &su, SCALE_VS))
524 SCALE_VS_INIT(&su, term_len(p, 1));
526 return(term_vspan(p, &su));
531 a2width(const struct termp *p, const char *v)
536 if ( ! a2roffsu(v, &su, SCALE_MAX))
537 SCALE_HS_INIT(&su, term_strlen(p, v));
539 return(term_hspan(p, &su));
544 a2offs(const struct termp *p, const char *v)
550 else if (0 == strcmp(v, "left"))
552 else if (0 == strcmp(v, "indent"))
553 return(term_len(p, INDENT + 1));
554 else if (0 == strcmp(v, "indent-two"))
555 return(term_len(p, (INDENT + 1) * 2));
556 else if ( ! a2roffsu(v, &su, SCALE_MAX))
557 SCALE_HS_INIT(&su, term_strlen(p, v));
559 return(term_hspan(p, &su));
564 * Determine how much space to print out before block elements of `It'
565 * (and thus `Bl') and `Bd'. And then go ahead and print that space,
569 print_bvspace(struct termp *p,
570 const struct mdoc_node *bl,
571 const struct mdoc_node *n)
573 const struct mdoc_node *nn;
577 if (MDOC_Bd == bl->tok && bl->norm->Bd.comp)
579 if (MDOC_Bl == bl->tok && bl->norm->Bl.comp)
582 /* Do not vspace directly after Ss/Sh. */
584 for (nn = n; nn; nn = nn->parent) {
585 if (MDOC_BLOCK != nn->type)
587 if (MDOC_Ss == nn->tok)
589 if (MDOC_Sh == nn->tok)
591 if (NULL == nn->prev)
596 /* A `-column' does not assert vspace within the list. */
598 if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type)
599 if (n->prev && MDOC_It == n->prev->tok)
602 /* A `-diag' without body does not vspace. */
604 if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type)
605 if (n->prev && MDOC_It == n->prev->tok) {
606 assert(n->prev->body);
607 if (NULL == n->prev->body->child)
617 termp_it_pre(DECL_ARGS)
619 const struct mdoc_node *bl, *nn;
622 size_t width, offset, ncols, dcol;
625 if (MDOC_BLOCK == n->type) {
626 print_bvspace(p, n->parent->parent, n);
630 bl = n->parent->parent->parent;
631 type = bl->norm->Bl.type;
634 * First calculate width and offset. This is pretty easy unless
635 * we're a -column list, in which case all prior columns must
641 if (bl->norm->Bl.offs)
642 offset = a2offs(p, bl->norm->Bl.offs);
646 if (MDOC_HEAD == n->type)
650 * Imitate groff's column handling:
651 * - For each earlier column, add its width.
652 * - For less than 5 columns, add four more blanks per
654 * - For exactly 5 columns, add three more blank per
656 * - For more than 5 columns, add only one column.
658 ncols = bl->norm->Bl.ncols;
661 dcol = ncols < 5 ? term_len(p, 4) :
662 ncols == 5 ? term_len(p, 3) : term_len(p, 1);
665 * Calculate the offset by applying all prior MDOC_BODY,
666 * so we stop at the MDOC_HEAD (NULL == nn->prev).
669 for (i = 0, nn = n->prev;
670 nn->prev && i < (int)ncols;
672 offset += dcol + a2width
673 (p, bl->norm->Bl.cols[i]);
676 * When exceeding the declared number of columns, leave
677 * the remaining widths at 0. This will later be
678 * adjusted to the default width of 10, or, for the last
679 * column, stretched to the right margin.
685 * Use the declared column widths, extended as explained
686 * in the preceding paragraph.
688 width = a2width(p, bl->norm->Bl.cols[i]) + dcol;
691 if (NULL == bl->norm->Bl.width)
695 * Note: buffer the width by 2, which is groff's magic
696 * number for buffering single arguments. See the above
697 * handling for column for how this changes.
699 assert(bl->norm->Bl.width);
700 width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
705 * List-type can override the width in the case of fixed-head
706 * values (bullet, dash/hyphen, enum). Tags need a non-zero
716 if (width < term_len(p, 4))
717 width = term_len(p, 4);
720 if (width < term_len(p, 5))
721 width = term_len(p, 5);
725 width = term_len(p, 8);
731 width = term_len(p, 10);
738 * Whitespace control. Inset bodies need an initial space,
739 * while diagonal bodies need two.
742 p->flags |= TERMP_NOSPACE;
746 if (MDOC_BODY == n->type)
747 term_word(p, "\\ \\ ");
750 if (MDOC_BODY == n->type)
757 p->flags |= TERMP_NOSPACE;
761 if (MDOC_HEAD == n->type)
762 term_fontpush(p, TERMFONT_BOLD);
769 * Pad and break control. This is the tricky part. These flags
770 * are documented in term_flushln() in term.c. Note that we're
771 * going to unset all of these flags in termp_it_post() when we
783 if (MDOC_HEAD == n->type)
784 p->flags |= TERMP_NOBREAK;
786 p->flags |= TERMP_NOLPAD;
789 if (MDOC_HEAD == n->type)
790 p->flags |= TERMP_NOBREAK;
792 p->flags |= TERMP_NOLPAD;
794 if (MDOC_HEAD != n->type)
798 * This is ugly. If `-hang' is specified and the body
799 * is a `Bl' or `Bd', then we want basically to nullify
800 * the "overstep" effect in term_flushln() and treat
801 * this as a `-ohang' list instead.
803 if (n->next->child &&
804 (MDOC_Bl == n->next->child->tok ||
805 MDOC_Bd == n->next->child->tok)) {
806 p->flags &= ~TERMP_NOBREAK;
807 p->flags &= ~TERMP_NOLPAD;
809 p->flags |= TERMP_HANG;
812 if (MDOC_HEAD == n->type)
813 p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
815 p->flags |= TERMP_NOLPAD;
817 if (MDOC_HEAD != n->type)
819 if (NULL == n->next || NULL == n->next->child)
820 p->flags |= TERMP_DANGLE;
823 if (MDOC_HEAD == n->type)
827 p->flags &= ~TERMP_NOBREAK;
829 p->flags |= TERMP_NOBREAK;
832 if (MDOC_BODY == n->prev->type)
833 p->flags |= TERMP_NOLPAD;
837 if (MDOC_HEAD == n->type)
838 p->flags |= TERMP_NOBREAK;
845 * Margin control. Set-head-width lists have their right
846 * margins shortened. The body for these lists has the offset
847 * necessarily lengthened. Everybody gets the offset.
855 * Same stipulation as above, regarding `-hang'. We
856 * don't want to recalculate rmargin and offsets when
857 * using `Bd' or `Bl' within `-hang' overstep lists.
859 if (MDOC_HEAD == n->type && n->next->child &&
860 (MDOC_Bl == n->next->child->tok ||
861 MDOC_Bd == n->next->child->tok))
874 if (MDOC_HEAD == n->type)
875 p->rmargin = p->offset + width;
881 p->rmargin = p->offset + width;
883 * XXX - this behaviour is not documented: the
884 * right-most column is filled to the right margin.
886 if (MDOC_HEAD == n->type)
888 if (NULL == n->next && p->rmargin < p->maxrmargin)
889 p->rmargin = p->maxrmargin;
896 * The dash, hyphen, bullet and enum lists all have a special
897 * HEAD character (temporarily bold, in some cases).
900 if (MDOC_HEAD == n->type)
903 term_fontpush(p, TERMFONT_BOLD);
904 term_word(p, "\\[bu]");
910 term_fontpush(p, TERMFONT_BOLD);
911 term_word(p, "\\(hy");
915 (pair->ppair->ppair->count)++;
916 snprintf(buf, sizeof(buf), "%d.",
917 pair->ppair->ppair->count);
925 * If we're not going to process our children, indicate so here.
938 if (MDOC_HEAD == n->type)
942 if (MDOC_HEAD == n->type)
955 termp_it_post(DECL_ARGS)
959 if (MDOC_BLOCK == n->type)
962 type = n->parent->parent->parent->norm->Bl.type;
970 if (MDOC_BODY == n->type)
974 if (MDOC_BODY == n->type)
983 * Now that our output is flushed, we can reset our tags. Since
984 * only `It' sets these flags, we're free to assume that nobody
985 * has munged them in the meanwhile.
988 p->flags &= ~TERMP_DANGLE;
989 p->flags &= ~TERMP_NOBREAK;
990 p->flags &= ~TERMP_TWOSPACE;
991 p->flags &= ~TERMP_NOLPAD;
992 p->flags &= ~TERMP_HANG;
998 termp_nm_pre(DECL_ARGS)
1001 if (MDOC_BLOCK == n->type)
1004 if (MDOC_BODY == n->type) {
1005 if (NULL == n->child)
1007 p->flags |= TERMP_NOLPAD | TERMP_NOSPACE;
1008 p->offset += term_len(p, 1) +
1009 (NULL == n->prev->child ? term_strlen(p, m->name) :
1010 MDOC_TEXT == n->prev->child->type ?
1011 term_strlen(p, n->prev->child->string) :
1016 if (NULL == n->child && NULL == m->name)
1019 if (MDOC_HEAD == n->type)
1020 synopsis_pre(p, n->parent);
1022 if (MDOC_HEAD == n->type && n->next->child) {
1023 p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
1024 p->rmargin = p->offset + term_len(p, 1);
1025 if (NULL == n->child) {
1026 p->rmargin += term_strlen(p, m->name);
1027 } else if (MDOC_TEXT == n->child->type) {
1028 p->rmargin += term_strlen(p, n->child->string);
1030 p->flags |= TERMP_HANG;
1032 p->rmargin += term_len(p, 5);
1033 p->flags |= TERMP_HANG;
1037 term_fontpush(p, TERMFONT_BOLD);
1038 if (NULL == n->child)
1039 term_word(p, m->name);
1046 termp_nm_post(DECL_ARGS)
1049 if (MDOC_HEAD == n->type && n->next->child) {
1051 p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
1052 } else if (MDOC_BODY == n->type && n->child) {
1054 p->flags &= ~TERMP_NOLPAD;
1061 termp_fl_pre(DECL_ARGS)
1064 term_fontpush(p, TERMFONT_BOLD);
1065 term_word(p, "\\-");
1068 p->flags |= TERMP_NOSPACE;
1069 else if (n->next && n->next->line == n->line)
1070 p->flags |= TERMP_NOSPACE;
1078 termp__a_pre(DECL_ARGS)
1081 if (n->prev && MDOC__A == n->prev->tok)
1082 if (NULL == n->next || MDOC__A != n->next->tok)
1083 term_word(p, "and");
1091 termp_an_pre(DECL_ARGS)
1094 if (NULL == n->child)
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.
1106 if (n->sec == SEC_AUTHORS) {
1107 if ( ! (TERMP_ANPREC & p->flags)) {
1108 if (TERMP_SPLIT & p->flags)
1112 if (TERMP_NOSPLIT & p->flags)
1118 if (TERMP_SPLIT & p->flags)
1127 termp_an_post(DECL_ARGS)
1131 if (SEC_AUTHORS == n->sec)
1132 p->flags |= TERMP_ANPREC;
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;
1149 termp_ns_pre(DECL_ARGS)
1152 p->flags |= TERMP_NOSPACE;
1159 termp_rs_pre(DECL_ARGS)
1162 if (SEC_SEE_ALSO != n->sec)
1164 if (MDOC_BLOCK == n->type && n->prev)
1172 termp_rv_pre(DECL_ARGS)
1174 const struct mdoc_node *nn;
1177 term_word(p, "The");
1179 for (nn = n->child; nn; nn = nn->next) {
1180 term_fontpush(p, TERMFONT_BOLD);
1181 term_word(p, nn->string);
1183 p->flags |= TERMP_NOSPACE;
1184 if (nn->next && NULL == nn->next->next)
1185 term_word(p, "(), and");
1187 term_word(p, "(),");
1192 if (n->child && n->child->next)
1193 term_word(p, "functions return");
1195 term_word(p, "function returns");
1197 term_word(p, "the value 0 if successful; otherwise the value "
1198 "-1 is returned and the global variable");
1200 term_fontpush(p, TERMFONT_UNDER);
1201 term_word(p, "errno");
1204 term_word(p, "is set to indicate the error.");
1205 p->flags |= TERMP_SENTENCE;
1213 termp_ex_pre(DECL_ARGS)
1215 const struct mdoc_node *nn;
1217 term_word(p, "The");
1219 for (nn = n->child; nn; nn = nn->next) {
1220 term_fontpush(p, TERMFONT_BOLD);
1221 term_word(p, nn->string);
1223 p->flags |= TERMP_NOSPACE;
1224 if (nn->next && NULL == nn->next->next)
1225 term_word(p, ", and");
1229 p->flags &= ~TERMP_NOSPACE;
1232 if (n->child && n->child->next)
1233 term_word(p, "utilities exit");
1235 term_word(p, "utility exits");
1237 term_word(p, "0 on success, and >0 if an error occurs.");
1238 p->flags |= TERMP_SENTENCE;
1246 termp_nd_pre(DECL_ARGS)
1249 if (MDOC_BODY != n->type)
1252 #if defined(__OpenBSD__) || defined(__linux__)
1253 term_word(p, "\\(en");
1255 term_word(p, "\\(em");
1263 termp_bl_pre(DECL_ARGS)
1266 return(MDOC_HEAD != n->type);
1272 termp_bl_post(DECL_ARGS)
1275 if (MDOC_BLOCK == n->type)
1282 termp_xr_pre(DECL_ARGS)
1284 const struct mdoc_node *nn;
1286 if (NULL == n->child)
1289 assert(MDOC_TEXT == n->child->type);
1292 term_word(p, nn->string);
1293 if (NULL == (nn = nn->next))
1295 p->flags |= TERMP_NOSPACE;
1297 term_word(p, nn->string);
1305 * This decides how to assert whitespace before any of the SYNOPSIS set
1306 * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
1310 synopsis_pre(struct termp *p, const struct mdoc_node *n)
1313 * Obviously, if we're not in a SYNOPSIS or no prior macros
1314 * exist, do nothing.
1316 if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
1320 * If we're the second in a pair of like elements, emit our
1321 * newline and return. UNLESS we're `Fo', `Fn', `Fn', in which
1322 * case we soldier on.
1324 if (n->prev->tok == n->tok &&
1325 MDOC_Ft != n->tok &&
1326 MDOC_Fo != n->tok &&
1327 MDOC_Fn != n->tok) {
1333 * If we're one of the SYNOPSIS set and non-like pair-wise after
1334 * another (or Fn/Fo, which we've let slip through) then assert
1335 * vertical space, else only newline and move on.
1337 switch (n->prev->tok) {
1350 if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
1363 termp_vt_pre(DECL_ARGS)
1366 if (MDOC_ELEM == n->type) {
1368 return(termp_under_pre(p, pair, m, n));
1369 } else if (MDOC_BLOCK == n->type) {
1372 } else if (MDOC_HEAD == n->type)
1375 return(termp_under_pre(p, pair, m, n));
1381 termp_bold_pre(DECL_ARGS)
1384 term_fontpush(p, TERMFONT_BOLD);
1391 termp_fd_pre(DECL_ARGS)
1395 return(termp_bold_pre(p, pair, m, n));
1401 termp_sh_pre(DECL_ARGS)
1404 /* No vspace between consecutive `Sh' calls. */
1408 if (n->prev && MDOC_Sh == n->prev->tok)
1409 if (NULL == n->prev->body->child)
1414 term_fontpush(p, TERMFONT_BOLD);
1417 p->offset = term_len(p, INDENT);
1428 termp_sh_post(DECL_ARGS)
1447 termp_bt_pre(DECL_ARGS)
1450 term_word(p, "is currently in beta test.");
1451 p->flags |= TERMP_SENTENCE;
1458 termp_lb_post(DECL_ARGS)
1461 if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags)
1468 termp_ud_pre(DECL_ARGS)
1471 term_word(p, "currently under development.");
1472 p->flags |= TERMP_SENTENCE;
1479 termp_d1_pre(DECL_ARGS)
1482 if (MDOC_BLOCK != n->type)
1485 p->offset += term_len(p, (INDENT + 1));
1492 termp_d1_post(DECL_ARGS)
1495 if (MDOC_BLOCK != n->type)
1503 termp_ft_pre(DECL_ARGS)
1506 /* NB: MDOC_LINE does not effect this! */
1508 term_fontpush(p, TERMFONT_UNDER);
1515 termp_fn_pre(DECL_ARGS)
1517 const struct mdoc_node *nn;
1521 term_fontpush(p, TERMFONT_BOLD);
1522 term_word(p, n->child->string);
1525 p->flags |= TERMP_NOSPACE;
1528 for (nn = n->child->next; nn; nn = nn->next) {
1529 term_fontpush(p, TERMFONT_UNDER);
1530 term_word(p, nn->string);
1539 if (MDOC_SYNPRETTY & n->flags)
1548 termp_fa_pre(DECL_ARGS)
1550 const struct mdoc_node *nn;
1552 if (n->parent->tok != MDOC_Fo) {
1553 term_fontpush(p, TERMFONT_UNDER);
1557 for (nn = n->child; nn; nn = nn->next) {
1558 term_fontpush(p, TERMFONT_UNDER);
1559 term_word(p, nn->string);
1566 if (n->child && n->next && n->next->tok == MDOC_Fa)
1575 termp_bd_pre(DECL_ARGS)
1577 size_t tabwidth, rm, rmax;
1578 const struct mdoc_node *nn;
1580 if (MDOC_BLOCK == n->type) {
1581 print_bvspace(p, n, n);
1583 } else if (MDOC_HEAD == n->type)
1586 if (n->norm->Bd.offs)
1587 p->offset += a2offs(p, n->norm->Bd.offs);
1590 * If -ragged or -filled are specified, the block does nothing
1591 * but change the indentation. If -unfilled or -literal are
1592 * specified, text is printed exactly as entered in the display:
1593 * for macro lines, a newline is appended to the line. Blank
1594 * lines are allowed.
1597 if (DISP_literal != n->norm->Bd.type &&
1598 DISP_unfilled != n->norm->Bd.type)
1601 tabwidth = p->tabwidth;
1602 if (DISP_literal == n->norm->Bd.type)
1603 p->tabwidth = term_len(p, 8);
1606 rmax = p->maxrmargin;
1607 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1609 for (nn = n->child; nn; nn = nn->next) {
1610 print_mdoc_node(p, pair, m, nn);
1612 * If the printed node flushes its own line, then we
1613 * needn't do it here as well. This is hacky, but the
1614 * notion of selective eoln whitespace is pretty dumb
1615 * anyway, so don't sweat it.
1637 if (nn->next && nn->next->line == nn->line)
1640 p->flags |= TERMP_NOSPACE;
1643 p->tabwidth = tabwidth;
1645 p->maxrmargin = rmax;
1652 termp_bd_post(DECL_ARGS)
1656 if (MDOC_BODY != n->type)
1660 rmax = p->maxrmargin;
1662 if (DISP_literal == n->norm->Bd.type ||
1663 DISP_unfilled == n->norm->Bd.type)
1664 p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
1666 p->flags |= TERMP_NOSPACE;
1670 p->maxrmargin = rmax;
1676 termp_bx_post(DECL_ARGS)
1680 p->flags |= TERMP_NOSPACE;
1681 term_word(p, "BSD");
1687 termp_xx_pre(DECL_ARGS)
1723 termp_igndelim_pre(DECL_ARGS)
1726 p->flags |= TERMP_IGNDELIM;
1733 termp_pf_post(DECL_ARGS)
1736 p->flags |= TERMP_NOSPACE;
1742 termp_ss_pre(DECL_ARGS)
1752 term_fontpush(p, TERMFONT_BOLD);
1753 p->offset = term_len(p, HALFINDENT);
1765 termp_ss_post(DECL_ARGS)
1768 if (MDOC_HEAD == n->type)
1775 termp_cd_pre(DECL_ARGS)
1779 term_fontpush(p, TERMFONT_BOLD);
1786 termp_in_pre(DECL_ARGS)
1791 if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) {
1792 term_fontpush(p, TERMFONT_BOLD);
1793 term_word(p, "#include");
1797 term_fontpush(p, TERMFONT_UNDER);
1800 p->flags |= TERMP_NOSPACE;
1807 termp_in_post(DECL_ARGS)
1810 if (MDOC_SYNPRETTY & n->flags)
1811 term_fontpush(p, TERMFONT_BOLD);
1813 p->flags |= TERMP_NOSPACE;
1816 if (MDOC_SYNPRETTY & n->flags)
1823 termp_sp_pre(DECL_ARGS)
1829 len = n->child ? a2height(p, n->child->string) : 1;
1841 for (i = 0; i < len; i++)
1850 termp_quote_pre(DECL_ARGS)
1853 if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
1905 p->flags |= TERMP_NOSPACE;
1912 termp_quote_post(DECL_ARGS)
1915 if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
1918 p->flags |= TERMP_NOSPACE;
1973 termp_fo_pre(DECL_ARGS)
1976 if (MDOC_BLOCK == n->type) {
1979 } else if (MDOC_BODY == n->type) {
1980 p->flags |= TERMP_NOSPACE;
1985 if (NULL == n->child)
1988 /* XXX: we drop non-initial arguments as per groff. */
1990 assert(n->child->string);
1991 term_fontpush(p, TERMFONT_BOLD);
1992 term_word(p, n->child->string);
1999 termp_fo_post(DECL_ARGS)
2002 if (MDOC_BODY != n->type)
2007 if (MDOC_SYNPRETTY & n->flags)
2014 termp_bf_pre(DECL_ARGS)
2017 if (MDOC_HEAD == n->type)
2019 else if (MDOC_BLOCK != n->type)
2022 if (FONT_Em == n->norm->Bf.font)
2023 term_fontpush(p, TERMFONT_UNDER);
2024 else if (FONT_Sy == n->norm->Bf.font)
2025 term_fontpush(p, TERMFONT_BOLD);
2027 term_fontpush(p, TERMFONT_NONE);
2035 termp_sm_pre(DECL_ARGS)
2038 assert(n->child && MDOC_TEXT == n->child->type);
2039 if (0 == strcmp("on", n->child->string)) {
2041 p->flags &= ~TERMP_NOSPACE;
2042 p->flags &= ~TERMP_NONOSPACE;
2044 p->flags |= TERMP_NONOSPACE;
2052 termp_ap_pre(DECL_ARGS)
2055 p->flags |= TERMP_NOSPACE;
2057 p->flags |= TERMP_NOSPACE;
2064 termp____post(DECL_ARGS)
2068 * Handle lists of authors. In general, print each followed by
2069 * a comma. Don't print the comma if there are only two
2072 if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
2073 if (NULL == n->next->next || MDOC__A != n->next->next->tok)
2074 if (NULL == n->prev || MDOC__A != n->prev->tok)
2079 if (NULL == n->parent || MDOC_Rs != n->parent->tok)
2082 if (NULL == n->next) {
2084 p->flags |= TERMP_SENTENCE;
2092 termp_li_pre(DECL_ARGS)
2095 term_fontpush(p, TERMFONT_NONE);
2102 termp_lk_pre(DECL_ARGS)
2104 const struct mdoc_node *nn, *sv;
2106 term_fontpush(p, TERMFONT_UNDER);
2110 if (NULL == nn || NULL == nn->next)
2113 for (nn = nn->next; nn; nn = nn->next)
2114 term_word(p, nn->string);
2120 term_fontpush(p, TERMFONT_BOLD);
2121 term_word(p, sv->string);
2130 termp_bk_pre(DECL_ARGS)
2139 if (n->parent->args || 0 == n->prev->nchild)
2140 p->flags |= TERMP_PREKEEP;
2153 termp_bk_post(DECL_ARGS)
2156 if (MDOC_BODY == n->type)
2157 p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
2162 termp__t_post(DECL_ARGS)
2166 * If we're in an `Rs' and there's a journal present, then quote
2167 * us instead of underlining us (for disambiguation).
2169 if (n->parent && MDOC_Rs == n->parent->tok &&
2170 n->parent->norm->Rs.child_J)
2171 termp_quote_post(p, pair, m, n);
2173 termp____post(p, pair, m, n);
2178 termp__t_pre(DECL_ARGS)
2182 * If we're in an `Rs' and there's a journal present, then quote
2183 * us instead of underlining us (for disambiguation).
2185 if (n->parent && MDOC_Rs == n->parent->tok &&
2186 n->parent->norm->Rs.child_J)
2187 return(termp_quote_pre(p, pair, m, n));
2189 term_fontpush(p, TERMFONT_UNDER);
2195 termp_under_pre(DECL_ARGS)
2198 term_fontpush(p, TERMFONT_UNDER);