1 /* $Id: mdoc_html.c,v 1.350 2022/07/06 16:05:40 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 t
= print_otag(h
, TAG_HEAD
, "");
297 print_mdoc_head(mdoc
, h
);
299 if (n
!= NULL
&& n
->type
== ROFFT_COMMENT
)
300 print_gen_comment(h
, n
);
301 print_otag(h
, TAG_BODY
, "");
304 mdoc_root_pre(mdoc
, h
);
305 t
= print_otag(h
, TAG_MAIN
, "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_DIV
, "cr?", "foot", "doc-pagefooter",
459 "aria-label", "Manual footer line");
461 print_otag(h
, TAG_SPAN
, "c", "foot-left");
464 print_otag(h
, TAG_SPAN
, "c", "foot-date");
465 print_text(h
, meta
->date
);
468 print_otag(h
, TAG_SPAN
, "c", "foot-os");
469 print_text(h
, meta
->os
);
474 mdoc_root_pre(const struct roff_meta
*meta
, struct html
*h
)
477 char *volume
, *title
;
479 if (NULL
== meta
->arch
)
480 volume
= mandoc_strdup(meta
->vol
);
482 mandoc_asprintf(&volume
, "%s (%s)",
483 meta
->vol
, meta
->arch
);
485 if (NULL
== meta
->msec
)
486 title
= mandoc_strdup(meta
->title
);
488 mandoc_asprintf(&title
, "%s(%s)",
489 meta
->title
, meta
->msec
);
491 t
= print_otag(h
, TAG_DIV
, "cr?", "head", "doc-pageheader",
492 "aria-label", "Manual header line");
494 print_otag(h
, TAG_SPAN
, "c", "head-ltitle");
495 print_text(h
, title
);
498 print_otag(h
, TAG_SPAN
, "c", "head-vol");
499 print_text(h
, volume
);
502 print_otag(h
, TAG_SPAN
, "c", "head-rtitle");
503 print_text(h
, title
);
512 mdoc_code_pre(MDOC_ARGS
)
514 print_otag_id(h
, TAG_CODE
, roff_name
[n
->tok
], n
);
519 mdoc_sh_pre(MDOC_ARGS
)
521 struct roff_node
*sn
, *subn
;
522 struct tag
*t
, *tnav
, *tsec
, *tsub
;
528 html_close_paragraph(h
);
529 if ((h
->oflags
& HTML_TOC
) == 0 ||
530 h
->flags
& HTML_TOCDONE
||
531 n
->sec
<= SEC_SYNOPSIS
) {
532 print_otag(h
, TAG_SECTION
, "c", "Sh");
535 h
->flags
|= HTML_TOCDONE
;
537 for (sn
= n
->next
; sn
!= NULL
; sn
= sn
->next
)
538 if (sn
->sec
== SEC_CUSTOM
)
543 tnav
= print_otag(h
, TAG_NAV
, "r", "doc-toc");
544 t
= print_otag(h
, TAG_H2
, "c", "Sh");
545 print_text(h
, "TABLE OF CONTENTS");
547 t
= print_otag(h
, TAG_UL
, "c", "Bl-compact");
548 for (sn
= n
; sn
!= NULL
; sn
= sn
->next
) {
549 tsec
= print_otag(h
, TAG_LI
, "");
550 id
= html_make_id(sn
->head
, 0);
551 tsub
= print_otag(h
, TAG_A
, "hR", id
);
553 print_mdoc_nodelist(meta
, sn
->head
->child
, h
);
556 for (subn
= sn
->body
->child
; subn
!= NULL
;
558 if (subn
->tok
!= MDOC_Ss
)
560 id
= html_make_id(subn
->head
, 0);
564 print_otag(h
, TAG_UL
,
566 tsub
= print_otag(h
, TAG_LI
, "");
567 print_otag(h
, TAG_A
, "hR", id
);
569 print_mdoc_nodelist(meta
,
570 subn
->head
->child
, h
);
576 print_otag(h
, TAG_SECTION
, "c", "Sh");
579 print_otag_id(h
, TAG_H2
, "Sh", n
);
582 if (n
->sec
== SEC_AUTHORS
)
583 h
->flags
&= ~(HTML_SPLIT
|HTML_NOSPLIT
);
592 mdoc_ss_pre(MDOC_ARGS
)
596 html_close_paragraph(h
);
597 print_otag(h
, TAG_SECTION
, "c", "Ss");
600 print_otag_id(h
, TAG_H3
, "Ss", n
);
611 mdoc_fl_pre(MDOC_ARGS
)
613 struct roff_node
*nn
;
615 print_otag_id(h
, TAG_CODE
, "Fl", n
);
616 print_text(h
, "\\-");
617 if (n
->child
!= NULL
||
618 ((nn
= roff_node_next(n
)) != NULL
&&
619 nn
->type
!= ROFFT_TEXT
&&
620 (nn
->flags
& NODE_LINE
) == 0))
621 h
->flags
|= HTML_NOSPACE
;
627 mdoc_nd_pre(MDOC_ARGS
)
639 print_text(h
, "\\(em");
640 print_otag(h
, TAG_SPAN
, "cr", "Nd", "doc-subtitle");
645 mdoc_nm_pre(MDOC_ARGS
)
651 print_otag(h
, TAG_TD
, "");
654 print_otag(h
, TAG_CODE
, "c", "Nm");
657 print_otag(h
, TAG_TD
, "");
662 html_close_paragraph(h
);
664 print_otag(h
, TAG_TABLE
, "c", "Nm");
665 print_otag(h
, TAG_TR
, "");
670 mdoc_xr_pre(MDOC_ARGS
)
672 char *name
, *section
, *label
;
674 if (n
->child
== NULL
)
677 name
= n
->child
->string
;
678 if (n
->child
->next
!= NULL
) {
679 section
= n
->child
->next
->string
;
680 mandoc_asprintf(&label
, "%s, section %s", name
, section
);
682 section
= label
= NULL
;
685 print_otag(h
, TAG_A
, "chM?", "Xr",
686 name
, section
, "aria-label", label
);
688 print_otag(h
, TAG_A
, "c?", "Xr", "aria-label", label
);
696 h
->flags
|= HTML_NOSPACE
;
698 h
->flags
|= HTML_NOSPACE
;
699 print_text(h
, section
);
700 h
->flags
|= HTML_NOSPACE
;
706 mdoc_tg_pre(MDOC_ARGS
)
710 if ((id
= html_make_id(n
, 1)) != NULL
) {
711 print_tagq(h
, print_otag(h
, TAG_MARK
, "i", id
));
718 mdoc_ns_pre(MDOC_ARGS
)
721 if ( ! (NODE_LINE
& n
->flags
))
722 h
->flags
|= HTML_NOSPACE
;
727 mdoc_ar_pre(MDOC_ARGS
)
729 print_otag(h
, TAG_VAR
, "c", "Ar");
734 mdoc_xx_pre(MDOC_ARGS
)
736 print_otag(h
, TAG_SPAN
, "c", "Ux");
741 mdoc_it_pre(MDOC_ARGS
)
743 const struct roff_node
*bl
;
747 while (bl
->tok
!= MDOC_Bl
)
749 type
= bl
->norm
->Bl
.type
;
761 print_otag_id(h
, TAG_LI
, NULL
, n
);
773 print_otag_id(h
, TAG_DT
, NULL
, n
);
776 print_otag(h
, TAG_DD
, "");
785 print_otag_id(h
, TAG_DT
, NULL
, n
);
788 if (n
->child
== NULL
) {
789 print_otag(h
, TAG_DD
, "s", "width", "auto");
790 print_text(h
, "\\ ");
792 print_otag(h
, TAG_DD
, "");
803 print_otag(h
, TAG_TD
, "");
806 print_otag_id(h
, TAG_TR
, NULL
, n
);
816 mdoc_bl_pre(MDOC_ARGS
)
820 enum htmltag elemtype
;
824 html_close_paragraph(h
);
838 (void)strlcpy(cattr
, "Bl-bullet", sizeof(cattr
));
843 (void)strlcpy(cattr
, "Bl-dash", sizeof(cattr
));
847 (void)strlcpy(cattr
, "Bl-item", sizeof(cattr
));
851 (void)strlcpy(cattr
, "Bl-enum", sizeof(cattr
));
855 (void)strlcpy(cattr
, "Bl-diag", sizeof(cattr
));
859 (void)strlcpy(cattr
, "Bl-hang", sizeof(cattr
));
863 (void)strlcpy(cattr
, "Bl-inset", sizeof(cattr
));
867 (void)strlcpy(cattr
, "Bl-ohang", sizeof(cattr
));
871 print_otag(h
, TAG_DIV
, "c", "Bd-indent");
872 print_otag_id(h
, TAG_DL
,
873 bl
->comp
? "Bl-tag Bl-compact" : "Bl-tag", n
->body
);
876 elemtype
= TAG_TABLE
;
877 (void)strlcpy(cattr
, "Bl-column", sizeof(cattr
));
882 if (bl
->offs
!= NULL
)
883 (void)strlcat(cattr
, " Bd-indent", sizeof(cattr
));
885 (void)strlcat(cattr
, " Bl-compact", sizeof(cattr
));
886 print_otag_id(h
, elemtype
, cattr
, n
->body
);
891 mdoc_ex_pre(MDOC_ARGS
)
893 if (roff_node_prev(n
) != NULL
)
894 print_otag(h
, TAG_BR
, "");
899 mdoc_st_pre(MDOC_ARGS
)
901 print_otag(h
, TAG_SPAN
, "c", "St");
906 mdoc_em_pre(MDOC_ARGS
)
908 print_otag_id(h
, TAG_I
, "Em", n
);
913 mdoc_d1_pre(MDOC_ARGS
)
917 html_close_paragraph(h
);
926 print_otag_id(h
, TAG_DIV
, "Bd Bd-indent", n
);
927 if (n
->tok
== MDOC_Dl
)
928 print_otag(h
, TAG_CODE
, "c", "Li");
933 mdoc_sx_pre(MDOC_ARGS
)
937 id
= html_make_id(n
, 0);
938 print_otag(h
, TAG_A
, "chR", "Sx", id
);
944 mdoc_bd_pre(MDOC_ARGS
)
947 struct roff_node
*nn
;
952 html_close_paragraph(h
);
962 /* Handle preceding whitespace. */
964 comp
= n
->norm
->Bd
.comp
;
965 for (nn
= n
; nn
!= NULL
&& comp
== 0; nn
= nn
->parent
) {
966 if (nn
->type
!= ROFFT_BLOCK
)
968 if (nn
->tok
== MDOC_Sh
|| nn
->tok
== MDOC_Ss
)
970 if (roff_node_prev(nn
) != NULL
)
973 (void)strlcpy(buf
, "Bd", sizeof(buf
));
975 (void)strlcat(buf
, " Pp", sizeof(buf
));
977 /* Handle the -offset argument. */
979 if (n
->norm
->Bd
.offs
!= NULL
&&
980 strcmp(n
->norm
->Bd
.offs
, "left") != 0)
981 (void)strlcat(buf
, " Bd-indent", sizeof(buf
));
983 if (n
->norm
->Bd
.type
== DISP_literal
)
984 (void)strlcat(buf
, " Li", sizeof(buf
));
986 print_otag_id(h
, TAG_DIV
, buf
, n
);
991 mdoc_pa_pre(MDOC_ARGS
)
993 print_otag(h
, TAG_SPAN
, "c", "Pa");
998 mdoc_ad_pre(MDOC_ARGS
)
1000 print_otag(h
, TAG_SPAN
, "c", "Ad");
1005 mdoc_an_pre(MDOC_ARGS
)
1007 if (n
->norm
->An
.auth
== AUTH_split
) {
1008 h
->flags
&= ~HTML_NOSPLIT
;
1009 h
->flags
|= HTML_SPLIT
;
1012 if (n
->norm
->An
.auth
== AUTH_nosplit
) {
1013 h
->flags
&= ~HTML_SPLIT
;
1014 h
->flags
|= HTML_NOSPLIT
;
1018 if (h
->flags
& HTML_SPLIT
)
1019 print_otag(h
, TAG_BR
, "");
1021 if (n
->sec
== SEC_AUTHORS
&& ! (h
->flags
& HTML_NOSPLIT
))
1022 h
->flags
|= HTML_SPLIT
;
1024 print_otag(h
, TAG_SPAN
, "c", "An");
1029 mdoc_cd_pre(MDOC_ARGS
)
1032 print_otag(h
, TAG_CODE
, "c", "Cd");
1037 mdoc_fa_pre(MDOC_ARGS
)
1039 const struct roff_node
*nn
;
1042 if (n
->parent
->tok
!= MDOC_Fo
) {
1043 print_otag(h
, TAG_VAR
, "c", "Fa");
1046 for (nn
= n
->child
; nn
!= NULL
; nn
= nn
->next
) {
1047 t
= print_otag(h
, TAG_VAR
, "c", "Fa");
1048 print_text(h
, nn
->string
);
1050 if (nn
->next
!= NULL
) {
1051 h
->flags
|= HTML_NOSPACE
;
1055 if (n
->child
!= NULL
&&
1056 (nn
= roff_node_next(n
)) != NULL
&&
1057 nn
->tok
== MDOC_Fa
) {
1058 h
->flags
|= HTML_NOSPACE
;
1065 mdoc_fd_pre(MDOC_ARGS
)
1072 if (NULL
== (n
= n
->child
))
1075 assert(n
->type
== ROFFT_TEXT
);
1077 if (strcmp(n
->string
, "#include")) {
1078 print_otag(h
, TAG_CODE
, "c", "Fd");
1082 print_otag(h
, TAG_CODE
, "c", "In");
1083 print_text(h
, n
->string
);
1085 if (NULL
!= (n
= n
->next
)) {
1086 assert(n
->type
== ROFFT_TEXT
);
1088 if (h
->base_includes
) {
1090 if (*cp
== '<' || *cp
== '"')
1092 buf
= mandoc_strdup(cp
);
1093 cp
= strchr(buf
, '\0') - 1;
1094 if (cp
>= buf
&& (*cp
== '>' || *cp
== '"'))
1096 t
= print_otag(h
, TAG_A
, "chI", "In", buf
);
1099 t
= print_otag(h
, TAG_A
, "c", "In");
1101 print_text(h
, n
->string
);
1107 for ( ; n
; n
= n
->next
) {
1108 assert(n
->type
== ROFFT_TEXT
);
1109 print_text(h
, n
->string
);
1116 mdoc_vt_pre(MDOC_ARGS
)
1118 if (n
->type
== ROFFT_BLOCK
) {
1121 } else if (n
->type
== ROFFT_ELEM
) {
1123 } else if (n
->type
== ROFFT_HEAD
)
1126 print_otag(h
, TAG_VAR
, "c", "Vt");
1131 mdoc_ft_pre(MDOC_ARGS
)
1134 print_otag(h
, TAG_VAR
, "c", "Ft");
1139 mdoc_fn_pre(MDOC_ARGS
)
1143 const char *sp
, *ep
;
1146 pretty
= NODE_SYNPRETTY
& n
->flags
;
1149 /* Split apart into type and name. */
1150 assert(n
->child
->string
);
1151 sp
= n
->child
->string
;
1153 ep
= strchr(sp
, ' ');
1155 t
= print_otag(h
, TAG_VAR
, "c", "Ft");
1158 sz
= MIN((int)(ep
- sp
), BUFSIZ
- 1);
1159 (void)memcpy(nbuf
, sp
, (size_t)sz
);
1161 print_text(h
, nbuf
);
1163 ep
= strchr(sp
, ' ');
1168 t
= print_otag_id(h
, TAG_CODE
, "Fn", n
);
1175 h
->flags
|= HTML_NOSPACE
;
1177 h
->flags
|= HTML_NOSPACE
;
1179 for (n
= n
->child
->next
; n
; n
= n
->next
) {
1180 if (NODE_SYNPRETTY
& n
->flags
)
1181 t
= print_otag(h
, TAG_VAR
, "cs", "Fa",
1182 "white-space", "nowrap");
1184 t
= print_otag(h
, TAG_VAR
, "c", "Fa");
1185 print_text(h
, n
->string
);
1188 h
->flags
|= HTML_NOSPACE
;
1193 h
->flags
|= HTML_NOSPACE
;
1197 h
->flags
|= HTML_NOSPACE
;
1205 mdoc_sm_pre(MDOC_ARGS
)
1208 if (NULL
== n
->child
)
1209 h
->flags
^= HTML_NONOSPACE
;
1210 else if (0 == strcmp("on", n
->child
->string
))
1211 h
->flags
&= ~HTML_NONOSPACE
;
1213 h
->flags
|= HTML_NONOSPACE
;
1215 if ( ! (HTML_NONOSPACE
& h
->flags
))
1216 h
->flags
&= ~HTML_NOSPACE
;
1222 mdoc_skip_pre(MDOC_ARGS
)
1229 mdoc_pp_pre(MDOC_ARGS
)
1233 if (n
->flags
& NODE_NOFILL
) {
1235 if (n
->flags
& NODE_ID
)
1236 mdoc_tg_pre(meta
, n
, h
);
1242 html_close_paragraph(h
);
1243 id
= n
->flags
& NODE_ID
? html_make_id(n
, 1) : NULL
;
1244 print_otag(h
, TAG_P
, "ci", "Pp", id
);
1251 mdoc_lk_pre(MDOC_ARGS
)
1253 const struct roff_node
*link
, *descr
, *punct
;
1256 if ((link
= n
->child
) == NULL
)
1259 /* Find beginning of trailing punctuation. */
1261 while (punct
!= link
&& punct
->flags
& NODE_DELIMC
)
1262 punct
= punct
->prev
;
1263 punct
= punct
->next
;
1265 /* Link target and link text. */
1268 descr
= link
; /* no text */
1269 t
= print_otag(h
, TAG_A
, "ch", "Lk", link
->string
);
1271 if (descr
->flags
& (NODE_DELIMC
| NODE_DELIMO
))
1272 h
->flags
|= HTML_NOSPACE
;
1273 print_text(h
, descr
->string
);
1274 descr
= descr
->next
;
1275 } while (descr
!= punct
);
1278 /* Trailing punctuation. */
1279 while (punct
!= NULL
) {
1280 h
->flags
|= HTML_NOSPACE
;
1281 print_text(h
, punct
->string
);
1282 punct
= punct
->next
;
1288 mdoc_mt_pre(MDOC_ARGS
)
1293 for (n
= n
->child
; n
; n
= n
->next
) {
1294 assert(n
->type
== ROFFT_TEXT
);
1295 mandoc_asprintf(&cp
, "mailto:%s", n
->string
);
1296 t
= print_otag(h
, TAG_A
, "ch", "Mt", cp
);
1297 print_text(h
, n
->string
);
1305 mdoc_fo_pre(MDOC_ARGS
)
1314 if (n
->child
!= NULL
) {
1315 t
= print_otag_id(h
, TAG_CODE
, "Fn", n
);
1316 print_text(h
, n
->child
->string
);
1321 h
->flags
|= HTML_NOSPACE
;
1323 h
->flags
|= HTML_NOSPACE
;
1331 mdoc_fo_post(MDOC_ARGS
)
1333 if (n
->type
!= ROFFT_BODY
)
1335 h
->flags
|= HTML_NOSPACE
;
1337 h
->flags
|= HTML_NOSPACE
;
1342 mdoc_in_pre(MDOC_ARGS
)
1347 print_otag(h
, TAG_CODE
, "c", "In");
1350 * The first argument of the `In' gets special treatment as
1351 * being a linked value. Subsequent values are printed
1352 * afterward. groff does similarly. This also handles the case
1356 if (NODE_SYNPRETTY
& n
->flags
&& NODE_LINE
& n
->flags
)
1357 print_text(h
, "#include");
1360 h
->flags
|= HTML_NOSPACE
;
1362 if (NULL
!= (n
= n
->child
)) {
1363 assert(n
->type
== ROFFT_TEXT
);
1365 if (h
->base_includes
)
1366 t
= print_otag(h
, TAG_A
, "chI", "In", n
->string
);
1368 t
= print_otag(h
, TAG_A
, "c", "In");
1369 print_text(h
, n
->string
);
1375 h
->flags
|= HTML_NOSPACE
;
1378 for ( ; n
; n
= n
->next
) {
1379 assert(n
->type
== ROFFT_TEXT
);
1380 print_text(h
, n
->string
);
1386 mdoc_va_pre(MDOC_ARGS
)
1388 print_otag(h
, TAG_VAR
, "c", "Va");
1393 mdoc_ap_pre(MDOC_ARGS
)
1395 h
->flags
|= HTML_NOSPACE
;
1396 print_text(h
, "\\(aq");
1397 h
->flags
|= HTML_NOSPACE
;
1402 mdoc_bf_pre(MDOC_ARGS
)
1408 html_close_paragraph(h
);
1418 if (FONT_Em
== n
->norm
->Bf
.font
)
1420 else if (FONT_Sy
== n
->norm
->Bf
.font
)
1422 else if (FONT_Li
== n
->norm
->Bf
.font
)
1427 /* Cannot use TAG_SPAN because it may contain blocks. */
1428 print_otag(h
, TAG_DIV
, "c", cattr
);
1433 mdoc_igndelim_pre(MDOC_ARGS
)
1435 h
->flags
|= HTML_IGNDELIM
;
1440 mdoc_pf_post(MDOC_ARGS
)
1442 if ( ! (n
->next
== NULL
|| n
->next
->flags
& NODE_LINE
))
1443 h
->flags
|= HTML_NOSPACE
;
1447 mdoc_rs_pre(MDOC_ARGS
)
1451 if (n
->sec
== SEC_SEE_ALSO
)
1452 html_close_paragraph(h
);
1457 if (n
->sec
== SEC_SEE_ALSO
)
1458 print_otag(h
, TAG_P
, "c", "Pp");
1459 print_otag(h
, TAG_CITE
, "c", "Rs");
1468 mdoc_no_pre(MDOC_ARGS
)
1470 print_otag_id(h
, TAG_SPAN
, roff_name
[n
->tok
], n
);
1475 mdoc_sy_pre(MDOC_ARGS
)
1477 print_otag_id(h
, TAG_B
, "Sy", n
);
1482 mdoc_lb_pre(MDOC_ARGS
)
1484 if (n
->sec
== SEC_LIBRARY
&&
1485 n
->flags
& NODE_LINE
&&
1486 roff_node_prev(n
) != NULL
)
1487 print_otag(h
, TAG_BR
, "");
1489 print_otag(h
, TAG_SPAN
, "c", "Lb");
1494 mdoc__x_pre(MDOC_ARGS
)
1496 struct roff_node
*nn
;
1505 if ((nn
= roff_node_prev(n
)) != NULL
&& nn
->tok
== MDOC__A
&&
1506 ((nn
= roff_node_next(n
)) == NULL
|| nn
->tok
!= MDOC__A
))
1507 print_text(h
, "and");
1546 print_otag(h
, TAG_A
, "ch", "RsU", n
->child
->string
);
1555 print_otag(h
, t
, "c", cattr
);
1560 mdoc__x_post(MDOC_ARGS
)
1562 struct roff_node
*nn
;
1564 if (n
->tok
== MDOC__A
&&
1565 (nn
= roff_node_next(n
)) != NULL
&& nn
->tok
== MDOC__A
&&
1566 ((nn
= roff_node_next(nn
)) == NULL
|| nn
->tok
!= MDOC__A
) &&
1567 ((nn
= roff_node_prev(n
)) == NULL
|| nn
->tok
!= MDOC__A
))
1572 if (n
->parent
== NULL
|| n
->parent
->tok
!= MDOC_Rs
)
1575 h
->flags
|= HTML_NOSPACE
;
1576 print_text(h
, roff_node_next(n
) ? "," : ".");
1580 mdoc_bk_pre(MDOC_ARGS
)
1589 if (n
->parent
->args
!= NULL
|| n
->prev
->child
== NULL
)
1590 h
->flags
|= HTML_PREKEEP
;
1600 mdoc_bk_post(MDOC_ARGS
)
1603 if (n
->type
== ROFFT_BODY
)
1604 h
->flags
&= ~(HTML_KEEP
| HTML_PREKEEP
);
1608 mdoc_quote_pre(MDOC_ARGS
)
1610 if (n
->type
!= ROFFT_BODY
)
1616 print_text(h
, n
->child
!= NULL
&& n
->child
->next
== NULL
&&
1617 n
->child
->tok
== MDOC_Mt
? "<" : "\\(la");
1621 print_text(h
, "\\(lC");
1625 print_text(h
, "\\(lB");
1629 print_text(h
, "\\(lB");
1631 * Give up on semantic markup for now.
1632 * We cannot use TAG_SPAN because .Oo may contain blocks.
1633 * We cannot use TAG_DIV because we might be in a
1634 * phrasing context (like .Dl or .Pp); we cannot
1635 * close out a .Pp at this point either because
1636 * that would break the line.
1638 /* XXX print_otag(h, TAG_???, "c", "Op"); */
1641 if (NULL
== n
->norm
->Es
||
1642 NULL
== n
->norm
->Es
->child
)
1644 print_text(h
, n
->norm
->Es
->child
->string
);
1648 print_text(h
, "\\(lq");
1652 print_text(h
, "\"");
1659 print_text(h
, "\\(oq");
1660 h
->flags
|= HTML_NOSPACE
;
1661 print_otag(h
, TAG_CODE
, "c", "Li");
1665 print_text(h
, "\\(oq");
1671 h
->flags
|= HTML_NOSPACE
;
1676 mdoc_quote_post(MDOC_ARGS
)
1679 if (n
->type
!= ROFFT_BODY
&& n
->type
!= ROFFT_ELEM
)
1682 h
->flags
|= HTML_NOSPACE
;
1687 print_text(h
, n
->child
!= NULL
&& n
->child
->next
== NULL
&&
1688 n
->child
->tok
== MDOC_Mt
? ">" : "\\(ra");
1692 print_text(h
, "\\(rC");
1698 print_text(h
, "\\(rB");
1701 if (n
->norm
->Es
== NULL
||
1702 n
->norm
->Es
->child
== NULL
||
1703 n
->norm
->Es
->child
->next
== NULL
)
1704 h
->flags
&= ~HTML_NOSPACE
;
1706 print_text(h
, n
->norm
->Es
->child
->next
->string
);
1710 print_text(h
, "\\(rq");
1714 print_text(h
, "\"");
1723 print_text(h
, "\\(cq");
1731 mdoc_eo_pre(MDOC_ARGS
)
1734 if (n
->type
!= ROFFT_BODY
)
1737 if (n
->end
== ENDBODY_NOT
&&
1738 n
->parent
->head
->child
== NULL
&&
1740 n
->child
->end
!= ENDBODY_NOT
)
1741 print_text(h
, "\\&");
1742 else if (n
->end
!= ENDBODY_NOT
? n
->child
!= NULL
:
1743 n
->parent
->head
->child
!= NULL
&& (n
->child
!= NULL
||
1744 (n
->parent
->tail
!= NULL
&& n
->parent
->tail
->child
!= NULL
)))
1745 h
->flags
|= HTML_NOSPACE
;
1750 mdoc_eo_post(MDOC_ARGS
)
1754 if (n
->type
!= ROFFT_BODY
)
1757 if (n
->end
!= ENDBODY_NOT
) {
1758 h
->flags
&= ~HTML_NOSPACE
;
1762 body
= n
->child
!= NULL
|| n
->parent
->head
->child
!= NULL
;
1763 tail
= n
->parent
->tail
!= NULL
&& n
->parent
->tail
->child
!= NULL
;
1766 h
->flags
|= HTML_NOSPACE
;
1768 h
->flags
&= ~HTML_NOSPACE
;
1772 mdoc_abort_pre(MDOC_ARGS
)