1*f6697133Sschwarze /* $OpenBSD: man_html.c,v 1.140 2023/10/24 20:30:49 schwarze Exp $ */
24175bdabSschwarze /*
3*f6697133Sschwarze * Copyright (c) 2013-15,2017-20,2022-23 Ingo Schwarze <schwarze@openbsd.org>
40ac7e6ecSschwarze * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
54175bdabSschwarze *
64175bdabSschwarze * Permission to use, copy, modify, and distribute this software for any
74175bdabSschwarze * purpose with or without fee is hereby granted, provided that the above
84175bdabSschwarze * copyright notice and this permission notice appear in all copies.
94175bdabSschwarze *
10d1982c71Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
114175bdabSschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d1982c71Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
134175bdabSschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144175bdabSschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154175bdabSschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164175bdabSschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
170ac7e6ecSschwarze *
180ac7e6ecSschwarze * HTML formatter for man(7) used by mandoc(1).
194175bdabSschwarze */
204175bdabSschwarze #include <sys/types.h>
214175bdabSschwarze
224175bdabSschwarze #include <assert.h>
234175bdabSschwarze #include <ctype.h>
244175bdabSschwarze #include <stdio.h>
254175bdabSschwarze #include <stdlib.h>
264175bdabSschwarze #include <string.h>
274175bdabSschwarze
28a92c1cd8Sschwarze #include "mandoc_aux.h"
292e362670Sschwarze #include "mandoc.h"
30d1982c71Sschwarze #include "roff.h"
31dd617d76Sschwarze #include "man.h"
324175bdabSschwarze #include "out.h"
334175bdabSschwarze #include "html.h"
344175bdabSschwarze #include "main.h"
354175bdabSschwarze
362a238f45Sschwarze #define MAN_ARGS const struct roff_meta *man, \
377ebbefbeSschwarze struct roff_node *n, \
384175bdabSschwarze struct html *h
394175bdabSschwarze
4016fe0cfcSschwarze struct man_html_act {
414175bdabSschwarze int (*pre)(MAN_ARGS);
424175bdabSschwarze int (*post)(MAN_ARGS);
434175bdabSschwarze };
444175bdabSschwarze
45cc202ecaSschwarze static void print_man_head(const struct roff_meta *,
46cc202ecaSschwarze struct html *);
474175bdabSschwarze static void print_man_nodelist(MAN_ARGS);
484175bdabSschwarze static void print_man_node(MAN_ARGS);
496f6388b4Sschwarze static char list_continues(const struct roff_node *,
506f6388b4Sschwarze const struct roff_node *);
5166ae7cc0Sschwarze static int man_B_pre(MAN_ARGS);
5266ae7cc0Sschwarze static int man_IP_pre(MAN_ARGS);
5366ae7cc0Sschwarze static int man_I_pre(MAN_ARGS);
54*f6697133Sschwarze static int man_MR_pre(MAN_ARGS);
5566ae7cc0Sschwarze static int man_OP_pre(MAN_ARGS);
5666ae7cc0Sschwarze static int man_PP_pre(MAN_ARGS);
5766ae7cc0Sschwarze static int man_RS_pre(MAN_ARGS);
5866ae7cc0Sschwarze static int man_SH_pre(MAN_ARGS);
5966ae7cc0Sschwarze static int man_SM_pre(MAN_ARGS);
605e5a9c61Sschwarze static int man_SY_pre(MAN_ARGS);
613aeff926Sschwarze static int man_UR_pre(MAN_ARGS);
624175bdabSschwarze static int man_alt_pre(MAN_ARGS);
634175bdabSschwarze static int man_ign_pre(MAN_ARGS);
64ddce0b0cSschwarze static int man_in_pre(MAN_ARGS);
65cc202ecaSschwarze static void man_root_post(const struct roff_meta *,
66cc202ecaSschwarze struct html *);
67cc202ecaSschwarze static void man_root_pre(const struct roff_meta *,
68cc202ecaSschwarze struct html *);
694175bdabSschwarze
7016fe0cfcSschwarze static const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
714175bdabSschwarze { NULL, NULL }, /* TH */
724175bdabSschwarze { man_SH_pre, NULL }, /* SH */
737f442bfeSschwarze { man_SH_pre, NULL }, /* SS */
744175bdabSschwarze { man_IP_pre, NULL }, /* TP */
75d991fc2cSschwarze { man_IP_pre, NULL }, /* TQ */
763f3c303aSschwarze { man_PP_pre, NULL }, /* LP */
774175bdabSschwarze { man_PP_pre, NULL }, /* PP */
783f3c303aSschwarze { man_PP_pre, NULL }, /* P */
794175bdabSschwarze { man_IP_pre, NULL }, /* IP */
807f442bfeSschwarze { man_PP_pre, NULL }, /* HP */
814175bdabSschwarze { man_SM_pre, NULL }, /* SM */
8264f4b2e7Sschwarze { man_SM_pre, NULL }, /* SB */
834175bdabSschwarze { man_alt_pre, NULL }, /* BI */
844175bdabSschwarze { man_alt_pre, NULL }, /* IB */
854175bdabSschwarze { man_alt_pre, NULL }, /* BR */
864175bdabSschwarze { man_alt_pre, NULL }, /* RB */
874175bdabSschwarze { NULL, NULL }, /* R */
884175bdabSschwarze { man_B_pre, NULL }, /* B */
894175bdabSschwarze { man_I_pre, NULL }, /* I */
904175bdabSschwarze { man_alt_pre, NULL }, /* IR */
914175bdabSschwarze { man_alt_pre, NULL }, /* RI */
924175bdabSschwarze { NULL, NULL }, /* RE */
934175bdabSschwarze { man_RS_pre, NULL }, /* RS */
944175bdabSschwarze { man_ign_pre, NULL }, /* DT */
954175bdabSschwarze { man_ign_pre, NULL }, /* UC */
96b822ca0dSschwarze { man_ign_pre, NULL }, /* PD */
97dfc705a3Sschwarze { man_ign_pre, NULL }, /* AT */
98ddce0b0cSschwarze { man_in_pre, NULL }, /* in */
995e5a9c61Sschwarze { man_SY_pre, NULL }, /* SY */
1005e5a9c61Sschwarze { NULL, NULL }, /* YS */
10166ae7cc0Sschwarze { man_OP_pre, NULL }, /* OP */
102de63e416Sschwarze { NULL, NULL }, /* EX */
103de63e416Sschwarze { NULL, NULL }, /* EE */
1043aeff926Sschwarze { man_UR_pre, NULL }, /* UR */
1053aeff926Sschwarze { NULL, NULL }, /* UE */
106df9a9479Sbentley { man_UR_pre, NULL }, /* MT */
107df9a9479Sbentley { NULL, NULL }, /* ME */
108*f6697133Sschwarze { man_MR_pre, NULL }, /* MR */
1094175bdabSschwarze };
1104175bdabSschwarze
11149aff9f8Sschwarze
1124175bdabSschwarze void
html_man(void * arg,const struct roff_meta * man)1136b86842eSschwarze html_man(void *arg, const struct roff_meta *man)
1144175bdabSschwarze {
1156d0e9b63Sschwarze struct html *h;
116cc202ecaSschwarze struct roff_node *n;
117ce781f36Sschwarze struct tag *t;
1184175bdabSschwarze
1196d0e9b63Sschwarze h = (struct html *)arg;
120cc202ecaSschwarze n = man->first->child;
121ca0ce676Sschwarze
122ce781f36Sschwarze if ((h->oflags & HTML_FRAGMENT) == 0) {
123ca0ce676Sschwarze print_gen_decls(h);
124ce781f36Sschwarze print_otag(h, TAG_HTML, "");
125ce781f36Sschwarze t = print_otag(h, TAG_HEAD, "");
1266b86842eSschwarze print_man_head(man, h);
127ce781f36Sschwarze print_tagq(h, t);
128501adfefSschwarze if (n != NULL && n->type == ROFFT_COMMENT)
129501adfefSschwarze print_gen_comment(h, n);
130229cc7fdSschwarze print_otag(h, TAG_BODY, "");
131ce781f36Sschwarze }
132467b61c6Sschwarze
1336b86842eSschwarze man_root_pre(man, h);
1346774f271Sschwarze t = print_otag(h, TAG_MAIN, "c", "manual-text");
1356b86842eSschwarze print_man_nodelist(man, n, h);
136c7402a19Sschwarze print_tagq(h, t);
1376b86842eSschwarze man_root_post(man, h);
138ce781f36Sschwarze print_tagq(h, NULL);
1394175bdabSschwarze }
1404175bdabSschwarze
1414175bdabSschwarze static void
print_man_head(const struct roff_meta * man,struct html * h)142cc202ecaSschwarze print_man_head(const struct roff_meta *man, struct html *h)
1434175bdabSschwarze {
144fef1eecdSschwarze char *cp;
1454175bdabSschwarze
1464175bdabSschwarze print_gen_head(h);
147fef1eecdSschwarze mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
148229cc7fdSschwarze print_otag(h, TAG_TITLE, "");
149fef1eecdSschwarze print_text(h, cp);
150fef1eecdSschwarze free(cp);
1514175bdabSschwarze }
1524175bdabSschwarze
1534175bdabSschwarze static void
print_man_nodelist(MAN_ARGS)1544175bdabSschwarze print_man_nodelist(MAN_ARGS)
1554175bdabSschwarze {
156e4534905Sschwarze while (n != NULL) {
157de63e416Sschwarze print_man_node(man, n, h);
158e4534905Sschwarze n = n->next;
159e4534905Sschwarze }
1604175bdabSschwarze }
1614175bdabSschwarze
1624175bdabSschwarze static void
print_man_node(MAN_ARGS)1634175bdabSschwarze print_man_node(MAN_ARGS)
1644175bdabSschwarze {
165de63e416Sschwarze struct tag *t;
166de63e416Sschwarze int child;
167de63e416Sschwarze
1682dd33770Sschwarze if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
1692dd33770Sschwarze return;
1702dd33770Sschwarze
17152e71e33Sschwarze if ((n->flags & NODE_NOFILL) == 0)
17252e71e33Sschwarze html_fillmode(h, ROFF_fi);
17352e71e33Sschwarze else if (html_fillmode(h, ROFF_nf) == ROFF_nf &&
17452e71e33Sschwarze n->tok != ROFF_fi && n->flags & NODE_LINE &&
17552e71e33Sschwarze (n->prev == NULL || n->prev->tok != MAN_YS))
17652e71e33Sschwarze print_endline(h);
177de63e416Sschwarze
178de63e416Sschwarze child = 1;
179de63e416Sschwarze switch (n->type) {
180de63e416Sschwarze case ROFFT_TEXT:
181275804acSschwarze if (*n->string == '\0') {
182275804acSschwarze print_endline(h);
183275804acSschwarze return;
184275804acSschwarze }
185275804acSschwarze if (*n->string == ' ' && n->flags & NODE_LINE &&
186275804acSschwarze (h->flags & HTML_NONEWLINE) == 0)
187d28d6572Sschwarze print_otag(h, TAG_BR, "");
188275804acSschwarze else if (n->flags & NODE_DELIMC)
189275804acSschwarze h->flags |= HTML_NOSPACE;
190520a575cSschwarze t = h->tag;
191520a575cSschwarze t->refcnt++;
1924175bdabSschwarze print_text(h, n->string);
1930692510cSschwarze break;
194d1982c71Sschwarze case ROFFT_EQN:
195520a575cSschwarze t = h->tag;
196520a575cSschwarze t->refcnt++;
197f8618d99Sschwarze print_eqn(h, n->eqn);
19819a69263Sschwarze break;
199d1982c71Sschwarze case ROFFT_TBL:
200366f22eeSschwarze /*
201366f22eeSschwarze * This will take care of initialising all of the table
202366f22eeSschwarze * state data for the first table, then tearing it down
203366f22eeSschwarze * for the last one.
204366f22eeSschwarze */
2052791bd1cSschwarze print_tbl(h, n->span);
20639fa4f70Sschwarze return;
2074175bdabSschwarze default:
208fa70b73eSschwarze /*
209fa70b73eSschwarze * Close out scope of font prior to opening a macro
210366f22eeSschwarze * scope.
211fa70b73eSschwarze */
212cefe8974Sschwarze if (h->metac != ESCAPE_FONTROMAN) {
2133a7b861cSschwarze h->metal = h->metac;
214cefe8974Sschwarze h->metac = ESCAPE_FONTROMAN;
215fa70b73eSschwarze }
216366f22eeSschwarze
217366f22eeSschwarze /*
218366f22eeSschwarze * Close out the current table, if it's open, and unset
219366f22eeSschwarze * the "meta" table state. This will be reopened on the
220366f22eeSschwarze * next table element.
221366f22eeSschwarze */
222520a575cSschwarze if (h->tblt != NULL)
223366f22eeSschwarze print_tblclose(h);
2244614a369Sschwarze t = h->tag;
225520a575cSschwarze t->refcnt++;
22629478532Sschwarze if (n->tok < ROFF_MAX) {
22796a5de47Sschwarze roff_html_pre(h, n);
228520a575cSschwarze t->refcnt--;
229275804acSschwarze print_stagq(h, t);
230275804acSschwarze return;
23129478532Sschwarze }
23229478532Sschwarze assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
23316fe0cfcSschwarze if (man_html_acts[n->tok - MAN_TH].pre != NULL)
23416fe0cfcSschwarze child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
23516fe0cfcSschwarze n, h);
2364175bdabSschwarze break;
2374175bdabSschwarze }
2384175bdabSschwarze
23918c80517Sschwarze if (child && n->child != NULL)
240de63e416Sschwarze print_man_nodelist(man, n->child, h);
2414175bdabSschwarze
242fa70b73eSschwarze /* This will automatically close out any font scope. */
243520a575cSschwarze t->refcnt--;
244ed811782Sschwarze if (n->type == ROFFT_BLOCK &&
245ed811782Sschwarze (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
246ed811782Sschwarze t = h->tag;
2476f6388b4Sschwarze while (t->tag != TAG_DL && t->tag != TAG_UL)
248ed811782Sschwarze t = t->next;
249ed811782Sschwarze /*
250ed811782Sschwarze * Close the list if no further item of the same type
251ed811782Sschwarze * follows; otherwise, close the item only.
252ed811782Sschwarze */
2537ebbefbeSschwarze if (list_continues(n, roff_node_next(n)) == '\0') {
254ed811782Sschwarze print_tagq(h, t);
255ed811782Sschwarze t = NULL;
256ed811782Sschwarze }
257ed811782Sschwarze }
258ed811782Sschwarze if (t != NULL)
259de63e416Sschwarze print_stagq(h, t);
26047657bd5Sschwarze }
2614175bdabSschwarze
262a5e11edeSschwarze static void
man_root_pre(const struct roff_meta * man,struct html * h)263cc202ecaSschwarze man_root_pre(const struct roff_meta *man, struct html *h)
2644175bdabSschwarze {
2658f48bc46Sschwarze struct tag *t;
266a92c1cd8Sschwarze char *title;
2674175bdabSschwarze
2687ead8a4eSschwarze assert(man->title);
2697ead8a4eSschwarze assert(man->msec);
270a92c1cd8Sschwarze mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
2714175bdabSschwarze
2728f48bc46Sschwarze t = print_otag(h, TAG_DIV, "cr?", "head", "doc-pageheader",
273f0f927fcSschwarze "aria-label", "Manual header line");
274a66b65d0Sschwarze
2758f48bc46Sschwarze print_otag(h, TAG_SPAN, "c", "head-ltitle");
2764175bdabSschwarze print_text(h, title);
2778f48bc46Sschwarze print_stagq(h, t);
2784175bdabSschwarze
2798f48bc46Sschwarze print_otag(h, TAG_SPAN, "c", "head-vol");
28018c80517Sschwarze if (man->vol != NULL)
2810b2f1307Sschwarze print_text(h, man->vol);
2828f48bc46Sschwarze print_stagq(h, t);
2834175bdabSschwarze
2848f48bc46Sschwarze print_otag(h, TAG_SPAN, "c", "head-rtitle");
2854175bdabSschwarze print_text(h, title);
2864175bdabSschwarze print_tagq(h, t);
287a92c1cd8Sschwarze free(title);
2884175bdabSschwarze }
2894175bdabSschwarze
2904175bdabSschwarze static void
man_root_post(const struct roff_meta * man,struct html * h)291cc202ecaSschwarze man_root_post(const struct roff_meta *man, struct html *h)
2924175bdabSschwarze {
2938f48bc46Sschwarze struct tag *t;
2944175bdabSschwarze
2958f48bc46Sschwarze t = print_otag(h, TAG_DIV, "cr?", "foot", "doc-pagefooter",
296f0f927fcSschwarze "aria-label", "Manual footer line");
297a66b65d0Sschwarze
2988f48bc46Sschwarze print_otag(h, TAG_SPAN, "c", "foot-left");
2998f48bc46Sschwarze print_stagq(h, t);
3008f48bc46Sschwarze
3018f48bc46Sschwarze print_otag(h, TAG_SPAN, "c", "foot-date");
3027ead8a4eSschwarze print_text(h, man->date);
3038f48bc46Sschwarze print_stagq(h, t);
3044175bdabSschwarze
3058f48bc46Sschwarze print_otag(h, TAG_SPAN, "c", "foot-os");
30618c80517Sschwarze if (man->os != NULL)
3072a238f45Sschwarze print_text(h, man->os);
3084175bdabSschwarze print_tagq(h, t);
3094175bdabSschwarze }
3104175bdabSschwarze
3114175bdabSschwarze static int
man_SH_pre(MAN_ARGS)3124175bdabSschwarze man_SH_pre(MAN_ARGS)
3134175bdabSschwarze {
3141a2b7b3cSschwarze const char *class;
3151a2b7b3cSschwarze enum htmltag tag;
3166ef173c4Sschwarze
3171a2b7b3cSschwarze if (n->tok == MAN_SH) {
31800b92a3fSschwarze tag = TAG_H2;
3191a2b7b3cSschwarze class = "Sh";
3201a2b7b3cSschwarze } else {
32100b92a3fSschwarze tag = TAG_H3;
3221a2b7b3cSschwarze class = "Ss";
3231a2b7b3cSschwarze }
3247f442bfeSschwarze switch (n->type) {
3257f442bfeSschwarze case ROFFT_BLOCK:
3267f442bfeSschwarze html_close_paragraph(h);
3271a2b7b3cSschwarze print_otag(h, TAG_SECTION, "c", class);
3287f442bfeSschwarze break;
3297f442bfeSschwarze case ROFFT_HEAD:
3300ac7e6ecSschwarze print_otag_id(h, tag, class, n);
3317f442bfeSschwarze break;
3327f442bfeSschwarze case ROFFT_BODY:
3337f442bfeSschwarze break;
3347f442bfeSschwarze default:
3357f442bfeSschwarze abort();
3366ef173c4Sschwarze }
337526e306bSschwarze return 1;
3384175bdabSschwarze }
3394175bdabSschwarze
3404175bdabSschwarze static int
man_alt_pre(MAN_ARGS)3414175bdabSschwarze man_alt_pre(MAN_ARGS)
3424175bdabSschwarze {
3433a0d07afSschwarze const struct roff_node *nn;
34418c80517Sschwarze struct tag *t;
3450692510cSschwarze int i;
3463a7b861cSschwarze enum htmltag fp;
3474175bdabSschwarze
34818c80517Sschwarze for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
3494175bdabSschwarze switch (n->tok) {
35049aff9f8Sschwarze case MAN_BI:
3513a7b861cSschwarze fp = i % 2 ? TAG_I : TAG_B;
3524175bdabSschwarze break;
35349aff9f8Sschwarze case MAN_IB:
3543a7b861cSschwarze fp = i % 2 ? TAG_B : TAG_I;
3554175bdabSschwarze break;
35649aff9f8Sschwarze case MAN_RI:
3573a7b861cSschwarze fp = i % 2 ? TAG_I : TAG_MAX;
3584175bdabSschwarze break;
35949aff9f8Sschwarze case MAN_IR:
3603a7b861cSschwarze fp = i % 2 ? TAG_MAX : TAG_I;
3614175bdabSschwarze break;
36249aff9f8Sschwarze case MAN_BR:
3633a7b861cSschwarze fp = i % 2 ? TAG_MAX : TAG_B;
3644175bdabSschwarze break;
36549aff9f8Sschwarze case MAN_RB:
3663a7b861cSschwarze fp = i % 2 ? TAG_B : TAG_MAX;
3674175bdabSschwarze break;
3684175bdabSschwarze default:
3694175bdabSschwarze abort();
3704175bdabSschwarze }
3714175bdabSschwarze
3724175bdabSschwarze if (i)
3734175bdabSschwarze h->flags |= HTML_NOSPACE;
3744175bdabSschwarze
3750692510cSschwarze if (fp != TAG_MAX)
376229cc7fdSschwarze t = print_otag(h, fp, "");
3773a7b861cSschwarze
3780692510cSschwarze print_text(h, nn->string);
3793a7b861cSschwarze
3800692510cSschwarze if (fp != TAG_MAX)
3814175bdabSschwarze print_tagq(h, t);
3824175bdabSschwarze }
383526e306bSschwarze return 0;
3844175bdabSschwarze }
3854175bdabSschwarze
3864175bdabSschwarze static int
man_SM_pre(MAN_ARGS)3874175bdabSschwarze man_SM_pre(MAN_ARGS)
3884175bdabSschwarze {
389229cc7fdSschwarze print_otag(h, TAG_SMALL, "");
39018c80517Sschwarze if (n->tok == MAN_SB)
391229cc7fdSschwarze print_otag(h, TAG_B, "");
392526e306bSschwarze return 1;
3934175bdabSschwarze }
3944175bdabSschwarze
3954175bdabSschwarze static int
man_PP_pre(MAN_ARGS)3964175bdabSschwarze man_PP_pre(MAN_ARGS)
3974175bdabSschwarze {
3987f442bfeSschwarze switch (n->type) {
3997f442bfeSschwarze case ROFFT_BLOCK:
4007f442bfeSschwarze html_close_paragraph(h);
4017f442bfeSschwarze break;
4027f442bfeSschwarze case ROFFT_HEAD:
403526e306bSschwarze return 0;
4047f442bfeSschwarze case ROFFT_BODY:
4057f442bfeSschwarze if (n->child != NULL &&
4067f442bfeSschwarze (n->child->flags & NODE_NOFILL) == 0)
4077f442bfeSschwarze print_otag(h, TAG_P, "c",
4083f3c303aSschwarze n->tok == MAN_HP ? "Pp HP" : "Pp");
4097f442bfeSschwarze break;
4107f442bfeSschwarze default:
4117f442bfeSschwarze abort();
4127f442bfeSschwarze }
413526e306bSschwarze return 1;
4144175bdabSschwarze }
4154175bdabSschwarze
4166f6388b4Sschwarze static char
list_continues(const struct roff_node * n1,const struct roff_node * n2)4176f6388b4Sschwarze list_continues(const struct roff_node *n1, const struct roff_node *n2)
4186f6388b4Sschwarze {
4196f6388b4Sschwarze const char *s1, *s2;
4206f6388b4Sschwarze char c1, c2;
4216f6388b4Sschwarze
4226f6388b4Sschwarze if (n1 == NULL || n1->type != ROFFT_BLOCK ||
4236f6388b4Sschwarze n2 == NULL || n2->type != ROFFT_BLOCK)
4246f6388b4Sschwarze return '\0';
4256f6388b4Sschwarze if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
4266f6388b4Sschwarze (n2->tok == MAN_TP || n2->tok == MAN_TQ))
4276f6388b4Sschwarze return ' ';
4286f6388b4Sschwarze if (n1->tok != MAN_IP || n2->tok != MAN_IP)
4296f6388b4Sschwarze return '\0';
4306f6388b4Sschwarze n1 = n1->head->child;
4316f6388b4Sschwarze n2 = n2->head->child;
4326f6388b4Sschwarze s1 = n1 == NULL ? "" : n1->string;
4336f6388b4Sschwarze s2 = n2 == NULL ? "" : n2->string;
4346f6388b4Sschwarze c1 = strcmp(s1, "*") == 0 ? '*' :
4356f6388b4Sschwarze strcmp(s1, "\\-") == 0 ? '-' :
4360f7f2ebbSschwarze strcmp(s1, "\\(bu") == 0 ? 'b' :
4370f7f2ebbSschwarze strcmp(s1, "\\[bu]") == 0 ? 'b' : ' ';
4386f6388b4Sschwarze c2 = strcmp(s2, "*") == 0 ? '*' :
4396f6388b4Sschwarze strcmp(s2, "\\-") == 0 ? '-' :
4400f7f2ebbSschwarze strcmp(s2, "\\(bu") == 0 ? 'b' :
4410f7f2ebbSschwarze strcmp(s2, "\\[bu]") == 0 ? 'b' : ' ';
4426f6388b4Sschwarze return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
4436f6388b4Sschwarze }
4446f6388b4Sschwarze
4454175bdabSschwarze static int
man_IP_pre(MAN_ARGS)4464175bdabSschwarze man_IP_pre(MAN_ARGS)
4474175bdabSschwarze {
4487ebbefbeSschwarze struct roff_node *nn;
4496f6388b4Sschwarze const char *list_class;
4506f6388b4Sschwarze enum htmltag list_elem, body_elem;
4516f6388b4Sschwarze char list_type;
4526f6388b4Sschwarze
4536f6388b4Sschwarze nn = n->type == ROFFT_BLOCK ? n : n->parent;
4547ebbefbeSschwarze list_type = list_continues(roff_node_prev(nn), nn);
4557ebbefbeSschwarze if (list_type == '\0') {
4566f6388b4Sschwarze /* Start a new list. */
4577ebbefbeSschwarze list_type = list_continues(nn, roff_node_next(nn));
4587ebbefbeSschwarze if (list_type == '\0')
4596f6388b4Sschwarze list_type = ' ';
4606f6388b4Sschwarze switch (list_type) {
4616f6388b4Sschwarze case ' ':
4626f6388b4Sschwarze list_class = "Bl-tag";
4636f6388b4Sschwarze list_elem = TAG_DL;
4646f6388b4Sschwarze break;
4656f6388b4Sschwarze case '*':
4666f6388b4Sschwarze list_class = "Bl-bullet";
4676f6388b4Sschwarze list_elem = TAG_UL;
4686f6388b4Sschwarze break;
4696f6388b4Sschwarze case '-':
4706f6388b4Sschwarze list_class = "Bl-dash";
4716f6388b4Sschwarze list_elem = TAG_UL;
4726f6388b4Sschwarze break;
4736f6388b4Sschwarze default:
4746f6388b4Sschwarze abort();
4756f6388b4Sschwarze }
4766f6388b4Sschwarze } else {
4776f6388b4Sschwarze /* Continue a list that was started earlier. */
4786f6388b4Sschwarze list_class = NULL;
4796f6388b4Sschwarze list_elem = TAG_MAX;
4806f6388b4Sschwarze }
4816f6388b4Sschwarze body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
4824175bdabSschwarze
4837f442bfeSschwarze switch (n->type) {
4847f442bfeSschwarze case ROFFT_BLOCK:
4857f442bfeSschwarze html_close_paragraph(h);
4866f6388b4Sschwarze if (list_elem != TAG_MAX)
4876f6388b4Sschwarze print_otag(h, list_elem, "c", list_class);
488526e306bSschwarze return 1;
4897f442bfeSschwarze case ROFFT_HEAD:
4906f6388b4Sschwarze if (body_elem == TAG_LI)
4916f6388b4Sschwarze return 0;
4920ac7e6ecSschwarze print_otag_id(h, TAG_DT, NULL, n);
4937f442bfeSschwarze break;
4947f442bfeSschwarze case ROFFT_BODY:
4956f6388b4Sschwarze print_otag(h, body_elem, "");
4967f442bfeSschwarze return 1;
4977f442bfeSschwarze default:
4987f442bfeSschwarze abort();
4997f442bfeSschwarze }
500d991fc2cSschwarze switch(n->tok) {
501d991fc2cSschwarze case MAN_IP: /* Only print the first header element. */
502d991fc2cSschwarze if (n->child != NULL)
503de63e416Sschwarze print_man_node(man, n->child, h);
504d991fc2cSschwarze break;
505d991fc2cSschwarze case MAN_TP: /* Only print next-line header elements. */
506d991fc2cSschwarze case MAN_TQ:
507074125cbSschwarze nn = n->child;
508d991fc2cSschwarze while (nn != NULL && (NODE_LINE & nn->flags) == 0)
509074125cbSschwarze nn = nn->next;
510d991fc2cSschwarze while (nn != NULL) {
511de63e416Sschwarze print_man_node(man, nn, h);
512074125cbSschwarze nn = nn->next;
513074125cbSschwarze }
514d991fc2cSschwarze break;
515d991fc2cSschwarze default:
516d991fc2cSschwarze abort();
517074125cbSschwarze }
518526e306bSschwarze return 0;
5194175bdabSschwarze }
5204175bdabSschwarze
5214175bdabSschwarze static int
man_MR_pre(MAN_ARGS)522*f6697133Sschwarze man_MR_pre(MAN_ARGS)
523*f6697133Sschwarze {
524*f6697133Sschwarze struct tag *t;
525*f6697133Sschwarze const char *name, *section, *suffix;
526*f6697133Sschwarze char *label;
527*f6697133Sschwarze
528*f6697133Sschwarze html_setfont(h, ESCAPE_FONTROMAN);
529*f6697133Sschwarze name = section = suffix = label = NULL;
530*f6697133Sschwarze if (n->child != NULL) {
531*f6697133Sschwarze name = n->child->string;
532*f6697133Sschwarze if (n->child->next != NULL) {
533*f6697133Sschwarze section = n->child->next->string;
534*f6697133Sschwarze mandoc_asprintf(&label,
535*f6697133Sschwarze "%s, section %s", name, section);
536*f6697133Sschwarze if (n->child->next->next != NULL)
537*f6697133Sschwarze suffix = n->child->next->next->string;
538*f6697133Sschwarze }
539*f6697133Sschwarze }
540*f6697133Sschwarze
541*f6697133Sschwarze if (name != NULL && section != NULL && h->base_man1 != NULL)
542*f6697133Sschwarze t = print_otag(h, TAG_A, "chM?", "Xr",
543*f6697133Sschwarze name, section, "aria-label", label);
544*f6697133Sschwarze else
545*f6697133Sschwarze t = print_otag(h, TAG_A, "c?", "Xr", "aria-label", label);
546*f6697133Sschwarze
547*f6697133Sschwarze free(label);
548*f6697133Sschwarze if (name != NULL) {
549*f6697133Sschwarze print_text(h, name);
550*f6697133Sschwarze h->flags |= HTML_NOSPACE;
551*f6697133Sschwarze }
552*f6697133Sschwarze print_text(h, "(");
553*f6697133Sschwarze h->flags |= HTML_NOSPACE;
554*f6697133Sschwarze if (section != NULL) {
555*f6697133Sschwarze print_text(h, section);
556*f6697133Sschwarze h->flags |= HTML_NOSPACE;
557*f6697133Sschwarze }
558*f6697133Sschwarze print_text(h, ")");
559*f6697133Sschwarze print_tagq(h, t);
560*f6697133Sschwarze if (suffix != NULL) {
561*f6697133Sschwarze h->flags |= HTML_NOSPACE;
562*f6697133Sschwarze print_text(h, suffix);
563*f6697133Sschwarze }
564*f6697133Sschwarze return 0;
565*f6697133Sschwarze }
566*f6697133Sschwarze
567*f6697133Sschwarze static int
man_OP_pre(MAN_ARGS)56866ae7cc0Sschwarze man_OP_pre(MAN_ARGS)
56966ae7cc0Sschwarze {
57066ae7cc0Sschwarze struct tag *tt;
57166ae7cc0Sschwarze
57266ae7cc0Sschwarze print_text(h, "[");
57366ae7cc0Sschwarze h->flags |= HTML_NOSPACE;
574774a248cSschwarze tt = print_otag(h, TAG_SPAN, "c", "Op");
57566ae7cc0Sschwarze
57618c80517Sschwarze if ((n = n->child) != NULL) {
577229cc7fdSschwarze print_otag(h, TAG_B, "");
57866ae7cc0Sschwarze print_text(h, n->string);
57966ae7cc0Sschwarze }
58066ae7cc0Sschwarze
58166ae7cc0Sschwarze print_stagq(h, tt);
58266ae7cc0Sschwarze
58318c80517Sschwarze if (n != NULL && n->next != NULL) {
584229cc7fdSschwarze print_otag(h, TAG_I, "");
58566ae7cc0Sschwarze print_text(h, n->next->string);
58666ae7cc0Sschwarze }
58766ae7cc0Sschwarze
58866ae7cc0Sschwarze print_stagq(h, tt);
58966ae7cc0Sschwarze h->flags |= HTML_NOSPACE;
59066ae7cc0Sschwarze print_text(h, "]");
591526e306bSschwarze return 0;
59266ae7cc0Sschwarze }
59366ae7cc0Sschwarze
59466ae7cc0Sschwarze static int
man_B_pre(MAN_ARGS)5954175bdabSschwarze man_B_pre(MAN_ARGS)
5964175bdabSschwarze {
597229cc7fdSschwarze print_otag(h, TAG_B, "");
598526e306bSschwarze return 1;
5994175bdabSschwarze }
6004175bdabSschwarze
6014175bdabSschwarze static int
man_I_pre(MAN_ARGS)6024175bdabSschwarze man_I_pre(MAN_ARGS)
6034175bdabSschwarze {
604229cc7fdSschwarze print_otag(h, TAG_I, "");
605526e306bSschwarze return 1;
6064175bdabSschwarze }
6074175bdabSschwarze
6084175bdabSschwarze static int
man_in_pre(MAN_ARGS)609ddce0b0cSschwarze man_in_pre(MAN_ARGS)
610ddce0b0cSschwarze {
611229cc7fdSschwarze print_otag(h, TAG_BR, "");
612526e306bSschwarze return 0;
613ddce0b0cSschwarze }
614ddce0b0cSschwarze
615ddce0b0cSschwarze static int
man_ign_pre(MAN_ARGS)6164175bdabSschwarze man_ign_pre(MAN_ARGS)
6174175bdabSschwarze {
618526e306bSschwarze return 0;
6194175bdabSschwarze }
6204175bdabSschwarze
6214175bdabSschwarze static int
man_RS_pre(MAN_ARGS)6224175bdabSschwarze man_RS_pre(MAN_ARGS)
6234175bdabSschwarze {
6247f442bfeSschwarze switch (n->type) {
6257f442bfeSschwarze case ROFFT_BLOCK:
6267f442bfeSschwarze html_close_paragraph(h);
6277f442bfeSschwarze break;
6287f442bfeSschwarze case ROFFT_HEAD:
629526e306bSschwarze return 0;
6307f442bfeSschwarze case ROFFT_BODY:
631b9015fd6Sschwarze print_otag(h, TAG_DIV, "c", "Bd-indent");
6327f442bfeSschwarze break;
6337f442bfeSschwarze default:
6347f442bfeSschwarze abort();
6357f442bfeSschwarze }
636526e306bSschwarze return 1;
6374175bdabSschwarze }
6383aeff926Sschwarze
6393aeff926Sschwarze static int
man_SY_pre(MAN_ARGS)6405e5a9c61Sschwarze man_SY_pre(MAN_ARGS)
6415e5a9c61Sschwarze {
6425e5a9c61Sschwarze switch (n->type) {
6435e5a9c61Sschwarze case ROFFT_BLOCK:
6447f442bfeSschwarze html_close_paragraph(h);
6455e5a9c61Sschwarze print_otag(h, TAG_TABLE, "c", "Nm");
6465e5a9c61Sschwarze print_otag(h, TAG_TR, "");
6475e5a9c61Sschwarze break;
6485e5a9c61Sschwarze case ROFFT_HEAD:
6495e5a9c61Sschwarze print_otag(h, TAG_TD, "");
6500ce603abSschwarze print_otag(h, TAG_CODE, "c", "Nm");
6515e5a9c61Sschwarze break;
6525e5a9c61Sschwarze case ROFFT_BODY:
6535e5a9c61Sschwarze print_otag(h, TAG_TD, "");
6545e5a9c61Sschwarze break;
6555e5a9c61Sschwarze default:
6565e5a9c61Sschwarze abort();
6575e5a9c61Sschwarze }
6585e5a9c61Sschwarze return 1;
6595e5a9c61Sschwarze }
6605e5a9c61Sschwarze
6615e5a9c61Sschwarze static int
man_UR_pre(MAN_ARGS)6623aeff926Sschwarze man_UR_pre(MAN_ARGS)
6633aeff926Sschwarze {
664df9a9479Sbentley char *cp;
66518c80517Sschwarze
6663aeff926Sschwarze n = n->child;
667d1982c71Sschwarze assert(n->type == ROFFT_HEAD);
66830e5ee06Sschwarze if (n->child != NULL) {
669d1982c71Sschwarze assert(n->child->type == ROFFT_TEXT);
670df9a9479Sbentley if (n->tok == MAN_MT) {
671df9a9479Sbentley mandoc_asprintf(&cp, "mailto:%s", n->child->string);
6720ce603abSschwarze print_otag(h, TAG_A, "ch", "Mt", cp);
673df9a9479Sbentley free(cp);
674df9a9479Sbentley } else
6750ce603abSschwarze print_otag(h, TAG_A, "ch", "Lk", n->child->string);
6763aeff926Sschwarze }
6773aeff926Sschwarze
678d1982c71Sschwarze assert(n->next->type == ROFFT_BODY);
67930e5ee06Sschwarze if (n->next->child != NULL)
6803aeff926Sschwarze n = n->next;
6813aeff926Sschwarze
682de63e416Sschwarze print_man_nodelist(man, n->child, h);
683526e306bSschwarze return 0;
6843aeff926Sschwarze }
685