1*544c191cSchristos /* Id: man_html.c,v 1.173 2019/03/02 16:30:53 schwarze Exp */
24154958bSjoerg /*
3fec65c98Schristos * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
4*544c191cSchristos * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org>
54154958bSjoerg *
64154958bSjoerg * Permission to use, copy, modify, and distribute this software for any
74154958bSjoerg * purpose with or without fee is hereby granted, provided that the above
84154958bSjoerg * copyright notice and this permission notice appear in all copies.
94154958bSjoerg *
109ff1f2acSchristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
114154958bSjoerg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
129ff1f2acSchristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
134154958bSjoerg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
144154958bSjoerg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
154154958bSjoerg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
164154958bSjoerg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
174154958bSjoerg */
18d5e63c8dSjoerg #include "config.h"
19d5e63c8dSjoerg
204154958bSjoerg #include <sys/types.h>
214154958bSjoerg
224154958bSjoerg #include <assert.h>
234154958bSjoerg #include <ctype.h>
244154958bSjoerg #include <stdio.h>
254154958bSjoerg #include <stdlib.h>
264154958bSjoerg #include <string.h>
274154958bSjoerg
28fec65c98Schristos #include "mandoc_aux.h"
29c9bcef03Schristos #include "mandoc.h"
309ff1f2acSchristos #include "roff.h"
31fec65c98Schristos #include "man.h"
324154958bSjoerg #include "out.h"
334154958bSjoerg #include "html.h"
344154958bSjoerg #include "main.h"
354154958bSjoerg
369ff1f2acSchristos #define MAN_ARGS const struct roff_meta *man, \
379ff1f2acSchristos const struct roff_node *n, \
384154958bSjoerg struct html *h
394154958bSjoerg
40*544c191cSchristos struct man_html_act {
414154958bSjoerg int (*pre)(MAN_ARGS);
424154958bSjoerg int (*post)(MAN_ARGS);
434154958bSjoerg };
444154958bSjoerg
45c9bcef03Schristos static void print_man_head(const struct roff_meta *,
46c9bcef03Schristos struct html *);
474154958bSjoerg static void print_man_nodelist(MAN_ARGS);
484154958bSjoerg static void print_man_node(MAN_ARGS);
49*544c191cSchristos static char list_continues(const struct roff_node *,
50*544c191cSchristos const struct roff_node *);
51cf816816Sjoerg static int man_B_pre(MAN_ARGS);
52cf816816Sjoerg static int man_IP_pre(MAN_ARGS);
53cf816816Sjoerg static int man_I_pre(MAN_ARGS);
54cf816816Sjoerg static int man_OP_pre(MAN_ARGS);
55cf816816Sjoerg static int man_PP_pre(MAN_ARGS);
56cf816816Sjoerg static int man_RS_pre(MAN_ARGS);
57cf816816Sjoerg static int man_SH_pre(MAN_ARGS);
58cf816816Sjoerg static int man_SM_pre(MAN_ARGS);
59*544c191cSchristos static int man_SY_pre(MAN_ARGS);
6070f041f9Sjoerg static int man_UR_pre(MAN_ARGS);
61*544c191cSchristos static int man_abort_pre(MAN_ARGS);
624154958bSjoerg static int man_alt_pre(MAN_ARGS);
634154958bSjoerg static int man_ign_pre(MAN_ARGS);
647da9b934Sjoerg static int man_in_pre(MAN_ARGS);
65c9bcef03Schristos static void man_root_post(const struct roff_meta *,
66c9bcef03Schristos struct html *);
67c9bcef03Schristos static void man_root_pre(const struct roff_meta *,
68c9bcef03Schristos struct html *);
694154958bSjoerg
70*544c191cSchristos static const struct man_html_act man_html_acts[MAN_MAX - MAN_TH] = {
714154958bSjoerg { NULL, NULL }, /* TH */
724154958bSjoerg { man_SH_pre, NULL }, /* SH */
73*544c191cSchristos { man_SH_pre, NULL }, /* SS */
744154958bSjoerg { man_IP_pre, NULL }, /* TP */
75*544c191cSchristos { man_IP_pre, NULL }, /* TQ */
76*544c191cSchristos { man_abort_pre, NULL }, /* LP */
774154958bSjoerg { man_PP_pre, NULL }, /* PP */
78*544c191cSchristos { man_abort_pre, NULL }, /* P */
794154958bSjoerg { man_IP_pre, NULL }, /* IP */
80*544c191cSchristos { man_PP_pre, NULL }, /* HP */
814154958bSjoerg { man_SM_pre, NULL }, /* SM */
82c0d9444aSjoerg { man_SM_pre, NULL }, /* SB */
834154958bSjoerg { man_alt_pre, NULL }, /* BI */
844154958bSjoerg { man_alt_pre, NULL }, /* IB */
854154958bSjoerg { man_alt_pre, NULL }, /* BR */
864154958bSjoerg { man_alt_pre, NULL }, /* RB */
874154958bSjoerg { NULL, NULL }, /* R */
884154958bSjoerg { man_B_pre, NULL }, /* B */
894154958bSjoerg { man_I_pre, NULL }, /* I */
904154958bSjoerg { man_alt_pre, NULL }, /* IR */
914154958bSjoerg { man_alt_pre, NULL }, /* RI */
924154958bSjoerg { NULL, NULL }, /* RE */
934154958bSjoerg { man_RS_pre, NULL }, /* RS */
944154958bSjoerg { man_ign_pre, NULL }, /* DT */
954154958bSjoerg { man_ign_pre, NULL }, /* UC */
9622af4063Sjoerg { man_ign_pre, NULL }, /* PD */
970a84adc5Sjoerg { man_ign_pre, NULL }, /* AT */
987da9b934Sjoerg { man_in_pre, NULL }, /* in */
99*544c191cSchristos { man_SY_pre, NULL }, /* SY */
100*544c191cSchristos { NULL, NULL }, /* YS */
101cf816816Sjoerg { man_OP_pre, NULL }, /* OP */
1029508192eSchristos { NULL, NULL }, /* EX */
1039508192eSchristos { NULL, NULL }, /* EE */
10470f041f9Sjoerg { man_UR_pre, NULL }, /* UR */
10570f041f9Sjoerg { NULL, NULL }, /* UE */
106c9bcef03Schristos { man_UR_pre, NULL }, /* MT */
107c9bcef03Schristos { NULL, NULL }, /* ME */
1084154958bSjoerg };
1094154958bSjoerg
110fec65c98Schristos
1114154958bSjoerg void
html_man(void * arg,const struct roff_meta * man)112*544c191cSchristos html_man(void *arg, const struct roff_meta *man)
1134154958bSjoerg {
1149ff1f2acSchristos struct html *h;
115c9bcef03Schristos struct roff_node *n;
1169508192eSchristos struct tag *t;
1174154958bSjoerg
1189ff1f2acSchristos h = (struct html *)arg;
119c9bcef03Schristos n = man->first->child;
120c5f73b34Sjoerg
1219508192eSchristos if ((h->oflags & HTML_FRAGMENT) == 0) {
122c5f73b34Sjoerg print_gen_decls(h);
1239508192eSchristos print_otag(h, TAG_HTML, "");
124*544c191cSchristos if (n != NULL && n->type == ROFFT_COMMENT)
125c9bcef03Schristos print_gen_comment(h, n);
1269508192eSchristos t = print_otag(h, TAG_HEAD, "");
127*544c191cSchristos print_man_head(man, h);
1284154958bSjoerg print_tagq(h, t);
1299508192eSchristos print_otag(h, TAG_BODY, "");
1309508192eSchristos }
1319508192eSchristos
132*544c191cSchristos man_root_pre(man, h);
1339508192eSchristos t = print_otag(h, TAG_DIV, "c", "manual-text");
134*544c191cSchristos print_man_nodelist(man, n, h);
1359508192eSchristos print_tagq(h, t);
136*544c191cSchristos man_root_post(man, h);
1379508192eSchristos print_tagq(h, NULL);
1384154958bSjoerg }
1394154958bSjoerg
1404154958bSjoerg static void
print_man_head(const struct roff_meta * man,struct html * h)141c9bcef03Schristos print_man_head(const struct roff_meta *man, struct html *h)
1424154958bSjoerg {
1439508192eSchristos char *cp;
1444154958bSjoerg
1454154958bSjoerg print_gen_head(h);
1469508192eSchristos mandoc_asprintf(&cp, "%s(%s)", man->title, man->msec);
1479508192eSchristos print_otag(h, TAG_TITLE, "");
1489508192eSchristos print_text(h, cp);
1499508192eSchristos free(cp);
1504154958bSjoerg }
1514154958bSjoerg
1524154958bSjoerg static void
print_man_nodelist(MAN_ARGS)1534154958bSjoerg print_man_nodelist(MAN_ARGS)
1544154958bSjoerg {
155fec65c98Schristos while (n != NULL) {
1569508192eSchristos print_man_node(man, n, h);
157fec65c98Schristos n = n->next;
1584154958bSjoerg }
159fec65c98Schristos }
1604154958bSjoerg
1614154958bSjoerg static void
print_man_node(MAN_ARGS)1624154958bSjoerg print_man_node(MAN_ARGS)
1634154958bSjoerg {
1649508192eSchristos struct tag *t;
1659508192eSchristos int child;
1669508192eSchristos
167*544c191cSchristos if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
1689508192eSchristos return;
1699508192eSchristos
170*544c191cSchristos html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi);
1719508192eSchristos
1729508192eSchristos child = 1;
1739508192eSchristos switch (n->type) {
1749508192eSchristos case ROFFT_TEXT:
175*544c191cSchristos if (*n->string == '\0') {
176*544c191cSchristos print_endline(h);
177*544c191cSchristos return;
178*544c191cSchristos }
179*544c191cSchristos if (*n->string == ' ' && n->flags & NODE_LINE &&
180*544c191cSchristos (h->flags & HTML_NONEWLINE) == 0)
181*544c191cSchristos print_endline(h);
182*544c191cSchristos else if (n->flags & NODE_DELIMC)
183*544c191cSchristos h->flags |= HTML_NOSPACE;
1849508192eSchristos t = h->tag;
185*544c191cSchristos t->refcnt++;
1864154958bSjoerg print_text(h, n->string);
1879508192eSchristos break;
1889ff1f2acSchristos case ROFFT_EQN:
1899508192eSchristos t = h->tag;
190*544c191cSchristos t->refcnt++;
191c5f73b34Sjoerg print_eqn(h, n->eqn);
192c0d9444aSjoerg break;
1939ff1f2acSchristos case ROFFT_TBL:
19448741257Sjoerg /*
19548741257Sjoerg * This will take care of initialising all of the table
19648741257Sjoerg * state data for the first table, then tearing it down
19748741257Sjoerg * for the last one.
19848741257Sjoerg */
19948741257Sjoerg print_tbl(h, n->span);
20048741257Sjoerg return;
2014154958bSjoerg default:
2027d71a621Sjoerg /*
2037d71a621Sjoerg * Close out scope of font prior to opening a macro
20448741257Sjoerg * scope.
2057d71a621Sjoerg */
206c0d9444aSjoerg if (HTMLFONT_NONE != h->metac) {
207c0d9444aSjoerg h->metal = h->metac;
208c0d9444aSjoerg h->metac = HTMLFONT_NONE;
2097d71a621Sjoerg }
21048741257Sjoerg
21148741257Sjoerg /*
21248741257Sjoerg * Close out the current table, if it's open, and unset
21348741257Sjoerg * the "meta" table state. This will be reopened on the
21448741257Sjoerg * next table element.
21548741257Sjoerg */
216*544c191cSchristos if (h->tblt != NULL)
21748741257Sjoerg print_tblclose(h);
2189508192eSchristos t = h->tag;
219*544c191cSchristos t->refcnt++;
220c9bcef03Schristos if (n->tok < ROFF_MAX) {
221c9bcef03Schristos roff_html_pre(h, n);
222*544c191cSchristos t->refcnt--;
223*544c191cSchristos print_stagq(h, t);
224*544c191cSchristos return;
225c9bcef03Schristos }
226c9bcef03Schristos assert(n->tok >= MAN_TH && n->tok < MAN_MAX);
227*544c191cSchristos if (man_html_acts[n->tok - MAN_TH].pre != NULL)
228*544c191cSchristos child = (*man_html_acts[n->tok - MAN_TH].pre)(man,
229*544c191cSchristos n, h);
2304154958bSjoerg break;
2314154958bSjoerg }
2324154958bSjoerg
233*544c191cSchristos if (child && n->child != NULL)
2349508192eSchristos print_man_nodelist(man, n->child, h);
2354154958bSjoerg
2367d71a621Sjoerg /* This will automatically close out any font scope. */
237*544c191cSchristos t->refcnt--;
238*544c191cSchristos if (n->type == ROFFT_BLOCK &&
239*544c191cSchristos (n->tok == MAN_IP || n->tok == MAN_TP || n->tok == MAN_TQ)) {
240*544c191cSchristos t = h->tag;
241*544c191cSchristos while (t->tag != TAG_DL && t->tag != TAG_UL)
242*544c191cSchristos t = t->next;
243*544c191cSchristos /*
244*544c191cSchristos * Close the list if no further item of the same type
245*544c191cSchristos * follows; otherwise, close the item only.
246*544c191cSchristos */
247*544c191cSchristos if (list_continues(n, n->next) == '\0') {
248*544c191cSchristos print_tagq(h, t);
249*544c191cSchristos t = NULL;
250*544c191cSchristos }
251*544c191cSchristos }
252*544c191cSchristos if (t != NULL)
2534154958bSjoerg print_stagq(h, t);
2544154958bSjoerg
255*544c191cSchristos if (n->flags & NODE_NOFILL && n->tok != MAN_YS &&
256*544c191cSchristos (n->next != NULL && n->next->flags & NODE_LINE)) {
257*544c191cSchristos /* In .nf = <pre>, print even empty lines. */
258*544c191cSchristos h->col++;
2599508192eSchristos print_endline(h);
2604154958bSjoerg }
2614154958bSjoerg }
2624154958bSjoerg
26348741257Sjoerg static void
man_root_pre(const struct roff_meta * man,struct html * h)264c9bcef03Schristos man_root_pre(const struct roff_meta *man, struct html *h)
2654154958bSjoerg {
2664154958bSjoerg struct tag *t, *tt;
267fec65c98Schristos char *title;
2684154958bSjoerg
26970f041f9Sjoerg assert(man->title);
27070f041f9Sjoerg assert(man->msec);
271fec65c98Schristos mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
2724154958bSjoerg
2739508192eSchristos t = print_otag(h, TAG_TABLE, "c", "head");
2749508192eSchristos tt = print_otag(h, TAG_TR, "");
275c0d9444aSjoerg
2769508192eSchristos print_otag(h, TAG_TD, "c", "head-ltitle");
2774154958bSjoerg print_text(h, title);
2784154958bSjoerg print_stagq(h, tt);
2794154958bSjoerg
2809508192eSchristos print_otag(h, TAG_TD, "c", "head-vol");
281*544c191cSchristos if (man->vol != NULL)
282fec65c98Schristos print_text(h, man->vol);
2834154958bSjoerg print_stagq(h, tt);
2844154958bSjoerg
2859508192eSchristos print_otag(h, TAG_TD, "c", "head-rtitle");
2864154958bSjoerg print_text(h, title);
2874154958bSjoerg print_tagq(h, t);
288fec65c98Schristos free(title);
2894154958bSjoerg }
2904154958bSjoerg
2914154958bSjoerg static void
man_root_post(const struct roff_meta * man,struct html * h)292c9bcef03Schristos man_root_post(const struct roff_meta *man, struct html *h)
2934154958bSjoerg {
2944154958bSjoerg struct tag *t, *tt;
2954154958bSjoerg
2969508192eSchristos t = print_otag(h, TAG_TABLE, "c", "foot");
2979508192eSchristos tt = print_otag(h, TAG_TR, "");
298c0d9444aSjoerg
2999508192eSchristos print_otag(h, TAG_TD, "c", "foot-date");
30070f041f9Sjoerg print_text(h, man->date);
3014154958bSjoerg print_stagq(h, tt);
3024154958bSjoerg
3039508192eSchristos print_otag(h, TAG_TD, "c", "foot-os");
304*544c191cSchristos if (man->os != NULL)
3059ff1f2acSchristos print_text(h, man->os);
3064154958bSjoerg print_tagq(h, t);
3074154958bSjoerg }
3084154958bSjoerg
3094154958bSjoerg static int
man_SH_pre(MAN_ARGS)3104154958bSjoerg man_SH_pre(MAN_ARGS)
3114154958bSjoerg {
312*544c191cSchristos const char *class;
313c9bcef03Schristos char *id;
314*544c191cSchristos enum htmltag tag;
315c9bcef03Schristos
316*544c191cSchristos if (n->tok == MAN_SH) {
317*544c191cSchristos tag = TAG_H1;
318*544c191cSchristos class = "Sh";
319*544c191cSchristos } else {
320*544c191cSchristos tag = TAG_H2;
321*544c191cSchristos class = "Ss";
322*544c191cSchristos }
323*544c191cSchristos switch (n->type) {
324*544c191cSchristos case ROFFT_BLOCK:
325*544c191cSchristos html_close_paragraph(h);
326*544c191cSchristos print_otag(h, TAG_SECTION, "c", class);
327*544c191cSchristos break;
328*544c191cSchristos case ROFFT_HEAD:
329c9bcef03Schristos id = html_make_id(n, 1);
330*544c191cSchristos print_otag(h, tag, "ci", class, id);
331c9bcef03Schristos if (id != NULL)
332c9bcef03Schristos print_otag(h, TAG_A, "chR", "permalink", id);
333*544c191cSchristos break;
334*544c191cSchristos case ROFFT_BODY:
335*544c191cSchristos break;
336*544c191cSchristos default:
337*544c191cSchristos abort();
338c9bcef03Schristos }
3399ff1f2acSchristos return 1;
3404154958bSjoerg }
3414154958bSjoerg
3424154958bSjoerg static int
man_alt_pre(MAN_ARGS)3434154958bSjoerg man_alt_pre(MAN_ARGS)
3444154958bSjoerg {
3459ff1f2acSchristos const struct roff_node *nn;
346*544c191cSchristos struct tag *t;
3479508192eSchristos int i;
348c0d9444aSjoerg enum htmltag fp;
3494154958bSjoerg
350*544c191cSchristos for (i = 0, nn = n->child; nn != NULL; nn = nn->next, i++) {
3514154958bSjoerg switch (n->tok) {
352fec65c98Schristos case MAN_BI:
353c0d9444aSjoerg fp = i % 2 ? TAG_I : TAG_B;
3544154958bSjoerg break;
355fec65c98Schristos case MAN_IB:
356c0d9444aSjoerg fp = i % 2 ? TAG_B : TAG_I;
3574154958bSjoerg break;
358fec65c98Schristos case MAN_RI:
359c0d9444aSjoerg fp = i % 2 ? TAG_I : TAG_MAX;
3604154958bSjoerg break;
361fec65c98Schristos case MAN_IR:
362c0d9444aSjoerg fp = i % 2 ? TAG_MAX : TAG_I;
3634154958bSjoerg break;
364fec65c98Schristos case MAN_BR:
365c0d9444aSjoerg fp = i % 2 ? TAG_MAX : TAG_B;
3664154958bSjoerg break;
367fec65c98Schristos case MAN_RB:
368c0d9444aSjoerg fp = i % 2 ? TAG_B : TAG_MAX;
3694154958bSjoerg break;
3704154958bSjoerg default:
3714154958bSjoerg abort();
3724154958bSjoerg }
3734154958bSjoerg
3744154958bSjoerg if (i)
3754154958bSjoerg h->flags |= HTML_NOSPACE;
3764154958bSjoerg
3779508192eSchristos if (fp != TAG_MAX)
3789508192eSchristos t = print_otag(h, fp, "");
379c0d9444aSjoerg
3809508192eSchristos print_text(h, nn->string);
381c0d9444aSjoerg
3829508192eSchristos if (fp != TAG_MAX)
3834154958bSjoerg print_tagq(h, t);
3844154958bSjoerg }
3859ff1f2acSchristos return 0;
3864154958bSjoerg }
3874154958bSjoerg
3884154958bSjoerg static int
man_SM_pre(MAN_ARGS)3894154958bSjoerg man_SM_pre(MAN_ARGS)
3904154958bSjoerg {
3919508192eSchristos print_otag(h, TAG_SMALL, "");
392*544c191cSchristos if (n->tok == MAN_SB)
3939508192eSchristos print_otag(h, TAG_B, "");
3949ff1f2acSchristos return 1;
3954154958bSjoerg }
3964154958bSjoerg
3974154958bSjoerg static int
man_PP_pre(MAN_ARGS)3984154958bSjoerg man_PP_pre(MAN_ARGS)
3994154958bSjoerg {
400*544c191cSchristos switch (n->type) {
401*544c191cSchristos case ROFFT_BLOCK:
402*544c191cSchristos html_close_paragraph(h);
403*544c191cSchristos break;
404*544c191cSchristos case ROFFT_HEAD:
4059ff1f2acSchristos return 0;
406*544c191cSchristos case ROFFT_BODY:
407*544c191cSchristos if (n->child != NULL &&
408*544c191cSchristos (n->child->flags & NODE_NOFILL) == 0)
409*544c191cSchristos print_otag(h, TAG_P, "c",
410*544c191cSchristos n->tok == MAN_PP ? "Pp" : "Pp HP");
411*544c191cSchristos break;
412*544c191cSchristos default:
413*544c191cSchristos abort();
414*544c191cSchristos }
4159ff1f2acSchristos return 1;
4164154958bSjoerg }
4174154958bSjoerg
418*544c191cSchristos static char
list_continues(const struct roff_node * n1,const struct roff_node * n2)419*544c191cSchristos list_continues(const struct roff_node *n1, const struct roff_node *n2)
420*544c191cSchristos {
421*544c191cSchristos const char *s1, *s2;
422*544c191cSchristos char c1, c2;
423*544c191cSchristos
424*544c191cSchristos if (n1 == NULL || n1->type != ROFFT_BLOCK ||
425*544c191cSchristos n2 == NULL || n2->type != ROFFT_BLOCK)
426*544c191cSchristos return '\0';
427*544c191cSchristos if ((n1->tok == MAN_TP || n1->tok == MAN_TQ) &&
428*544c191cSchristos (n2->tok == MAN_TP || n2->tok == MAN_TQ))
429*544c191cSchristos return ' ';
430*544c191cSchristos if (n1->tok != MAN_IP || n2->tok != MAN_IP)
431*544c191cSchristos return '\0';
432*544c191cSchristos n1 = n1->head->child;
433*544c191cSchristos n2 = n2->head->child;
434*544c191cSchristos s1 = n1 == NULL ? "" : n1->string;
435*544c191cSchristos s2 = n2 == NULL ? "" : n2->string;
436*544c191cSchristos c1 = strcmp(s1, "*") == 0 ? '*' :
437*544c191cSchristos strcmp(s1, "\\-") == 0 ? '-' :
438*544c191cSchristos strcmp(s1, "\\(bu") == 0 ? 'b' : ' ';
439*544c191cSchristos c2 = strcmp(s2, "*") == 0 ? '*' :
440*544c191cSchristos strcmp(s2, "\\-") == 0 ? '-' :
441*544c191cSchristos strcmp(s2, "\\(bu") == 0 ? 'b' : ' ';
442*544c191cSchristos return c1 != c2 ? '\0' : c1 == 'b' ? '*' : c1;
443*544c191cSchristos }
444*544c191cSchristos
4454154958bSjoerg static int
man_IP_pre(MAN_ARGS)4464154958bSjoerg man_IP_pre(MAN_ARGS)
4474154958bSjoerg {
4489ff1f2acSchristos const struct roff_node *nn;
449*544c191cSchristos const char *list_class;
450*544c191cSchristos enum htmltag list_elem, body_elem;
451*544c191cSchristos char list_type;
4524154958bSjoerg
453*544c191cSchristos nn = n->type == ROFFT_BLOCK ? n : n->parent;
454*544c191cSchristos if ((list_type = list_continues(nn->prev, nn)) == '\0') {
455*544c191cSchristos /* Start a new list. */
456*544c191cSchristos if ((list_type = list_continues(nn, nn->next)) == '\0')
457*544c191cSchristos list_type = ' ';
458*544c191cSchristos switch (list_type) {
459*544c191cSchristos case ' ':
460*544c191cSchristos list_class = "Bl-tag";
461*544c191cSchristos list_elem = TAG_DL;
462*544c191cSchristos break;
463*544c191cSchristos case '*':
464*544c191cSchristos list_class = "Bl-bullet";
465*544c191cSchristos list_elem = TAG_UL;
466*544c191cSchristos break;
467*544c191cSchristos case '-':
468*544c191cSchristos list_class = "Bl-dash";
469*544c191cSchristos list_elem = TAG_UL;
470*544c191cSchristos break;
471*544c191cSchristos default:
472*544c191cSchristos abort();
473*544c191cSchristos }
474*544c191cSchristos } else {
475*544c191cSchristos /* Continue a list that was started earlier. */
476*544c191cSchristos list_class = NULL;
477*544c191cSchristos list_elem = TAG_MAX;
478*544c191cSchristos }
479*544c191cSchristos body_elem = list_type == ' ' ? TAG_DD : TAG_LI;
480*544c191cSchristos
481*544c191cSchristos switch (n->type) {
482*544c191cSchristos case ROFFT_BLOCK:
483*544c191cSchristos html_close_paragraph(h);
484*544c191cSchristos if (list_elem != TAG_MAX)
485*544c191cSchristos print_otag(h, list_elem, "c", list_class);
4869ff1f2acSchristos return 1;
487*544c191cSchristos case ROFFT_HEAD:
488*544c191cSchristos if (body_elem == TAG_LI)
489*544c191cSchristos return 0;
490*544c191cSchristos print_otag(h, TAG_DT, "");
491*544c191cSchristos break;
492*544c191cSchristos case ROFFT_BODY:
493*544c191cSchristos print_otag(h, body_elem, "");
4949ff1f2acSchristos return 1;
495*544c191cSchristos default:
496*544c191cSchristos abort();
4974154958bSjoerg }
4984154958bSjoerg
499*544c191cSchristos switch(n->tok) {
500*544c191cSchristos case MAN_IP: /* Only print the first header element. */
501*544c191cSchristos if (n->child != NULL)
5029508192eSchristos print_man_node(man, n->child, h);
503*544c191cSchristos break;
504*544c191cSchristos case MAN_TP: /* Only print next-line header elements. */
505*544c191cSchristos case MAN_TQ:
506fec65c98Schristos nn = n->child;
507*544c191cSchristos while (nn != NULL && (NODE_LINE & nn->flags) == 0)
508fec65c98Schristos nn = nn->next;
509*544c191cSchristos while (nn != NULL) {
5109508192eSchristos print_man_node(man, nn, h);
511fec65c98Schristos nn = nn->next;
512fec65c98Schristos }
513*544c191cSchristos break;
514*544c191cSchristos default:
515*544c191cSchristos abort();
516fec65c98Schristos }
5179ff1f2acSchristos return 0;
5184154958bSjoerg }
5194154958bSjoerg
5204154958bSjoerg static int
man_OP_pre(MAN_ARGS)521cf816816Sjoerg man_OP_pre(MAN_ARGS)
522cf816816Sjoerg {
523cf816816Sjoerg struct tag *tt;
524cf816816Sjoerg
525cf816816Sjoerg print_text(h, "[");
526cf816816Sjoerg h->flags |= HTML_NOSPACE;
5279508192eSchristos tt = print_otag(h, TAG_SPAN, "c", "Op");
528cf816816Sjoerg
529*544c191cSchristos if ((n = n->child) != NULL) {
5309508192eSchristos print_otag(h, TAG_B, "");
531cf816816Sjoerg print_text(h, n->string);
532cf816816Sjoerg }
533cf816816Sjoerg
534cf816816Sjoerg print_stagq(h, tt);
535cf816816Sjoerg
536*544c191cSchristos if (n != NULL && n->next != NULL) {
5379508192eSchristos print_otag(h, TAG_I, "");
538cf816816Sjoerg print_text(h, n->next->string);
539cf816816Sjoerg }
540cf816816Sjoerg
541cf816816Sjoerg print_stagq(h, tt);
542cf816816Sjoerg h->flags |= HTML_NOSPACE;
543cf816816Sjoerg print_text(h, "]");
5449ff1f2acSchristos return 0;
545cf816816Sjoerg }
546cf816816Sjoerg
547cf816816Sjoerg static int
man_B_pre(MAN_ARGS)5484154958bSjoerg man_B_pre(MAN_ARGS)
5494154958bSjoerg {
5509508192eSchristos print_otag(h, TAG_B, "");
5519ff1f2acSchristos return 1;
5524154958bSjoerg }
5534154958bSjoerg
5544154958bSjoerg static int
man_I_pre(MAN_ARGS)5554154958bSjoerg man_I_pre(MAN_ARGS)
5564154958bSjoerg {
5579508192eSchristos print_otag(h, TAG_I, "");
5589ff1f2acSchristos return 1;
5594154958bSjoerg }
5604154958bSjoerg
5614154958bSjoerg static int
man_in_pre(MAN_ARGS)5627da9b934Sjoerg man_in_pre(MAN_ARGS)
5637da9b934Sjoerg {
5649508192eSchristos print_otag(h, TAG_BR, "");
5659ff1f2acSchristos return 0;
5667da9b934Sjoerg }
5677da9b934Sjoerg
5687da9b934Sjoerg static int
man_ign_pre(MAN_ARGS)5694154958bSjoerg man_ign_pre(MAN_ARGS)
5704154958bSjoerg {
5719ff1f2acSchristos return 0;
5724154958bSjoerg }
5734154958bSjoerg
5744154958bSjoerg static int
man_RS_pre(MAN_ARGS)5754154958bSjoerg man_RS_pre(MAN_ARGS)
5764154958bSjoerg {
577*544c191cSchristos switch (n->type) {
578*544c191cSchristos case ROFFT_BLOCK:
579*544c191cSchristos html_close_paragraph(h);
580*544c191cSchristos break;
581*544c191cSchristos case ROFFT_HEAD:
5829ff1f2acSchristos return 0;
583*544c191cSchristos case ROFFT_BODY:
584c9bcef03Schristos print_otag(h, TAG_DIV, "c", "Bd-indent");
585*544c191cSchristos break;
586*544c191cSchristos default:
587*544c191cSchristos abort();
588*544c191cSchristos }
589*544c191cSchristos return 1;
590*544c191cSchristos }
591*544c191cSchristos
592*544c191cSchristos static int
man_SY_pre(MAN_ARGS)593*544c191cSchristos man_SY_pre(MAN_ARGS)
594*544c191cSchristos {
595*544c191cSchristos switch (n->type) {
596*544c191cSchristos case ROFFT_BLOCK:
597*544c191cSchristos html_close_paragraph(h);
598*544c191cSchristos print_otag(h, TAG_TABLE, "c", "Nm");
599*544c191cSchristos print_otag(h, TAG_TR, "");
600*544c191cSchristos break;
601*544c191cSchristos case ROFFT_HEAD:
602*544c191cSchristos print_otag(h, TAG_TD, "");
603*544c191cSchristos print_otag(h, TAG_CODE, "c", "Nm");
604*544c191cSchristos break;
605*544c191cSchristos case ROFFT_BODY:
606*544c191cSchristos print_otag(h, TAG_TD, "");
607*544c191cSchristos break;
608*544c191cSchristos default:
609*544c191cSchristos abort();
610*544c191cSchristos }
6119ff1f2acSchristos return 1;
6124154958bSjoerg }
61370f041f9Sjoerg
61470f041f9Sjoerg static int
man_UR_pre(MAN_ARGS)61570f041f9Sjoerg man_UR_pre(MAN_ARGS)
61670f041f9Sjoerg {
617c9bcef03Schristos char *cp;
618*544c191cSchristos
61970f041f9Sjoerg n = n->child;
6209ff1f2acSchristos assert(n->type == ROFFT_HEAD);
6219ff1f2acSchristos if (n->child != NULL) {
6229ff1f2acSchristos assert(n->child->type == ROFFT_TEXT);
623c9bcef03Schristos if (n->tok == MAN_MT) {
624c9bcef03Schristos mandoc_asprintf(&cp, "mailto:%s", n->child->string);
625*544c191cSchristos print_otag(h, TAG_A, "ch", "Mt", cp);
626c9bcef03Schristos free(cp);
627c9bcef03Schristos } else
628*544c191cSchristos print_otag(h, TAG_A, "ch", "Lk", n->child->string);
62970f041f9Sjoerg }
63070f041f9Sjoerg
6319ff1f2acSchristos assert(n->next->type == ROFFT_BODY);
6329ff1f2acSchristos if (n->next->child != NULL)
63370f041f9Sjoerg n = n->next;
63470f041f9Sjoerg
6359508192eSchristos print_man_nodelist(man, n->child, h);
6369ff1f2acSchristos return 0;
63770f041f9Sjoerg }
638*544c191cSchristos
639*544c191cSchristos static int
man_abort_pre(MAN_ARGS)640*544c191cSchristos man_abort_pre(MAN_ARGS)
641*544c191cSchristos {
642*544c191cSchristos abort();
643*544c191cSchristos }
644