version 1.265, 2020/04/06 10:16:17 |
version 1.275, 2021/09/09 14:47:24 |
|
|
/* $Id$ */ |
/* $Id$ */ |
/* |
/* |
* Copyright (c) 2011-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> |
|
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> |
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv> |
|
* Copyright (c) 2011-2015, 2017-2021 Ingo Schwarze <schwarze@openbsd.org> |
* |
* |
* Permission to use, copy, modify, and distribute this software for any |
* Permission to use, copy, modify, and distribute this software for any |
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
Line 81 static const struct htmldata htmltags[TAG_MAX] = { |
|
Line 81 static const struct htmldata htmltags[TAG_MAX] = { |
|
{"h1", HTML_TOPHRASE | HTML_NLAROUND}, |
{"h1", HTML_TOPHRASE | HTML_NLAROUND}, |
{"h2", HTML_TOPHRASE | HTML_NLAROUND}, |
{"h2", HTML_TOPHRASE | HTML_NLAROUND}, |
{"p", HTML_TOPHRASE | HTML_NLAROUND | HTML_INDENT}, |
{"p", HTML_TOPHRASE | HTML_NLAROUND | HTML_INDENT}, |
{"pre", HTML_TOPHRASE | HTML_NLALL | HTML_NOINDENT}, |
{"pre", HTML_TOPHRASE | HTML_NLAROUND | HTML_NOINDENT}, |
{"a", HTML_INPHRASE | HTML_TOPHRASE}, |
{"a", HTML_INPHRASE | HTML_TOPHRASE}, |
{"b", HTML_INPHRASE | HTML_TOPHRASE}, |
{"b", HTML_INPHRASE | HTML_TOPHRASE}, |
{"cite", HTML_INPHRASE | HTML_TOPHRASE}, |
{"cite", HTML_INPHRASE | HTML_TOPHRASE}, |
Line 91 static const struct htmldata htmltags[TAG_MAX] = { |
|
Line 91 static const struct htmldata htmltags[TAG_MAX] = { |
|
{"span", HTML_INPHRASE | HTML_TOPHRASE}, |
{"span", HTML_INPHRASE | HTML_TOPHRASE}, |
{"var", HTML_INPHRASE | HTML_TOPHRASE}, |
{"var", HTML_INPHRASE | HTML_TOPHRASE}, |
{"br", HTML_INPHRASE | HTML_NOSTACK | HTML_NLALL}, |
{"br", HTML_INPHRASE | HTML_NOSTACK | HTML_NLALL}, |
|
{"hr", HTML_INPHRASE | HTML_NOSTACK}, |
{"mark", HTML_INPHRASE }, |
{"mark", HTML_INPHRASE }, |
{"math", HTML_INPHRASE | HTML_NLALL | HTML_INDENT}, |
{"math", HTML_INPHRASE | HTML_NLALL | HTML_INDENT}, |
{"mrow", 0}, |
{"mrow", 0}, |
Line 112 static const struct htmldata htmltags[TAG_MAX] = { |
|
Line 113 static const struct htmldata htmltags[TAG_MAX] = { |
|
}; |
}; |
|
|
/* Avoid duplicate HTML id= attributes. */ |
/* Avoid duplicate HTML id= attributes. */ |
|
|
|
struct id_entry { |
|
int ord; /* Ordinal number of the latest occurrence. */ |
|
char id[]; /* The id= attribute without any ordinal suffix. */ |
|
}; |
static struct ohash id_unique; |
static struct ohash id_unique; |
|
|
static void html_reset_internal(struct html *); |
static void html_reset_internal(struct html *); |
Line 135 html_alloc(const struct manoutput *outopts) |
|
Line 141 html_alloc(const struct manoutput *outopts) |
|
h = mandoc_calloc(1, sizeof(struct html)); |
h = mandoc_calloc(1, sizeof(struct html)); |
|
|
h->tag = NULL; |
h->tag = NULL; |
|
h->metac = h->metal = ESCAPE_FONTROMAN; |
h->style = outopts->style; |
h->style = outopts->style; |
if ((h->base_man1 = outopts->man) == NULL) |
if ((h->base_man1 = outopts->man) == NULL) |
h->base_man2 = NULL; |
h->base_man2 = NULL; |
Line 146 html_alloc(const struct manoutput *outopts) |
|
Line 153 html_alloc(const struct manoutput *outopts) |
|
if (outopts->toc) |
if (outopts->toc) |
h->oflags |= HTML_TOC; |
h->oflags |= HTML_TOC; |
|
|
mandoc_ohash_init(&id_unique, 4, 0); |
mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); |
|
|
return h; |
return h; |
} |
} |
|
|
html_reset_internal(struct html *h) |
html_reset_internal(struct html *h) |
{ |
{ |
struct tag *tag; |
struct tag *tag; |
char *cp; |
struct id_entry *entry; |
unsigned int slot; |
unsigned int slot; |
|
|
while ((tag = h->tag) != NULL) { |
while ((tag = h->tag) != NULL) { |
h->tag = tag->next; |
h->tag = tag->next; |
free(tag); |
free(tag); |
} |
} |
cp = ohash_first(&id_unique, &slot); |
entry = ohash_first(&id_unique, &slot); |
while (cp != NULL) { |
while (entry != NULL) { |
free(cp); |
free(entry); |
cp = ohash_next(&id_unique, &slot); |
entry = ohash_next(&id_unique, &slot); |
} |
} |
ohash_delete(&id_unique); |
ohash_delete(&id_unique); |
} |
} |
|
|
html_reset(void *p) |
html_reset(void *p) |
{ |
{ |
html_reset_internal(p); |
html_reset_internal(p); |
mandoc_ohash_init(&id_unique, 4, 0); |
mandoc_ohash_init(&id_unique, 4, offsetof(struct id_entry, id)); |
} |
} |
|
|
void |
void |
Line 190 print_gen_head(struct html *h) |
|
Line 197 print_gen_head(struct html *h) |
|
struct tag *t; |
struct tag *t; |
|
|
print_otag(h, TAG_META, "?", "charset", "utf-8"); |
print_otag(h, TAG_META, "?", "charset", "utf-8"); |
|
print_otag(h, TAG_META, "??", "name", "viewport", |
|
"content", "width=device-width, initial-scale=1.0"); |
if (h->style != NULL) { |
if (h->style != NULL) { |
print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", |
print_otag(h, TAG_LINK, "?h??", "rel", "stylesheet", |
h->style, "type", "text/css", "media", "all"); |
h->style, "type", "text/css", "media", "all"); |
Line 232 html_setfont(struct html *h, enum mandoc_esc font) |
|
Line 241 html_setfont(struct html *h, enum mandoc_esc font) |
|
case ESCAPE_FONTITALIC: |
case ESCAPE_FONTITALIC: |
case ESCAPE_FONTBOLD: |
case ESCAPE_FONTBOLD: |
case ESCAPE_FONTBI: |
case ESCAPE_FONTBI: |
case ESCAPE_FONTCW: |
|
case ESCAPE_FONTROMAN: |
case ESCAPE_FONTROMAN: |
|
case ESCAPE_FONTCR: |
|
case ESCAPE_FONTCB: |
|
case ESCAPE_FONTCI: |
break; |
break; |
case ESCAPE_FONT: |
case ESCAPE_FONT: |
font = ESCAPE_FONTROMAN; |
font = ESCAPE_FONTROMAN; |
Line 264 print_metaf(struct html *h) |
|
Line 275 print_metaf(struct html *h) |
|
h->metaf = print_otag(h, TAG_B, ""); |
h->metaf = print_otag(h, TAG_B, ""); |
print_otag(h, TAG_I, ""); |
print_otag(h, TAG_I, ""); |
break; |
break; |
case ESCAPE_FONTCW: |
case ESCAPE_FONTCR: |
h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); |
h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); |
break; |
break; |
|
case ESCAPE_FONTCB: |
|
h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); |
|
print_otag(h, TAG_B, ""); |
|
break; |
|
case ESCAPE_FONTCI: |
|
h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); |
|
print_otag(h, TAG_I, ""); |
|
break; |
default: |
default: |
break; |
break; |
} |
} |
Line 329 html_fillmode(struct html *h, enum roff_tok want) |
|
Line 348 html_fillmode(struct html *h, enum roff_tok want) |
|
* element and/or as a segment identifier for a URI in an <a> element. |
* element and/or as a segment identifier for a URI in an <a> element. |
* The function may fail and return NULL if the node lacks text data |
* The function may fail and return NULL if the node lacks text data |
* to create the attribute from. |
* to create the attribute from. |
* If the "unique" argument is 0, the caller is responsible for |
* The caller is responsible for free(3)ing the returned string. |
* free(3)ing the returned string after using it. |
* |
* If the "unique" argument is non-zero, the "id_unique" ohash table |
* If the "unique" argument is non-zero, the "id_unique" ohash table |
* is used for de-duplication and owns the returned string, so the |
* is used for de-duplication. If the "unique" argument is 1, |
* caller must not free(3) it. In this case, it will be freed |
* it is the first time the function is called for this tag and |
* automatically by html_reset() or html_free(). |
* location, so if an ordinal suffix is needed, it is incremented. |
|
* If the "unique" argument is 2, it is the second time the function |
|
* is called for this tag and location, so the ordinal suffix |
|
* remains unchanged. |
*/ |
*/ |
char * |
char * |
html_make_id(const struct roff_node *n, int unique) |
html_make_id(const struct roff_node *n, int unique) |
{ |
{ |
const struct roff_node *nch; |
const struct roff_node *nch; |
char *buf, *bufs, *cp; |
struct id_entry *entry; |
|
char *buf, *cp; |
|
size_t len; |
unsigned int slot; |
unsigned int slot; |
int suffix; |
|
|
|
if (n->string != NULL) |
if (n->tag != NULL) |
buf = mandoc_strdup(n->string); |
buf = mandoc_strdup(n->tag); |
else { |
else { |
switch (n->tok) { |
switch (n->tok) { |
case MDOC_Sh: |
case MDOC_Sh: |
Line 374 html_make_id(const struct roff_node *n, int unique) |
|
Line 397 html_make_id(const struct roff_node *n, int unique) |
|
* permitted in URL-fragment strings according to the |
* permitted in URL-fragment strings according to the |
* explicit list at: |
* explicit list at: |
* https://url.spec.whatwg.org/#url-fragment-string |
* https://url.spec.whatwg.org/#url-fragment-string |
|
* In addition, reserve '~' for ordinal suffixes. |
*/ |
*/ |
|
|
for (cp = buf; *cp != '\0'; cp++) |
for (cp = buf; *cp != '\0'; cp++) |
if (isalnum((unsigned char)*cp) == 0 && |
if (isalnum((unsigned char)*cp) == 0 && |
strchr("!$&'()*+,-./:;=?@_~", *cp) == NULL) |
strchr("!$&'()*+,-./:;=?@_", *cp) == NULL) |
*cp = '_'; |
*cp = '_'; |
|
|
if (unique == 0) |
if (unique == 0) |
Line 386 html_make_id(const struct roff_node *n, int unique) |
|
Line 410 html_make_id(const struct roff_node *n, int unique) |
|
|
|
/* Avoid duplicate HTML id= attributes. */ |
/* Avoid duplicate HTML id= attributes. */ |
|
|
bufs = NULL; |
|
suffix = 1; |
|
slot = ohash_qlookup(&id_unique, buf); |
slot = ohash_qlookup(&id_unique, buf); |
cp = ohash_find(&id_unique, slot); |
if ((entry = ohash_find(&id_unique, slot)) == NULL) { |
if (cp != NULL) { |
len = strlen(buf) + 1; |
while (cp != NULL) { |
entry = mandoc_malloc(sizeof(*entry) + len); |
free(bufs); |
entry->ord = 1; |
if (++suffix > 127) { |
memcpy(entry->id, buf, len); |
free(buf); |
ohash_insert(&id_unique, slot, entry); |
return NULL; |
} else if (unique == 1) |
} |
entry->ord++; |
mandoc_asprintf(&bufs, "%s_%d", buf, suffix); |
|
slot = ohash_qlookup(&id_unique, bufs); |
if (entry->ord > 1) { |
cp = ohash_find(&id_unique, slot); |
cp = buf; |
} |
mandoc_asprintf(&buf, "%s~%d", cp, entry->ord); |
free(buf); |
free(cp); |
buf = bufs; |
|
} |
} |
ohash_insert(&id_unique, slot, buf); |
|
return buf; |
return buf; |
} |
} |
|
|
Line 494 print_encode(struct html *h, const char *p, const char |
|
Line 514 print_encode(struct html *h, const char *p, const char |
|
case ESCAPE_FONTBOLD: |
case ESCAPE_FONTBOLD: |
case ESCAPE_FONTITALIC: |
case ESCAPE_FONTITALIC: |
case ESCAPE_FONTBI: |
case ESCAPE_FONTBI: |
case ESCAPE_FONTCW: |
|
case ESCAPE_FONTROMAN: |
case ESCAPE_FONTROMAN: |
|
case ESCAPE_FONTCR: |
|
case ESCAPE_FONTCB: |
|
case ESCAPE_FONTCI: |
if (0 == norecurse) { |
if (0 == norecurse) { |
h->flags |= HTML_NOSPACE; |
h->flags |= HTML_NOSPACE; |
if (html_setfont(h, esc)) |
if (html_setfont(h, esc)) |
Line 779 print_otag_id(struct html *h, enum htmltag elemtype, c |
|
Line 801 print_otag_id(struct html *h, enum htmltag elemtype, c |
|
{ |
{ |
struct roff_node *nch; |
struct roff_node *nch; |
struct tag *ret, *t; |
struct tag *ret, *t; |
const char *id; |
char *id, *href; |
|
|
ret = NULL; |
ret = NULL; |
id = NULL; |
id = href = NULL; |
if (n->flags & NODE_ID) |
if (n->flags & NODE_ID) |
id = html_make_id(n, 1); |
id = html_make_id(n, 1); |
if (id != NULL && htmltags[elemtype].flags & HTML_INPHRASE) |
if (n->flags & NODE_HREF) |
ret = print_otag(h, TAG_A, "chR", "permalink", id); |
href = id == NULL ? html_make_id(n, 2) : id; |
|
if (href != NULL && htmltags[elemtype].flags & HTML_INPHRASE) |
|
ret = print_otag(h, TAG_A, "chR", "permalink", href); |
t = print_otag(h, elemtype, "ci", cattr, id); |
t = print_otag(h, elemtype, "ci", cattr, id); |
if (ret == NULL) { |
if (ret == NULL) { |
ret = t; |
ret = t; |
if (id != NULL && (nch = n->child) != NULL) { |
if (href != NULL && (nch = n->child) != NULL) { |
/* man(7) is safe, it tags phrasing content only. */ |
/* man(7) is safe, it tags phrasing content only. */ |
if (n->tok > MDOC_MAX || |
if (n->tok > MDOC_MAX || |
htmltags[elemtype].flags & HTML_TOPHRASE) |
htmltags[elemtype].flags & HTML_TOPHRASE) |
Line 799 print_otag_id(struct html *h, enum htmltag elemtype, c |
|
Line 823 print_otag_id(struct html *h, enum htmltag elemtype, c |
|
while (nch != NULL && nch->type == ROFFT_TEXT) |
while (nch != NULL && nch->type == ROFFT_TEXT) |
nch = nch->next; |
nch = nch->next; |
if (nch == NULL) |
if (nch == NULL) |
print_otag(h, TAG_A, "chR", "permalink", id); |
print_otag(h, TAG_A, "chR", "permalink", href); |
} |
} |
} |
} |
|
free(id); |
|
if (id == NULL) |
|
free(href); |
return ret; |
return ret; |
} |
} |
|
|
Line 874 print_gen_comment(struct html *h, struct roff_node *n) |
|
Line 901 print_gen_comment(struct html *h, struct roff_node *n) |
|
void |
void |
print_text(struct html *h, const char *word) |
print_text(struct html *h, const char *word) |
{ |
{ |
|
print_tagged_text(h, word, NULL); |
|
} |
|
|
|
void |
|
print_tagged_text(struct html *h, const char *word, struct roff_node *n) |
|
{ |
|
struct tag *t; |
|
char *href; |
|
|
/* |
/* |
* Always wrap text in a paragraph unless already contained in |
* Always wrap text in a paragraph unless already contained in |
* some flow container; never put it directly into a section. |
* some flow container; never put it directly into a section. |
Line 894 print_text(struct html *h, const char *word) |
|
Line 930 print_text(struct html *h, const char *word) |
|
} |
} |
|
|
/* |
/* |
* Print the text, optionally surrounded by HTML whitespace, |
* Optionally switch fonts, optionally write a permalink, then |
* optionally manually switching fonts before and after. |
* print the text, optionally surrounded by HTML whitespace. |
*/ |
*/ |
|
|
assert(h->metaf == NULL); |
assert(h->metaf == NULL); |
print_metaf(h); |
print_metaf(h); |
print_indent(h); |
print_indent(h); |
|
|
|
if (n != NULL && (href = html_make_id(n, 2)) != NULL) { |
|
t = print_otag(h, TAG_A, "chR", "permalink", href); |
|
free(href); |
|
} else |
|
t = NULL; |
|
|
if ( ! print_encode(h, word, NULL, 0)) { |
if ( ! print_encode(h, word, NULL, 0)) { |
if ( ! (h->flags & HTML_NONOSPACE)) |
if ( ! (h->flags & HTML_NONOSPACE)) |
h->flags &= ~HTML_NOSPACE; |
h->flags &= ~HTML_NOSPACE; |
Line 911 print_text(struct html *h, const char *word) |
|
Line 954 print_text(struct html *h, const char *word) |
|
if (h->metaf != NULL) { |
if (h->metaf != NULL) { |
print_tagq(h, h->metaf); |
print_tagq(h, h->metaf); |
h->metaf = NULL; |
h->metaf = NULL; |
} |
} else if (t != NULL) |
|
print_tagq(h, t); |
|
|
h->flags &= ~HTML_IGNDELIM; |
h->flags &= ~HTML_IGNDELIM; |
} |
} |