1 /* $Id: mdoc_html.c,v 1.344 2022/06/25 12:44:25 schwarze Exp $ */
3 * Copyright (c) 2014-2022 Ingo Schwarze <schwarze@openbsd.org>
4 * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
5 * Copyright (c) 2022 Anna Vyalkova <cyber@sysrq.in>
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.
19 * HTML formatter for mdoc(7) used by mandoc(1).
23 #include <sys/types.h>
32 #include "mandoc_aux.h"
40 #define MDOC_ARGS const struct roff_meta *meta, \
41 struct roff_node *n, \
45 #define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b))
48 struct mdoc_html_act
{
49 int (*pre
)(MDOC_ARGS
);
50 void (*post
)(MDOC_ARGS
);
53 static void print_mdoc_head(const struct roff_meta
*,
55 static void print_mdoc_node(MDOC_ARGS
);
56 static void print_mdoc_nodelist(MDOC_ARGS
);
57 static void synopsis_pre(struct html
*, struct roff_node
*);
59 static void mdoc_root_post(const struct roff_meta
*,
61 static int mdoc_root_pre(const struct roff_meta
*,
64 static void mdoc__x_post(MDOC_ARGS
);
65 static int mdoc__x_pre(MDOC_ARGS
);
66 static int mdoc_abort_pre(MDOC_ARGS
);
67 static int mdoc_ad_pre(MDOC_ARGS
);
68 static int mdoc_an_pre(MDOC_ARGS
);
69 static int mdoc_ap_pre(MDOC_ARGS
);
70 static int mdoc_ar_pre(MDOC_ARGS
);
71 static int mdoc_bd_pre(MDOC_ARGS
);
72 static int mdoc_bf_pre(MDOC_ARGS
);
73 static void mdoc_bk_post(MDOC_ARGS
);
74 static int mdoc_bk_pre(MDOC_ARGS
);
75 static int mdoc_bl_pre(MDOC_ARGS
);
76 static int mdoc_cd_pre(MDOC_ARGS
);
77 static int mdoc_code_pre(MDOC_ARGS
);
78 static int mdoc_d1_pre(MDOC_ARGS
);
79 static int mdoc_fa_pre(MDOC_ARGS
);
80 static int mdoc_fd_pre(MDOC_ARGS
);
81 static int mdoc_fl_pre(MDOC_ARGS
);
82 static int mdoc_fn_pre(MDOC_ARGS
);
83 static int mdoc_ft_pre(MDOC_ARGS
);
84 static int mdoc_em_pre(MDOC_ARGS
);
85 static void mdoc_eo_post(MDOC_ARGS
);
86 static int mdoc_eo_pre(MDOC_ARGS
);
87 static int mdoc_ex_pre(MDOC_ARGS
);
88 static void mdoc_fo_post(MDOC_ARGS
);
89 static int mdoc_fo_pre(MDOC_ARGS
);
90 static int mdoc_igndelim_pre(MDOC_ARGS
);
91 static int mdoc_in_pre(MDOC_ARGS
);
92 static int mdoc_it_pre(MDOC_ARGS
);
93 static int mdoc_lb_pre(MDOC_ARGS
);
94 static int mdoc_lk_pre(MDOC_ARGS
);
95 static int mdoc_mt_pre(MDOC_ARGS
);
96 static int mdoc_nd_pre(MDOC_ARGS
);
97 static int mdoc_nm_pre(MDOC_ARGS
);
98 static int mdoc_no_pre(MDOC_ARGS
);
99 static int mdoc_ns_pre(MDOC_ARGS
);
100 static int mdoc_pa_pre(MDOC_ARGS
);
101 static void mdoc_pf_post(MDOC_ARGS
);
102 static int mdoc_pp_pre(MDOC_ARGS
);
103 static void mdoc_quote_post(MDOC_ARGS
);
104 static int mdoc_quote_pre(MDOC_ARGS
);
105 static int mdoc_rs_pre(MDOC_ARGS
);
106 static int mdoc_sh_pre(MDOC_ARGS
);
107 static int mdoc_skip_pre(MDOC_ARGS
);
108 static int mdoc_sm_pre(MDOC_ARGS
);
109 static int mdoc_ss_pre(MDOC_ARGS
);
110 static int mdoc_st_pre(MDOC_ARGS
);
111 static int mdoc_sx_pre(MDOC_ARGS
);
112 static int mdoc_sy_pre(MDOC_ARGS
);
113 static int mdoc_tg_pre(MDOC_ARGS
);
114 static int mdoc_va_pre(MDOC_ARGS
);
115 static int mdoc_vt_pre(MDOC_ARGS
);
116 static int mdoc_xr_pre(MDOC_ARGS
);
117 static int mdoc_xx_pre(MDOC_ARGS
);
119 static const struct mdoc_html_act mdoc_html_acts
[MDOC_MAX
- MDOC_Dd
] = {
120 {NULL
, NULL
}, /* Dd */
121 {NULL
, NULL
}, /* Dt */
122 {NULL
, NULL
}, /* Os */
123 {mdoc_sh_pre
, NULL
}, /* Sh */
124 {mdoc_ss_pre
, NULL
}, /* Ss */
125 {mdoc_pp_pre
, NULL
}, /* Pp */
126 {mdoc_d1_pre
, NULL
}, /* D1 */
127 {mdoc_d1_pre
, NULL
}, /* Dl */
128 {mdoc_bd_pre
, NULL
}, /* Bd */
129 {NULL
, NULL
}, /* Ed */
130 {mdoc_bl_pre
, NULL
}, /* Bl */
131 {NULL
, NULL
}, /* El */
132 {mdoc_it_pre
, NULL
}, /* It */
133 {mdoc_ad_pre
, NULL
}, /* Ad */
134 {mdoc_an_pre
, NULL
}, /* An */
135 {mdoc_ap_pre
, NULL
}, /* Ap */
136 {mdoc_ar_pre
, NULL
}, /* Ar */
137 {mdoc_cd_pre
, NULL
}, /* Cd */
138 {mdoc_code_pre
, NULL
}, /* Cm */
139 {mdoc_code_pre
, NULL
}, /* Dv */
140 {mdoc_code_pre
, NULL
}, /* Er */
141 {mdoc_code_pre
, NULL
}, /* Ev */
142 {mdoc_ex_pre
, NULL
}, /* Ex */
143 {mdoc_fa_pre
, NULL
}, /* Fa */
144 {mdoc_fd_pre
, NULL
}, /* Fd */
145 {mdoc_fl_pre
, NULL
}, /* Fl */
146 {mdoc_fn_pre
, NULL
}, /* Fn */
147 {mdoc_ft_pre
, NULL
}, /* Ft */
148 {mdoc_code_pre
, NULL
}, /* Ic */
149 {mdoc_in_pre
, NULL
}, /* In */
150 {mdoc_code_pre
, NULL
}, /* Li */
151 {mdoc_nd_pre
, NULL
}, /* Nd */
152 {mdoc_nm_pre
, NULL
}, /* Nm */
153 {mdoc_quote_pre
, mdoc_quote_post
}, /* Op */
154 {mdoc_abort_pre
, NULL
}, /* Ot */
155 {mdoc_pa_pre
, NULL
}, /* Pa */
156 {mdoc_ex_pre
, NULL
}, /* Rv */
157 {mdoc_st_pre
, NULL
}, /* St */
158 {mdoc_va_pre
, NULL
}, /* Va */
159 {mdoc_vt_pre
, NULL
}, /* Vt */
160 {mdoc_xr_pre
, NULL
}, /* Xr */
161 {mdoc__x_pre
, mdoc__x_post
}, /* %A */
162 {mdoc__x_pre
, mdoc__x_post
}, /* %B */
163 {mdoc__x_pre
, mdoc__x_post
}, /* %D */
164 {mdoc__x_pre
, mdoc__x_post
}, /* %I */
165 {mdoc__x_pre
, mdoc__x_post
}, /* %J */
166 {mdoc__x_pre
, mdoc__x_post
}, /* %N */
167 {mdoc__x_pre
, mdoc__x_post
}, /* %O */
168 {mdoc__x_pre
, mdoc__x_post
}, /* %P */
169 {mdoc__x_pre
, mdoc__x_post
}, /* %R */
170 {mdoc__x_pre
, mdoc__x_post
}, /* %T */
171 {mdoc__x_pre
, mdoc__x_post
}, /* %V */
172 {NULL
, NULL
}, /* Ac */
173 {mdoc_quote_pre
, mdoc_quote_post
}, /* Ao */
174 {mdoc_quote_pre
, mdoc_quote_post
}, /* Aq */
175 {mdoc_xx_pre
, NULL
}, /* At */
176 {NULL
, NULL
}, /* Bc */
177 {mdoc_bf_pre
, NULL
}, /* Bf */
178 {mdoc_quote_pre
, mdoc_quote_post
}, /* Bo */
179 {mdoc_quote_pre
, mdoc_quote_post
}, /* Bq */
180 {mdoc_xx_pre
, NULL
}, /* Bsx */
181 {mdoc_xx_pre
, NULL
}, /* Bx */
182 {mdoc_skip_pre
, NULL
}, /* Db */
183 {NULL
, NULL
}, /* Dc */
184 {mdoc_quote_pre
, mdoc_quote_post
}, /* Do */
185 {mdoc_quote_pre
, mdoc_quote_post
}, /* Dq */
186 {NULL
, NULL
}, /* Ec */ /* FIXME: no space */
187 {NULL
, NULL
}, /* Ef */
188 {mdoc_em_pre
, NULL
}, /* Em */
189 {mdoc_eo_pre
, mdoc_eo_post
}, /* Eo */
190 {mdoc_xx_pre
, NULL
}, /* Fx */
191 {mdoc_no_pre
, NULL
}, /* Ms */
192 {mdoc_no_pre
, NULL
}, /* No */
193 {mdoc_ns_pre
, NULL
}, /* Ns */
194 {mdoc_xx_pre
, NULL
}, /* Nx */
195 {mdoc_xx_pre
, NULL
}, /* Ox */
196 {NULL
, NULL
}, /* Pc */
197 {mdoc_igndelim_pre
, mdoc_pf_post
}, /* Pf */
198 {mdoc_quote_pre
, mdoc_quote_post
}, /* Po */
199 {mdoc_quote_pre
, mdoc_quote_post
}, /* Pq */
200 {NULL
, NULL
}, /* Qc */
201 {mdoc_quote_pre
, mdoc_quote_post
}, /* Ql */
202 {mdoc_quote_pre
, mdoc_quote_post
}, /* Qo */
203 {mdoc_quote_pre
, mdoc_quote_post
}, /* Qq */
204 {NULL
, NULL
}, /* Re */
205 {mdoc_rs_pre
, NULL
}, /* Rs */
206 {NULL
, NULL
}, /* Sc */
207 {mdoc_quote_pre
, mdoc_quote_post
}, /* So */
208 {mdoc_quote_pre
, mdoc_quote_post
}, /* Sq */
209 {mdoc_sm_pre
, NULL
}, /* Sm */
210 {mdoc_sx_pre
, NULL
}, /* Sx */
211 {mdoc_sy_pre
, NULL
}, /* Sy */
212 {NULL
, NULL
}, /* Tn */
213 {mdoc_xx_pre
, NULL
}, /* Ux */
214 {NULL
, NULL
}, /* Xc */
215 {NULL
, NULL
}, /* Xo */
216 {mdoc_fo_pre
, mdoc_fo_post
}, /* Fo */
217 {NULL
, NULL
}, /* Fc */
218 {mdoc_quote_pre
, mdoc_quote_post
}, /* Oo */
219 {NULL
, NULL
}, /* Oc */
220 {mdoc_bk_pre
, mdoc_bk_post
}, /* Bk */
221 {NULL
, NULL
}, /* Ek */
222 {NULL
, NULL
}, /* Bt */
223 {NULL
, NULL
}, /* Hf */
224 {mdoc_em_pre
, NULL
}, /* Fr */
225 {NULL
, NULL
}, /* Ud */
226 {mdoc_lb_pre
, NULL
}, /* Lb */
227 {mdoc_abort_pre
, NULL
}, /* Lp */
228 {mdoc_lk_pre
, NULL
}, /* Lk */
229 {mdoc_mt_pre
, NULL
}, /* Mt */
230 {mdoc_quote_pre
, mdoc_quote_post
}, /* Brq */
231 {mdoc_quote_pre
, mdoc_quote_post
}, /* Bro */
232 {NULL
, NULL
}, /* Brc */
233 {mdoc__x_pre
, mdoc__x_post
}, /* %C */
234 {mdoc_skip_pre
, NULL
}, /* Es */
235 {mdoc_quote_pre
, mdoc_quote_post
}, /* En */
236 {mdoc_xx_pre
, NULL
}, /* Dx */
237 {mdoc__x_pre
, mdoc__x_post
}, /* %Q */
238 {mdoc__x_pre
, mdoc__x_post
}, /* %U */
239 {NULL
, NULL
}, /* Ta */
240 {mdoc_tg_pre
, NULL
}, /* Tg */
245 * See the same function in mdoc_term.c for documentation.
248 synopsis_pre(struct html
*h
, struct roff_node
*n
)
250 struct roff_node
*np
;
252 if ((n
->flags
& NODE_SYNPRETTY
) == 0 ||
253 (np
= roff_node_prev(n
)) == NULL
)
256 if (np
->tok
== n
->tok
&&
260 print_otag(h
, TAG_BR
, "");
272 if (n
->tok
!= MDOC_Fn
&& n
->tok
!= MDOC_Fo
)
276 print_otag(h
, TAG_BR
, "");
279 html_close_paragraph(h
);
280 print_otag(h
, TAG_P
, "c", "Pp");
284 html_mdoc(void *arg
, const struct roff_meta
*mdoc
)
290 h
= (struct html
*)arg
;
291 n
= mdoc
->first
->child
;
293 if ((h
->oflags
& HTML_FRAGMENT
) == 0) {
295 print_otag(h
, TAG_HTML
, "");
296 if (n
!= NULL
&& n
->type
== ROFFT_COMMENT
)
297 print_gen_comment(h
, n
);
298 t
= print_otag(h
, TAG_HEAD
, "");
299 print_mdoc_head(mdoc
, h
);
301 print_otag(h
, TAG_BODY
, "");
304 mdoc_root_pre(mdoc
, h
);
305 t
= print_otag(h
, TAG_DIV
, "c", "manual-text");
306 print_mdoc_nodelist(mdoc
, n
, h
);
308 mdoc_root_post(mdoc
, h
);
313 print_mdoc_head(const struct roff_meta
*meta
, struct html
*h
)
319 if (meta
->arch
!= NULL
&& meta
->msec
!= NULL
)
320 mandoc_asprintf(&cp
, "%s(%s) (%s)", meta
->title
,
321 meta
->msec
, meta
->arch
);
322 else if (meta
->msec
!= NULL
)
323 mandoc_asprintf(&cp
, "%s(%s)", meta
->title
, meta
->msec
);
324 else if (meta
->arch
!= NULL
)
325 mandoc_asprintf(&cp
, "%s (%s)", meta
->title
, meta
->arch
);
327 cp
= mandoc_strdup(meta
->title
);
329 print_otag(h
, TAG_TITLE
, "");
335 print_mdoc_nodelist(MDOC_ARGS
)
339 print_mdoc_node(meta
, n
, h
);
345 print_mdoc_node(MDOC_ARGS
)
350 if (n
->type
== ROFFT_COMMENT
|| n
->flags
& NODE_NOPRT
)
353 if ((n
->flags
& NODE_NOFILL
) == 0)
354 html_fillmode(h
, ROFF_fi
);
355 else if (html_fillmode(h
, ROFF_nf
) == ROFF_nf
&&
356 n
->tok
!= ROFF_fi
&& n
->flags
& NODE_LINE
)
360 n
->flags
&= ~NODE_ENDED
;
363 if (n
->flags
& NODE_LINE
) {
364 switch (*n
->string
) {
370 if ((h
->flags
& HTML_NONEWLINE
) == 0 &&
371 (n
->flags
& NODE_NOFILL
) == 0)
372 print_otag(h
, TAG_BR
, "");
380 if (n
->flags
& NODE_DELIMC
)
381 h
->flags
|= HTML_NOSPACE
;
382 if (n
->flags
& NODE_HREF
)
383 print_tagged_text(h
, n
->string
, n
);
385 print_text(h
, n
->string
);
386 if (n
->flags
& NODE_DELIMO
)
387 h
->flags
|= HTML_NOSPACE
;
392 print_eqn(h
, n
->eqn
);
396 * This will take care of initialising all of the table
397 * state data for the first table, then tearing it down
400 print_tbl(h
, n
->span
);
404 * Close out the current table, if it's open, and unset
405 * the "meta" table state. This will be reopened on the
406 * next table element.
410 assert(h
->tblt
== NULL
);
413 if (n
->tok
< ROFF_MAX
) {
419 assert(n
->tok
>= MDOC_Dd
&& n
->tok
< MDOC_MAX
);
420 if (mdoc_html_acts
[n
->tok
- MDOC_Dd
].pre
!= NULL
&&
421 (n
->end
== ENDBODY_NOT
|| n
->child
!= NULL
))
422 child
= (*mdoc_html_acts
[n
->tok
- MDOC_Dd
].pre
)(meta
,
427 if (h
->flags
& HTML_KEEP
&& n
->flags
& NODE_LINE
) {
428 h
->flags
&= ~HTML_KEEP
;
429 h
->flags
|= HTML_PREKEEP
;
432 if (child
&& n
->child
!= NULL
)
433 print_mdoc_nodelist(meta
, n
->child
, h
);
443 if (mdoc_html_acts
[n
->tok
- MDOC_Dd
].post
== NULL
||
444 n
->flags
& NODE_ENDED
)
446 (*mdoc_html_acts
[n
->tok
- MDOC_Dd
].post
)(meta
, n
, h
);
447 if (n
->end
!= ENDBODY_NOT
)
448 n
->body
->flags
|= NODE_ENDED
;
454 mdoc_root_post(const struct roff_meta
*meta
, struct html
*h
)
458 t
= print_otag(h
, TAG_TABLE
, "c", "foot");
459 tt
= print_otag(h
, TAG_TR
, "");
461 print_otag(h
, TAG_TD
, "c", "foot-date");
462 print_text(h
, meta
->date
);
465 print_otag(h
, TAG_TD
, "c", "foot-os");
466 print_text(h
, meta
->os
);
471 mdoc_root_pre(const struct roff_meta
*meta
, struct html
*h
)
474 char *volume
, *title
;
476 if (NULL
== meta
->arch
)
477 volume
= mandoc_strdup(meta
->vol
);
479 mandoc_asprintf(&volume
, "%s (%s)",
480 meta
->vol
, meta
->arch
);
482 if (NULL
== meta
->msec
)
483 title
= mandoc_strdup(meta
->title
);
485 mandoc_asprintf(&title
, "%s(%s)",
486 meta
->title
, meta
->msec
);
488 t
= print_otag(h
, TAG_TABLE
, "c", "head");
489 tt
= print_otag(h
, TAG_TR
, "");
491 print_otag(h
, TAG_TD
, "c", "head-ltitle");
492 print_text(h
, title
);
495 print_otag(h
, TAG_TD
, "c", "head-vol");
496 print_text(h
, volume
);
499 print_otag(h
, TAG_TD
, "c", "head-rtitle");
500 print_text(h
, title
);
509 mdoc_code_pre(MDOC_ARGS
)
511 print_otag_id(h
, TAG_CODE
, roff_name
[n
->tok
], n
);
516 mdoc_sh_pre(MDOC_ARGS
)
518 struct roff_node
*sn
, *subn
;
519 struct tag
*t
, *tnav
, *tsec
, *tsub
;
525 html_close_paragraph(h
);
526 if ((h
->oflags
& HTML_TOC
) == 0 ||
527 h
->flags
& HTML_TOCDONE
||
528 n
->sec
<= SEC_SYNOPSIS
) {
529 print_otag(h
, TAG_SECTION
, "c", "Sh");
532 h
->flags
|= HTML_TOCDONE
;
534 for (sn
= n
->next
; sn
!= NULL
; sn
= sn
->next
)
535 if (sn
->sec
== SEC_CUSTOM
)
540 tnav
= print_otag(h
, TAG_NAV
, "r", "doc-toc");
541 t
= print_otag(h
, TAG_H1
, "c", "Sh");
542 print_text(h
, "TABLE OF CONTENTS");
544 t
= print_otag(h
, TAG_UL
, "c", "Bl-compact");
545 for (sn
= n
; sn
!= NULL
; sn
= sn
->next
) {
546 tsec
= print_otag(h
, TAG_LI
, "");
547 id
= html_make_id(sn
->head
, 0);
548 tsub
= print_otag(h
, TAG_A
, "hR", id
);
550 print_mdoc_nodelist(meta
, sn
->head
->child
, h
);
553 for (subn
= sn
->body
->child
; subn
!= NULL
;
555 if (subn
->tok
!= MDOC_Ss
)
557 id
= html_make_id(subn
->head
, 0);
561 print_otag(h
, TAG_UL
,
563 tsub
= print_otag(h
, TAG_LI
, "");
564 print_otag(h
, TAG_A
, "hR", id
);
566 print_mdoc_nodelist(meta
,
567 subn
->head
->child
, h
);
573 print_otag(h
, TAG_SECTION
, "c", "Sh");
576 print_otag_id(h
, TAG_H1
, "Sh", n
);
579 if (n
->sec
== SEC_AUTHORS
)
580 h
->flags
&= ~(HTML_SPLIT
|HTML_NOSPLIT
);
589 mdoc_ss_pre(MDOC_ARGS
)
593 html_close_paragraph(h
);
594 print_otag(h
, TAG_SECTION
, "c", "Ss");
597 print_otag_id(h
, TAG_H2
, "Ss", n
);
608 mdoc_fl_pre(MDOC_ARGS
)
610 struct roff_node
*nn
;
612 print_otag_id(h
, TAG_CODE
, "Fl", n
);
613 print_text(h
, "\\-");
614 if (n
->child
!= NULL
||
615 ((nn
= roff_node_next(n
)) != NULL
&&
616 nn
->type
!= ROFFT_TEXT
&&
617 (nn
->flags
& NODE_LINE
) == 0))
618 h
->flags
|= HTML_NOSPACE
;
624 mdoc_nd_pre(MDOC_ARGS
)
636 print_text(h
, "\\(em");
637 print_otag(h
, TAG_SPAN
, "c", "Nd");
642 mdoc_nm_pre(MDOC_ARGS
)
648 print_otag(h
, TAG_TD
, "");
651 print_otag(h
, TAG_CODE
, "c", "Nm");
654 print_otag(h
, TAG_TD
, "");
659 html_close_paragraph(h
);
661 print_otag(h
, TAG_TABLE
, "c", "Nm");
662 print_otag(h
, TAG_TR
, "");
667 mdoc_xr_pre(MDOC_ARGS
)
669 char *name
, *section
, *label
;
671 if (n
->child
== NULL
)
674 name
= n
->child
->string
;
675 if (n
->child
->next
!= NULL
) {
676 section
= n
->child
->next
->string
;
677 mandoc_asprintf(&label
, "%s, section %s", name
, section
);
679 section
= label
= NULL
;
682 print_otag(h
, TAG_A
, "chM?", "Xr",
683 name
, section
, "aria-label", label
);
685 print_otag(h
, TAG_A
, "c?", "Xr", "aria-label", label
);
693 h
->flags
|= HTML_NOSPACE
;
695 h
->flags
|= HTML_NOSPACE
;
696 print_text(h
, section
);
697 h
->flags
|= HTML_NOSPACE
;
703 mdoc_tg_pre(MDOC_ARGS
)
707 if ((id
= html_make_id(n
, 1)) != NULL
) {
708 print_tagq(h
, print_otag(h
, TAG_MARK
, "i", id
));
715 mdoc_ns_pre(MDOC_ARGS
)
718 if ( ! (NODE_LINE
& n
->flags
))
719 h
->flags
|= HTML_NOSPACE
;
724 mdoc_ar_pre(MDOC_ARGS
)
726 print_otag(h
, TAG_VAR
, "c", "Ar");
731 mdoc_xx_pre(MDOC_ARGS
)
733 print_otag(h
, TAG_SPAN
, "c", "Ux");
738 mdoc_it_pre(MDOC_ARGS
)
740 const struct roff_node
*bl
;
744 while (bl
->tok
!= MDOC_Bl
)
746 type
= bl
->norm
->Bl
.type
;
758 print_otag_id(h
, TAG_LI
, NULL
, n
);
770 print_otag_id(h
, TAG_DT
, NULL
, n
);
773 print_otag(h
, TAG_DD
, "");
782 print_otag_id(h
, TAG_DT
, NULL
, n
);
785 if (n
->child
== NULL
) {
786 print_otag(h
, TAG_DD
, "s", "width", "auto");
787 print_text(h
, "\\ ");
789 print_otag(h
, TAG_DD
, "");
800 print_otag(h
, TAG_TD
, "");
803 print_otag_id(h
, TAG_TR
, NULL
, n
);
813 mdoc_bl_pre(MDOC_ARGS
)
817 enum htmltag elemtype
;
821 html_close_paragraph(h
);
835 (void)strlcpy(cattr
, "Bl-bullet", sizeof(cattr
));
840 (void)strlcpy(cattr
, "Bl-dash", sizeof(cattr
));
844 (void)strlcpy(cattr
, "Bl-item", sizeof(cattr
));
848 (void)strlcpy(cattr
, "Bl-enum", sizeof(cattr
));
852 (void)strlcpy(cattr
, "Bl-diag", sizeof(cattr
));
856 (void)strlcpy(cattr
, "Bl-hang", sizeof(cattr
));
860 (void)strlcpy(cattr
, "Bl-inset", sizeof(cattr
));
864 (void)strlcpy(cattr
, "Bl-ohang", sizeof(cattr
));
868 print_otag(h
, TAG_DIV
, "c", "Bd-indent");
869 print_otag_id(h
, TAG_DL
,
870 bl
->comp
? "Bl-tag Bl-compact" : "Bl-tag", n
->body
);
873 elemtype
= TAG_TABLE
;
874 (void)strlcpy(cattr
, "Bl-column", sizeof(cattr
));
879 if (bl
->offs
!= NULL
)
880 (void)strlcat(cattr
, " Bd-indent", sizeof(cattr
));
882 (void)strlcat(cattr
, " Bl-compact", sizeof(cattr
));
883 print_otag_id(h
, elemtype
, cattr
, n
->body
);
888 mdoc_ex_pre(MDOC_ARGS
)
890 if (roff_node_prev(n
) != NULL
)
891 print_otag(h
, TAG_BR
, "");
896 mdoc_st_pre(MDOC_ARGS
)
898 print_otag(h
, TAG_SPAN
, "c", "St");
903 mdoc_em_pre(MDOC_ARGS
)
905 print_otag_id(h
, TAG_I
, "Em", n
);
910 mdoc_d1_pre(MDOC_ARGS
)
914 html_close_paragraph(h
);
923 print_otag_id(h
, TAG_DIV
, "Bd Bd-indent", n
);
924 if (n
->tok
== MDOC_Dl
)
925 print_otag(h
, TAG_CODE
, "c", "Li");
930 mdoc_sx_pre(MDOC_ARGS
)
934 id
= html_make_id(n
, 0);
935 print_otag(h
, TAG_A
, "chR", "Sx", id
);
941 mdoc_bd_pre(MDOC_ARGS
)
944 struct roff_node
*nn
;
949 html_close_paragraph(h
);
959 /* Handle preceding whitespace. */
961 comp
= n
->norm
->Bd
.comp
;
962 for (nn
= n
; nn
!= NULL
&& comp
== 0; nn
= nn
->parent
) {
963 if (nn
->type
!= ROFFT_BLOCK
)
965 if (nn
->tok
== MDOC_Sh
|| nn
->tok
== MDOC_Ss
)
967 if (roff_node_prev(nn
) != NULL
)
970 (void)strlcpy(buf
, "Bd", sizeof(buf
));
972 (void)strlcat(buf
, " Pp", sizeof(buf
));
974 /* Handle the -offset argument. */
976 if (n
->norm
->Bd
.offs
!= NULL
&&
977 strcmp(n
->norm
->Bd
.offs
, "left") != 0)
978 (void)strlcat(buf
, " Bd-indent", sizeof(buf
));
980 if (n
->norm
->Bd
.type
== DISP_literal
)
981 (void)strlcat(buf
, " Li", sizeof(buf
));
983 print_otag_id(h
, TAG_DIV
, buf
, n
);
988 mdoc_pa_pre(MDOC_ARGS
)
990 print_otag(h
, TAG_SPAN
, "c", "Pa");
995 mdoc_ad_pre(MDOC_ARGS
)
997 print_otag(h
, TAG_SPAN
, "c", "Ad");
1002 mdoc_an_pre(MDOC_ARGS
)
1004 if (n
->norm
->An
.auth
== AUTH_split
) {
1005 h
->flags
&= ~HTML_NOSPLIT
;
1006 h
->flags
|= HTML_SPLIT
;
1009 if (n
->norm
->An
.auth
== AUTH_nosplit
) {
1010 h
->flags
&= ~HTML_SPLIT
;
1011 h
->flags
|= HTML_NOSPLIT
;
1015 if (h
->flags
& HTML_SPLIT
)
1016 print_otag(h
, TAG_BR
, "");
1018 if (n
->sec
== SEC_AUTHORS
&& ! (h
->flags
& HTML_NOSPLIT
))
1019 h
->flags
|= HTML_SPLIT
;
1021 print_otag(h
, TAG_SPAN
, "c", "An");
1026 mdoc_cd_pre(MDOC_ARGS
)
1029 print_otag(h
, TAG_CODE
, "c", "Cd");
1034 mdoc_fa_pre(MDOC_ARGS
)
1036 const struct roff_node
*nn
;
1039 if (n
->parent
->tok
!= MDOC_Fo
) {
1040 print_otag(h
, TAG_VAR
, "c", "Fa");
1043 for (nn
= n
->child
; nn
!= NULL
; nn
= nn
->next
) {
1044 t
= print_otag(h
, TAG_VAR
, "c", "Fa");
1045 print_text(h
, nn
->string
);
1047 if (nn
->next
!= NULL
) {
1048 h
->flags
|= HTML_NOSPACE
;
1052 if (n
->child
!= NULL
&&
1053 (nn
= roff_node_next(n
)) != NULL
&&
1054 nn
->tok
== MDOC_Fa
) {
1055 h
->flags
|= HTML_NOSPACE
;
1062 mdoc_fd_pre(MDOC_ARGS
)
1069 if (NULL
== (n
= n
->child
))
1072 assert(n
->type
== ROFFT_TEXT
);
1074 if (strcmp(n
->string
, "#include")) {
1075 print_otag(h
, TAG_CODE
, "c", "Fd");
1079 print_otag(h
, TAG_CODE
, "c", "In");
1080 print_text(h
, n
->string
);
1082 if (NULL
!= (n
= n
->next
)) {
1083 assert(n
->type
== ROFFT_TEXT
);
1085 if (h
->base_includes
) {
1087 if (*cp
== '<' || *cp
== '"')
1089 buf
= mandoc_strdup(cp
);
1090 cp
= strchr(buf
, '\0') - 1;
1091 if (cp
>= buf
&& (*cp
== '>' || *cp
== '"'))
1093 t
= print_otag(h
, TAG_A
, "chI", "In", buf
);
1096 t
= print_otag(h
, TAG_A
, "c", "In");
1098 print_text(h
, n
->string
);
1104 for ( ; n
; n
= n
->next
) {
1105 assert(n
->type
== ROFFT_TEXT
);
1106 print_text(h
, n
->string
);
1113 mdoc_vt_pre(MDOC_ARGS
)
1115 if (n
->type
== ROFFT_BLOCK
) {
1118 } else if (n
->type
== ROFFT_ELEM
) {
1120 } else if (n
->type
== ROFFT_HEAD
)
1123 print_otag(h
, TAG_VAR
, "c", "Vt");
1128 mdoc_ft_pre(MDOC_ARGS
)
1131 print_otag(h
, TAG_VAR
, "c", "Ft");
1136 mdoc_fn_pre(MDOC_ARGS
)
1140 const char *sp
, *ep
;
1143 pretty
= NODE_SYNPRETTY
& n
->flags
;
1146 /* Split apart into type and name. */
1147 assert(n
->child
->string
);
1148 sp
= n
->child
->string
;
1150 ep
= strchr(sp
, ' ');
1152 t
= print_otag(h
, TAG_VAR
, "c", "Ft");
1155 sz
= MIN((int)(ep
- sp
), BUFSIZ
- 1);
1156 (void)memcpy(nbuf
, sp
, (size_t)sz
);
1158 print_text(h
, nbuf
);
1160 ep
= strchr(sp
, ' ');
1165 t
= print_otag_id(h
, TAG_CODE
, "Fn", n
);
1172 h
->flags
|= HTML_NOSPACE
;
1174 h
->flags
|= HTML_NOSPACE
;
1176 for (n
= n
->child
->next
; n
; n
= n
->next
) {
1177 if (NODE_SYNPRETTY
& n
->flags
)
1178 t
= print_otag(h
, TAG_VAR
, "cs", "Fa",
1179 "white-space", "nowrap");
1181 t
= print_otag(h
, TAG_VAR
, "c", "Fa");
1182 print_text(h
, n
->string
);
1185 h
->flags
|= HTML_NOSPACE
;
1190 h
->flags
|= HTML_NOSPACE
;
1194 h
->flags
|= HTML_NOSPACE
;
1202 mdoc_sm_pre(MDOC_ARGS
)
1205 if (NULL
== n
->child
)
1206 h
->flags
^= HTML_NONOSPACE
;
1207 else if (0 == strcmp("on", n
->child
->string
))
1208 h
->flags
&= ~HTML_NONOSPACE
;
1210 h
->flags
|= HTML_NONOSPACE
;
1212 if ( ! (HTML_NONOSPACE
& h
->flags
))
1213 h
->flags
&= ~HTML_NOSPACE
;
1219 mdoc_skip_pre(MDOC_ARGS
)
1226 mdoc_pp_pre(MDOC_ARGS
)
1230 if (n
->flags
& NODE_NOFILL
) {
1232 if (n
->flags
& NODE_ID
)
1233 mdoc_tg_pre(meta
, n
, h
);
1239 html_close_paragraph(h
);
1240 id
= n
->flags
& NODE_ID
? html_make_id(n
, 1) : NULL
;
1241 print_otag(h
, TAG_P
, "ci", "Pp", id
);
1248 mdoc_lk_pre(MDOC_ARGS
)
1250 const struct roff_node
*link
, *descr
, *punct
;
1253 if ((link
= n
->child
) == NULL
)
1256 /* Find beginning of trailing punctuation. */
1258 while (punct
!= link
&& punct
->flags
& NODE_DELIMC
)
1259 punct
= punct
->prev
;
1260 punct
= punct
->next
;
1262 /* Link target and link text. */
1265 descr
= link
; /* no text */
1266 t
= print_otag(h
, TAG_A
, "ch", "Lk", link
->string
);
1268 if (descr
->flags
& (NODE_DELIMC
| NODE_DELIMO
))
1269 h
->flags
|= HTML_NOSPACE
;
1270 print_text(h
, descr
->string
);
1271 descr
= descr
->next
;
1272 } while (descr
!= punct
);
1275 /* Trailing punctuation. */
1276 while (punct
!= NULL
) {
1277 h
->flags
|= HTML_NOSPACE
;
1278 print_text(h
, punct
->string
);
1279 punct
= punct
->next
;
1285 mdoc_mt_pre(MDOC_ARGS
)
1290 for (n
= n
->child
; n
; n
= n
->next
) {
1291 assert(n
->type
== ROFFT_TEXT
);
1292 mandoc_asprintf(&cp
, "mailto:%s", n
->string
);
1293 t
= print_otag(h
, TAG_A
, "ch", "Mt", cp
);
1294 print_text(h
, n
->string
);
1302 mdoc_fo_pre(MDOC_ARGS
)
1311 if (n
->child
!= NULL
) {
1312 t
= print_otag_id(h
, TAG_CODE
, "Fn", n
);
1313 print_text(h
, n
->child
->string
);
1318 h
->flags
|= HTML_NOSPACE
;
1320 h
->flags
|= HTML_NOSPACE
;
1328 mdoc_fo_post(MDOC_ARGS
)
1330 if (n
->type
!= ROFFT_BODY
)
1332 h
->flags
|= HTML_NOSPACE
;
1334 h
->flags
|= HTML_NOSPACE
;
1339 mdoc_in_pre(MDOC_ARGS
)
1344 print_otag(h
, TAG_CODE
, "c", "In");
1347 * The first argument of the `In' gets special treatment as
1348 * being a linked value. Subsequent values are printed
1349 * afterward. groff does similarly. This also handles the case
1353 if (NODE_SYNPRETTY
& n
->flags
&& NODE_LINE
& n
->flags
)
1354 print_text(h
, "#include");
1357 h
->flags
|= HTML_NOSPACE
;
1359 if (NULL
!= (n
= n
->child
)) {
1360 assert(n
->type
== ROFFT_TEXT
);
1362 if (h
->base_includes
)
1363 t
= print_otag(h
, TAG_A
, "chI", "In", n
->string
);
1365 t
= print_otag(h
, TAG_A
, "c", "In");
1366 print_text(h
, n
->string
);
1372 h
->flags
|= HTML_NOSPACE
;
1375 for ( ; n
; n
= n
->next
) {
1376 assert(n
->type
== ROFFT_TEXT
);
1377 print_text(h
, n
->string
);
1383 mdoc_va_pre(MDOC_ARGS
)
1385 print_otag(h
, TAG_VAR
, "c", "Va");
1390 mdoc_ap_pre(MDOC_ARGS
)
1392 h
->flags
|= HTML_NOSPACE
;
1393 print_text(h
, "\\(aq");
1394 h
->flags
|= HTML_NOSPACE
;
1399 mdoc_bf_pre(MDOC_ARGS
)
1405 html_close_paragraph(h
);
1415 if (FONT_Em
== n
->norm
->Bf
.font
)
1417 else if (FONT_Sy
== n
->norm
->Bf
.font
)
1419 else if (FONT_Li
== n
->norm
->Bf
.font
)
1424 /* Cannot use TAG_SPAN because it may contain blocks. */
1425 print_otag(h
, TAG_DIV
, "c", cattr
);
1430 mdoc_igndelim_pre(MDOC_ARGS
)
1432 h
->flags
|= HTML_IGNDELIM
;
1437 mdoc_pf_post(MDOC_ARGS
)
1439 if ( ! (n
->next
== NULL
|| n
->next
->flags
& NODE_LINE
))
1440 h
->flags
|= HTML_NOSPACE
;
1444 mdoc_rs_pre(MDOC_ARGS
)
1448 if (n
->sec
== SEC_SEE_ALSO
)
1449 html_close_paragraph(h
);
1454 if (n
->sec
== SEC_SEE_ALSO
)
1455 print_otag(h
, TAG_P
, "c", "Pp");
1456 print_otag(h
, TAG_CITE
, "c", "Rs");
1465 mdoc_no_pre(MDOC_ARGS
)
1467 print_otag_id(h
, TAG_SPAN
, roff_name
[n
->tok
], n
);
1472 mdoc_sy_pre(MDOC_ARGS
)
1474 print_otag_id(h
, TAG_B
, "Sy", n
);
1479 mdoc_lb_pre(MDOC_ARGS
)
1481 if (n
->sec
== SEC_LIBRARY
&&
1482 n
->flags
& NODE_LINE
&&
1483 roff_node_prev(n
) != NULL
)
1484 print_otag(h
, TAG_BR
, "");
1486 print_otag(h
, TAG_SPAN
, "c", "Lb");
1491 mdoc__x_pre(MDOC_ARGS
)
1493 struct roff_node
*nn
;
1502 if ((nn
= roff_node_prev(n
)) != NULL
&& nn
->tok
== MDOC__A
&&
1503 ((nn
= roff_node_next(n
)) == NULL
|| nn
->tok
!= MDOC__A
))
1504 print_text(h
, "and");
1543 print_otag(h
, TAG_A
, "ch", "RsU", n
->child
->string
);
1552 print_otag(h
, t
, "c", cattr
);
1557 mdoc__x_post(MDOC_ARGS
)
1559 struct roff_node
*nn
;
1561 if (n
->tok
== MDOC__A
&&
1562 (nn
= roff_node_next(n
)) != NULL
&& nn
->tok
== MDOC__A
&&
1563 ((nn
= roff_node_next(nn
)) == NULL
|| nn
->tok
!= MDOC__A
) &&
1564 ((nn
= roff_node_prev(n
)) == NULL
|| nn
->tok
!= MDOC__A
))
1569 if (n
->parent
== NULL
|| n
->parent
->tok
!= MDOC_Rs
)
1572 h
->flags
|= HTML_NOSPACE
;
1573 print_text(h
, roff_node_next(n
) ? "," : ".");
1577 mdoc_bk_pre(MDOC_ARGS
)
1586 if (n
->parent
->args
!= NULL
|| n
->prev
->child
== NULL
)
1587 h
->flags
|= HTML_PREKEEP
;
1597 mdoc_bk_post(MDOC_ARGS
)
1600 if (n
->type
== ROFFT_BODY
)
1601 h
->flags
&= ~(HTML_KEEP
| HTML_PREKEEP
);
1605 mdoc_quote_pre(MDOC_ARGS
)
1607 if (n
->type
!= ROFFT_BODY
)
1613 print_text(h
, n
->child
!= NULL
&& n
->child
->next
== NULL
&&
1614 n
->child
->tok
== MDOC_Mt
? "<" : "\\(la");
1618 print_text(h
, "\\(lC");
1622 print_text(h
, "\\(lB");
1626 print_text(h
, "\\(lB");
1628 * Give up on semantic markup for now.
1629 * We cannot use TAG_SPAN because .Oo may contain blocks.
1630 * We cannot use TAG_DIV because we might be in a
1631 * phrasing context (like .Dl or .Pp); we cannot
1632 * close out a .Pp at this point either because
1633 * that would break the line.
1635 /* XXX print_otag(h, TAG_???, "c", "Op"); */
1638 if (NULL
== n
->norm
->Es
||
1639 NULL
== n
->norm
->Es
->child
)
1641 print_text(h
, n
->norm
->Es
->child
->string
);
1645 print_text(h
, "\\(lq");
1649 print_text(h
, "\"");
1656 print_text(h
, "\\(oq");
1657 h
->flags
|= HTML_NOSPACE
;
1658 print_otag(h
, TAG_CODE
, "c", "Li");
1662 print_text(h
, "\\(oq");
1668 h
->flags
|= HTML_NOSPACE
;
1673 mdoc_quote_post(MDOC_ARGS
)
1676 if (n
->type
!= ROFFT_BODY
&& n
->type
!= ROFFT_ELEM
)
1679 h
->flags
|= HTML_NOSPACE
;
1684 print_text(h
, n
->child
!= NULL
&& n
->child
->next
== NULL
&&
1685 n
->child
->tok
== MDOC_Mt
? ">" : "\\(ra");
1689 print_text(h
, "\\(rC");
1695 print_text(h
, "\\(rB");
1698 if (n
->norm
->Es
== NULL
||
1699 n
->norm
->Es
->child
== NULL
||
1700 n
->norm
->Es
->child
->next
== NULL
)
1701 h
->flags
&= ~HTML_NOSPACE
;
1703 print_text(h
, n
->norm
->Es
->child
->next
->string
);
1707 print_text(h
, "\\(rq");
1711 print_text(h
, "\"");
1720 print_text(h
, "\\(cq");
1728 mdoc_eo_pre(MDOC_ARGS
)
1731 if (n
->type
!= ROFFT_BODY
)
1734 if (n
->end
== ENDBODY_NOT
&&
1735 n
->parent
->head
->child
== NULL
&&
1737 n
->child
->end
!= ENDBODY_NOT
)
1738 print_text(h
, "\\&");
1739 else if (n
->end
!= ENDBODY_NOT
? n
->child
!= NULL
:
1740 n
->parent
->head
->child
!= NULL
&& (n
->child
!= NULL
||
1741 (n
->parent
->tail
!= NULL
&& n
->parent
->tail
->child
!= NULL
)))
1742 h
->flags
|= HTML_NOSPACE
;
1747 mdoc_eo_post(MDOC_ARGS
)
1751 if (n
->type
!= ROFFT_BODY
)
1754 if (n
->end
!= ENDBODY_NOT
) {
1755 h
->flags
&= ~HTML_NOSPACE
;
1759 body
= n
->child
!= NULL
|| n
->parent
->head
->child
!= NULL
;
1760 tail
= n
->parent
->tail
!= NULL
&& n
->parent
->tail
->child
!= NULL
;
1763 h
->flags
|= HTML_NOSPACE
;
1765 h
->flags
&= ~HTML_NOSPACE
;
1769 mdoc_abort_pre(MDOC_ARGS
)