-/* $Id: mdoc_html.c,v 1.308 2018/06/25 14:06:07 schwarze Exp $ */
+/* $Id: mdoc_html.c,v 1.323 2019/01/07 07:26:29 schwarze Exp $ */
/*
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org>
#include "html.h"
#include "main.h"
-#define INDENT 5
-
#define MDOC_ARGS const struct roff_meta *meta, \
struct roff_node *n, \
struct html *h
#define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b))
#endif
-struct htmlmdoc {
+struct mdoc_html_act {
int (*pre)(MDOC_ARGS);
void (*post)(MDOC_ARGS);
};
static void mdoc__x_post(MDOC_ARGS);
static int mdoc__x_pre(MDOC_ARGS);
+static int mdoc_abort_pre(MDOC_ARGS);
static int mdoc_ad_pre(MDOC_ARGS);
static int mdoc_an_pre(MDOC_ARGS);
static int mdoc_ap_pre(MDOC_ARGS);
static int mdoc_xr_pre(MDOC_ARGS);
static int mdoc_xx_pre(MDOC_ARGS);
-static const struct htmlmdoc __mdocs[MDOC_MAX - MDOC_Dd] = {
+static const struct mdoc_html_act mdoc_html_acts[MDOC_MAX - MDOC_Dd] = {
{NULL, NULL}, /* Dd */
{NULL, NULL}, /* Dt */
{NULL, NULL}, /* Os */
{mdoc_nd_pre, NULL}, /* Nd */
{mdoc_nm_pre, NULL}, /* Nm */
{mdoc_quote_pre, mdoc_quote_post}, /* Op */
- {mdoc_ft_pre, NULL}, /* Ot */
+ {mdoc_abort_pre, NULL}, /* Ot */
{mdoc_pa_pre, NULL}, /* Pa */
{mdoc_ex_pre, NULL}, /* Rv */
{mdoc_st_pre, NULL}, /* St */
{mdoc_em_pre, NULL}, /* Fr */
{NULL, NULL}, /* Ud */
{mdoc_lb_pre, NULL}, /* Lb */
- {mdoc_pp_pre, NULL}, /* Lp */
+ {mdoc_abort_pre, NULL}, /* Lp */
{mdoc_lk_pre, NULL}, /* Lk */
{mdoc_mt_pre, NULL}, /* Mt */
{mdoc_quote_pre, mdoc_quote_post}, /* Brq */
{mdoc__x_pre, mdoc__x_post}, /* %U */
{NULL, NULL}, /* Ta */
};
-static const struct htmlmdoc *const mdocs = __mdocs - MDOC_Dd;
/*
case MDOC_Fo:
case MDOC_In:
case MDOC_Vt:
- print_paragraph(h);
break;
case MDOC_Ft:
- if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
- print_paragraph(h);
+ if (n->tok != MDOC_Fn && n->tok != MDOC_Fo)
break;
- }
/* FALLTHROUGH */
default:
print_otag(h, TAG_BR, "");
- break;
+ return;
}
+ html_close_paragraph(h);
+ print_otag(h, TAG_P, "c", "Pp");
}
void
-html_mdoc(void *arg, const struct roff_man *mdoc)
+html_mdoc(void *arg, const struct roff_meta *mdoc)
{
struct html *h;
struct roff_node *n;
if (n->type == ROFFT_COMMENT)
print_gen_comment(h, n);
t = print_otag(h, TAG_HEAD, "");
- print_mdoc_head(&mdoc->meta, h);
+ print_mdoc_head(mdoc, h);
print_tagq(h, t);
print_otag(h, TAG_BODY, "");
}
- mdoc_root_pre(&mdoc->meta, h);
+ mdoc_root_pre(mdoc, h);
t = print_otag(h, TAG_DIV, "c", "manual-text");
- print_mdoc_nodelist(&mdoc->meta, n, h);
+ print_mdoc_nodelist(mdoc, n, h);
print_tagq(h, t);
- mdoc_root_post(&mdoc->meta, h);
+ mdoc_root_post(mdoc, h);
print_tagq(h, NULL);
}
static void
print_mdoc_node(MDOC_ARGS)
{
- int child;
struct tag *t;
+ int child;
if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT)
return;
+ html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi);
+
child = 1;
t = h->tag;
- n->flags &= ~NODE_ENDED;
+ if (t->tag == TAG_P || t->tag == TAG_PRE)
+ t = t->next;
+ n->flags &= ~NODE_ENDED;
switch (n->type) {
case ROFFT_TEXT:
/* No tables in this mode... */
* (i.e., within a <PRE>) don't print the newline.
*/
if (*n->string == ' ' && n->flags & NODE_LINE &&
- (h->flags & (HTML_LITERAL | HTML_NONEWLINE)) == 0)
+ (h->flags & HTML_NONEWLINE) == 0 &&
+ (n->flags & NODE_NOFILL) == 0)
print_otag(h, TAG_BR, "");
if (NODE_DELIMC & n->flags)
h->flags |= HTML_NOSPACE;
print_text(h, n->string);
if (NODE_DELIMO & n->flags)
h->flags |= HTML_NOSPACE;
- return;
+ break;
case ROFFT_EQN:
print_eqn(h, n->eqn);
break;
assert(h->tblt == NULL);
if (n->tok < ROFF_MAX) {
roff_html_pre(h, n);
- child = 0;
- break;
+ print_stagq(h, t);
+ return;
}
assert(n->tok >= MDOC_Dd && n->tok < MDOC_MAX);
- if (mdocs[n->tok].pre != NULL &&
+ if (mdoc_html_acts[n->tok - MDOC_Dd].pre != NULL &&
(n->end == ENDBODY_NOT || n->child != NULL))
- child = (*mdocs[n->tok].pre)(meta, n, h);
+ child = (*mdoc_html_acts[n->tok - MDOC_Dd].pre)(meta,
+ n, h);
break;
}
h->flags |= HTML_PREKEEP;
}
- if (child && n->child)
+ if (child && n->child != NULL)
print_mdoc_nodelist(meta, n->child, h);
print_stagq(h, t);
switch (n->type) {
+ case ROFFT_TEXT:
case ROFFT_EQN:
break;
default:
- if (n->tok < ROFF_MAX ||
- mdocs[n->tok].post == NULL ||
+ if (mdoc_html_acts[n->tok - MDOC_Dd].post == NULL ||
n->flags & NODE_ENDED)
break;
- (*mdocs[n->tok].post)(meta, n, h);
+ (*mdoc_html_acts[n->tok - MDOC_Dd].post)(meta, n, h);
if (n->end != ENDBODY_NOT)
n->body->flags |= NODE_ENDED;
break;
}
+
+ if (n->flags & NODE_NOFILL &&
+ (n->next == NULL || n->next->flags & NODE_LINE)) {
+ h->col++;
+ print_endline(h);
+ }
}
static void
static int
mdoc_sh_pre(MDOC_ARGS)
{
- char *id;
+ struct roff_node *sn, *subn;
+ struct tag *t, *tsec, *tsub;
+ char *id;
+ int sc;
switch (n->type) {
+ case ROFFT_BLOCK:
+ html_close_paragraph(h);
+ if ((h->oflags & HTML_TOC) == 0 ||
+ h->flags & HTML_TOCDONE ||
+ n->sec <= SEC_SYNOPSIS)
+ break;
+ h->flags |= HTML_TOCDONE;
+ sc = 0;
+ for (sn = n->next; sn != NULL; sn = sn->next)
+ if (sn->sec == SEC_CUSTOM)
+ if (++sc == 2)
+ break;
+ if (sc < 2)
+ break;
+ t = print_otag(h, TAG_H1, "c", "Sh");
+ print_text(h, "TABLE OF CONTENTS");
+ print_tagq(h, t);
+ t = print_otag(h, TAG_UL, "c", "Bl-compact");
+ for (sn = n; sn != NULL; sn = sn->next) {
+ tsec = print_otag(h, TAG_LI, "");
+ id = html_make_id(sn->head, 0);
+ tsub = print_otag(h, TAG_A, "hR", id);
+ free(id);
+ print_mdoc_nodelist(meta, sn->head->child, h);
+ print_tagq(h, tsub);
+ tsub = NULL;
+ for (subn = sn->body->child; subn != NULL;
+ subn = subn->next) {
+ if (subn->tok != MDOC_Ss)
+ continue;
+ id = html_make_id(subn->head, 0);
+ if (id == NULL)
+ continue;
+ if (tsub == NULL)
+ print_otag(h, TAG_UL,
+ "c", "Bl-compact");
+ tsub = print_otag(h, TAG_LI, "");
+ print_otag(h, TAG_A, "hR", id);
+ free(id);
+ print_mdoc_nodelist(meta,
+ subn->head->child, h);
+ print_tagq(h, tsub);
+ }
+ print_tagq(h, tsec);
+ }
+ print_tagq(h, t);
+ break;
case ROFFT_HEAD:
id = html_make_id(n, 1);
print_otag(h, TAG_H1, "cTi", "Sh", id);
{
char *id;
- if (n->type != ROFFT_HEAD)
+ switch (n->type) {
+ case ROFFT_BLOCK:
+ html_close_paragraph(h);
+ return 1;
+ case ROFFT_HEAD:
+ break;
+ case ROFFT_BODY:
return 1;
+ default:
+ abort();
+ }
id = html_make_id(n, 1);
print_otag(h, TAG_H2, "cTi", "Ss", id);
static int
mdoc_nd_pre(MDOC_ARGS)
{
- if (n->type != ROFFT_BODY)
+ switch (n->type) {
+ case ROFFT_BLOCK:
+ html_close_paragraph(h);
return 1;
-
+ case ROFFT_HEAD:
+ return 0;
+ case ROFFT_BODY:
+ break;
+ default:
+ abort();
+ }
print_text(h, "\\(em");
/* Cannot use TAG_SPAN because it may contain blocks. */
print_otag(h, TAG_DIV, "cT", "Nd");
mdoc_nm_pre(MDOC_ARGS)
{
switch (n->type) {
+ case ROFFT_BLOCK:
+ break;
case ROFFT_HEAD:
print_otag(h, TAG_TD, "");
/* FALLTHROUGH */
print_otag(h, TAG_TD, "");
return 1;
default:
- break;
+ abort();
}
+ html_close_paragraph(h);
synopsis_pre(h, n);
print_otag(h, TAG_TABLE, "c", "Nm");
print_otag(h, TAG_TR, "");
if (NULL == n->child)
return 0;
- if (h->base_man)
+ if (h->base_man1)
print_otag(h, TAG_A, "cThM", "Xr",
n->child->string, n->child->next == NULL ?
NULL : n->child->next->string);
break;
case ROFFT_BODY:
if (n->child == NULL) {
- print_otag(h, TAG_DD, "ss?",
- "width", "auto");
+ print_otag(h, TAG_DD, "s", "width", "auto");
print_text(h, "\\ ");
} else
print_otag(h, TAG_DD, "");
static int
mdoc_bl_pre(MDOC_ARGS)
{
- char cattr[28];
+ char cattr[32];
struct mdoc_bl *bl;
enum htmltag elemtype;
switch (n->type) {
- case ROFFT_BODY:
- return 1;
+ case ROFFT_BLOCK:
+ html_close_paragraph(h);
+ break;
case ROFFT_HEAD:
return 0;
+ case ROFFT_BODY:
+ return 1;
default:
- break;
+ abort();
}
bl = &n->norm->Bl;
static int
mdoc_d1_pre(MDOC_ARGS)
{
- if (n->type != ROFFT_BLOCK)
+ switch (n->type) {
+ case ROFFT_BLOCK:
+ html_close_paragraph(h);
+ break;
+ case ROFFT_HEAD:
+ return 0;
+ case ROFFT_BODY:
return 1;
-
+ default:
+ abort();
+ }
print_otag(h, TAG_DIV, "c", "Bd Bd-indent");
-
if (n->tok == MDOC_Dl)
print_otag(h, TAG_CODE, "c", "Li");
-
return 1;
}
static int
mdoc_bd_pre(MDOC_ARGS)
{
- int comp, sv;
+ char buf[16];
struct roff_node *nn;
+ int comp;
- if (n->type == ROFFT_HEAD)
- return 0;
-
- if (n->type == ROFFT_BLOCK) {
- comp = n->norm->Bd.comp;
- for (nn = n; nn && ! comp; nn = nn->parent) {
- if (nn->type != ROFFT_BLOCK)
- continue;
- if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
- comp = 1;
- if (nn->prev)
- break;
- }
- if ( ! comp)
- print_paragraph(h);
+ switch (n->type) {
+ case ROFFT_BLOCK:
+ html_close_paragraph(h);
return 1;
+ case ROFFT_HEAD:
+ return 0;
+ case ROFFT_BODY:
+ break;
+ default:
+ abort();
}
- /* Handle the -offset argument. */
-
- if (n->norm->Bd.offs == NULL ||
- ! strcmp(n->norm->Bd.offs, "left"))
- print_otag(h, TAG_DIV, "c", "Bd");
- else
- print_otag(h, TAG_DIV, "c", "Bd Bd-indent");
+ /* Handle preceding whitespace. */
- if (n->norm->Bd.type != DISP_unfilled &&
- n->norm->Bd.type != DISP_literal)
- return 1;
-
- print_otag(h, TAG_PRE, "c", "Li");
-
- /* This can be recursive: save & set our literal state. */
-
- sv = h->flags & HTML_LITERAL;
- h->flags |= HTML_LITERAL;
-
- for (nn = n->child; nn; nn = nn->next) {
- print_mdoc_node(meta, nn, h);
- /*
- * If the printed node flushes its own line, then we
- * needn't do it here as well. This is hacky, but the
- * notion of selective eoln whitespace is pretty dumb
- * anyway, so don't sweat it.
- */
- switch (nn->tok) {
- case ROFF_br:
- case ROFF_sp:
- case MDOC_Sm:
- case MDOC_Bl:
- case MDOC_D1:
- case MDOC_Dl:
- case MDOC_Lp:
- case MDOC_Pp:
+ comp = n->norm->Bd.comp;
+ for (nn = n; nn != NULL && comp == 0; nn = nn->parent) {
+ if (nn->type != ROFFT_BLOCK)
continue;
- default:
+ if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
+ comp = 1;
+ if (nn->prev != NULL)
break;
- }
- if (h->flags & HTML_NONEWLINE ||
- (nn->next && ! (nn->next->flags & NODE_LINE)))
- continue;
- else if (nn->next)
- print_text(h, "\n");
-
- h->flags |= HTML_NOSPACE;
}
+ (void)strlcpy(buf, "Bd", sizeof(buf));
+ if (comp == 0)
+ (void)strlcat(buf, " Pp", sizeof(buf));
+
+ /* Handle the -offset argument. */
- if (0 == sv)
- h->flags &= ~HTML_LITERAL;
+ if (n->norm->Bd.offs != NULL &&
+ strcmp(n->norm->Bd.offs, "left") != 0)
+ (void)strlcat(buf, " Bd-indent", sizeof(buf));
- return 0;
+ print_otag(h, TAG_DIV, "c", buf);
+ return 1;
}
static int
for (n = n->child->next; n; n = n->next) {
if (NODE_SYNPRETTY & n->flags)
- t = print_otag(h, TAG_VAR, "cTss?", "Fa",
+ t = print_otag(h, TAG_VAR, "cTs", "Fa",
"white-space", "nowrap");
else
t = print_otag(h, TAG_VAR, "cT", "Fa");
static int
mdoc_pp_pre(MDOC_ARGS)
{
-
- print_paragraph(h);
+ if ((n->flags & NODE_NOFILL) == 0) {
+ html_close_paragraph(h);
+ print_otag(h, TAG_P, "c", "Pp");
+ }
return 0;
}
{
const char *cattr;
- if (n->type == ROFFT_HEAD)
- return 0;
- else if (n->type != ROFFT_BODY)
+ switch (n->type) {
+ case ROFFT_BLOCK:
+ html_close_paragraph(h);
return 1;
+ case ROFFT_HEAD:
+ return 0;
+ case ROFFT_BODY:
+ break;
+ default:
+ abort();
+ }
if (FONT_Em == n->norm->Bf.font)
cattr = "Bf Em";
static int
mdoc_rs_pre(MDOC_ARGS)
{
- if (n->type != ROFFT_BLOCK)
- return 1;
-
- if (n->prev && SEC_SEE_ALSO == n->sec)
- print_paragraph(h);
-
- print_otag(h, TAG_CITE, "cT", "Rs");
+ switch (n->type) {
+ case ROFFT_BLOCK:
+ if (n->sec == SEC_SEE_ALSO)
+ html_close_paragraph(h);
+ break;
+ case ROFFT_HEAD:
+ return 0;
+ case ROFFT_BODY:
+ if (n->sec == SEC_SEE_ALSO)
+ print_otag(h, TAG_P, "c", "Pp");
+ print_otag(h, TAG_CITE, "cT", "Rs");
+ break;
+ default:
+ abort();
+ }
return 1;
}
case MDOC_Oo:
case MDOC_Op:
print_text(h, "\\(lB");
- h->flags |= HTML_NOSPACE;
- /* Cannot use TAG_SPAN because it may contain blocks. */
- print_otag(h, TAG_IDIV, "c", "Op");
+ /*
+ * Give up on semantic markup for now.
+ * We cannot use TAG_SPAN because .Oo may contain blocks.
+ * We cannot use TAG_IDIV because we might be in a
+ * phrasing context (like .Dl or .Pp); we cannot
+ * close out a .Pp at this point either because
+ * that would break the line.
+ */
+ /* XXX print_otag(h, TAG_???, "c", "Op"); */
break;
case MDOC_En:
if (NULL == n->norm->Es ||
else if ( ! tail)
h->flags &= ~HTML_NOSPACE;
}
+
+static int
+mdoc_abort_pre(MDOC_ARGS)
+{
+ abort();
+}