=================================================================== RCS file: /cvs/mandoc/html.c,v retrieving revision 1.226 retrieving revision 1.242 diff -u -p -r1.226 -r1.242 --- mandoc/html.c 2018/05/21 00:00:37 1.226 +++ mandoc/html.c 2018/10/25 01:32:40 1.242 @@ -1,4 +1,4 @@ -/* $Id: html.c,v 1.226 2018/05/21 00:00:37 schwarze Exp $ */ +/* $Id: html.c,v 1.242 2018/10/25 01:32:40 schwarze Exp $ */ /* * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze @@ -18,10 +18,12 @@ #include "config.h" #include +#include #include #include #include +#include #include #include #include @@ -29,6 +31,7 @@ #include #include "mandoc_aux.h" +#include "mandoc_ohash.h" #include "mandoc.h" #include "roff.h" #include "out.h" @@ -67,8 +70,6 @@ static const struct htmldata htmltags[TAG_MAX] = { {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"a", 0}, {"table", HTML_NLALL | HTML_INDENT}, - {"colgroup", HTML_NLALL | HTML_INDENT}, - {"col", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, {"tr", HTML_NLALL | HTML_INDENT}, {"td", HTML_NLAROUND}, {"li", HTML_NLAROUND | HTML_INDENT}, @@ -104,20 +105,9 @@ static const struct htmldata htmltags[TAG_MAX] = { {"mover", 0}, }; -static const char *const roffscales[SCALE_MAX] = { - "cm", /* SCALE_CM */ - "in", /* SCALE_IN */ - "pc", /* SCALE_PC */ - "pt", /* SCALE_PT */ - "em", /* SCALE_EM */ - "em", /* SCALE_MM */ - "ex", /* SCALE_EN */ - "ex", /* SCALE_BU */ - "em", /* SCALE_VS */ - "ex", /* SCALE_FS */ -}; +/* Avoid duplicate HTML id= attributes. */ +static struct ohash id_unique; -static void a2width(const char *, struct roffsu *); static void print_byte(struct html *, char); static void print_endword(struct html *); static void print_indent(struct html *); @@ -139,11 +129,18 @@ html_alloc(const struct manoutput *outopts) h->tag = NULL; h->style = outopts->style; - h->base_man = outopts->man; + if ((h->base_man1 = outopts->man) == NULL) + h->base_man2 = NULL; + else if ((h->base_man2 = strchr(h->base_man1, ';')) != NULL) + *h->base_man2++ = '\0'; h->base_includes = outopts->includes; if (outopts->fragment) h->oflags |= HTML_FRAGMENT; + if (outopts->toc) + h->oflags |= HTML_TOC; + mandoc_ohash_init(&id_unique, 4, 0); + return h; } @@ -152,15 +149,22 @@ html_free(void *p) { struct tag *tag; struct html *h; + char *cp; + unsigned int slot; h = (struct html *)p; - while ((tag = h->tag) != NULL) { h->tag = tag->next; free(tag); } - free(h); + + cp = ohash_first(&id_unique, &slot); + while (cp != NULL) { + free(cp); + cp = ohash_next(&id_unique, &slot); + } + ohash_delete(&id_unique); } void @@ -190,8 +194,10 @@ print_gen_head(struct html *h) print_endline(h); print_text(h, "div.Nd, div.Bf, div.Op { display: inline; }"); print_endline(h); - print_text(h, "span.Pa { font-style: italic; }"); + print_text(h, "span.Pa, span.Ad { font-style: italic; }"); print_endline(h); + print_text(h, "span.Ms { font-weight: bold; }"); + print_endline(h); print_text(h, "dl.Bl-diag "); print_byte(h, '>'); print_text(h, " dt { font-weight: bold; }"); @@ -222,6 +228,9 @@ print_metaf(struct html *h, enum mandoc_esc deco) case ESCAPE_FONTBI: font = HTMLFONT_BI; break; + case ESCAPE_FONTCW: + font = HTMLFONT_CW; + break; case ESCAPE_FONT: case ESCAPE_FONTROMAN: font = HTMLFONT_NONE; @@ -249,16 +258,21 @@ print_metaf(struct html *h, enum mandoc_esc deco) h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; + case HTMLFONT_CW: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + break; default: break; } } char * -html_make_id(const struct roff_node *n) +html_make_id(const struct roff_node *n, int unique) { const struct roff_node *nch; - char *buf, *cp; + char *buf, *bufs, *cp; + unsigned int slot; + int suffix; for (nch = n->child; nch != NULL; nch = nch->next) if (nch->type != ROFFT_TEXT) @@ -269,64 +283,43 @@ html_make_id(const struct roff_node *n) if (buf == NULL) return NULL; - /* http://www.w3.org/TR/html5/dom.html#the-id-attribute */ + /* + * In ID attributes, only use ASCII characters that are + * permitted in URL-fragment strings according to the + * explicit list at: + * https://url.spec.whatwg.org/#url-fragment-string + */ for (cp = buf; *cp != '\0'; cp++) - if (*cp == ' ') + if (isalnum((unsigned char)*cp) == 0 && + strchr("!$&'()*+,-./:;=?@_~", *cp) == NULL) *cp = '_'; - return buf; -} + if (unique == 0) + return buf; -int -html_strlen(const char *cp) -{ - size_t rsz; - int skip, sz; + /* Avoid duplicate HTML id= attributes. */ - /* - * Account for escaped sequences within string length - * calculations. This follows the logic in term_strlen() as we - * must calculate the width of produced strings. - * Assume that characters are always width of "1". This is - * hacky, but it gets the job done for approximation of widths. - */ - - sz = 0; - skip = 0; - while (1) { - rsz = strcspn(cp, "\\"); - if (rsz) { - cp += rsz; - if (skip) { - skip = 0; - rsz--; + bufs = NULL; + suffix = 1; + slot = ohash_qlookup(&id_unique, buf); + cp = ohash_find(&id_unique, slot); + if (cp != NULL) { + while (cp != NULL) { + free(bufs); + if (++suffix > 127) { + free(buf); + return NULL; } - sz += rsz; + mandoc_asprintf(&bufs, "%s_%d", buf, suffix); + slot = ohash_qlookup(&id_unique, bufs); + cp = ohash_find(&id_unique, slot); } - if ('\0' == *cp) - break; - cp++; - switch (mandoc_escape(&cp, NULL, NULL)) { - case ESCAPE_ERROR: - return sz; - case ESCAPE_UNICODE: - case ESCAPE_NUMBERED: - case ESCAPE_SPECIAL: - case ESCAPE_OVERSTRIKE: - if (skip) - skip = 0; - else - sz++; - break; - case ESCAPE_SKIPCHAR: - skip = 1; - break; - default: - break; - } + free(buf); + buf = bufs; } - return sz; + ohash_insert(&id_unique, slot, buf); + return buf; } static int @@ -421,6 +414,7 @@ print_encode(struct html *h, const char *p, const char case ESCAPE_FONTBOLD: case ESCAPE_FONTITALIC: case ESCAPE_FONTBI: + case ESCAPE_FONTCW: case ESCAPE_FONTROMAN: if (0 == norecurse) print_metaf(h, esc); @@ -452,6 +446,9 @@ print_encode(struct html *h, const char *p, const char if (c <= 0) continue; break; + case ESCAPE_DEVICE: + print_word(h, "html"); + continue; case ESCAPE_BREAK: breakline = 1; continue; @@ -483,9 +480,21 @@ print_encode(struct html *h, const char *p, const char static void print_href(struct html *h, const char *name, const char *sec, int man) { + struct stat sb; const char *p, *pp; + char *filename; - pp = man ? h->base_man : h->base_includes; + if (man) { + pp = h->base_man1; + if (h->base_man2 != NULL) { + mandoc_asprintf(&filename, "%s.%s", name, sec); + if (stat(filename, &sb) == -1) + pp = h->base_man2; + free(filename); + } + } else + pp = h->base_includes; + while ((p = strchr(pp, '%')) != NULL) { print_encode(h, pp, p, 1); if (man && p[1] == 'S') { @@ -508,13 +517,10 @@ struct tag * print_otag(struct html *h, enum htmltag tag, const char *fmt, ...) { va_list ap; - struct roffsu mysu, *su; - char numbuf[16]; struct tag *t; const char *attr; char *arg1, *arg2; - double v; - int i, have_style, tflags; + int tflags; tflags = htmltags[tag].flags; @@ -554,17 +560,12 @@ print_otag(struct html *h, enum htmltag tag, const cha va_start(ap, fmt); - have_style = 0; while (*fmt != '\0') { - if (*fmt == 's') { - have_style = 1; - fmt++; - break; - } - /* Parse a non-style attribute and its arguments. */ + /* Parse attributes and arguments. */ arg1 = va_arg(ap, char *); + arg2 = NULL; switch (*fmt++) { case 'c': attr = "class"; @@ -575,6 +576,10 @@ print_otag(struct html *h, enum htmltag tag, const cha case 'i': attr = "id"; break; + case 's': + attr = "style"; + arg2 = va_arg(ap, char *); + break; case '?': attr = arg1; arg1 = va_arg(ap, char *); @@ -582,13 +587,12 @@ print_otag(struct html *h, enum htmltag tag, const cha default: abort(); } - arg2 = NULL; if (*fmt == 'M') arg2 = va_arg(ap, char *); if (arg1 == NULL) continue; - /* Print the non-style attributes. */ + /* Print the attributes. */ print_byte(h, ' '); print_word(h, attr); @@ -615,114 +619,19 @@ print_otag(struct html *h, enum htmltag tag, const cha fmt++; break; default: - print_encode(h, arg1, NULL, 1); - break; - } - print_byte(h, '"'); - } - - /* Print out styles. */ - - while (*fmt != '\0') { - arg1 = NULL; - su = NULL; - - /* First letter: input argument type. */ - - switch (*fmt++) { - case 'h': - i = va_arg(ap, int); - su = &mysu; - SCALE_HS_INIT(su, i); - break; - case 's': - arg1 = va_arg(ap, char *); - break; - case 'u': - su = va_arg(ap, struct roffsu *); - break; - case 'w': - if ((arg2 = va_arg(ap, char *)) != NULL) { - su = &mysu; - a2width(arg2, su); + if (arg2 == NULL) + print_encode(h, arg1, NULL, 1); + else { + print_word(h, arg1); + print_byte(h, ':'); + print_byte(h, ' '); + print_word(h, arg2); + print_byte(h, ';'); } - if (*fmt == '*') { - if (su != NULL && su->unit == SCALE_EN && - su->scale > 5.9 && su->scale < 6.1) - su = NULL; - fmt++; - } - if (*fmt == '+') { - if (su != NULL) { - /* Make even bold text fit. */ - su->scale *= 1.2; - /* Add padding. */ - su->scale += 3.0; - } - fmt++; - } - if (*fmt == '-') { - if (su != NULL) - su->scale *= -1.0; - fmt++; - } break; - default: - abort(); } - - /* Second letter: style name. */ - - switch (*fmt++) { - case 'h': - attr = "height"; - break; - case 'i': - attr = "text-indent"; - break; - case 'l': - attr = "margin-left"; - break; - case 'w': - attr = "width"; - break; - case 'W': - attr = "min-width"; - break; - case '?': - attr = arg1; - arg1 = va_arg(ap, char *); - break; - default: - abort(); - } - if (su == NULL && arg1 == NULL) - continue; - - if (have_style == 1) - print_word(h, " style=\""); - else - print_byte(h, ' '); - print_word(h, attr); - print_byte(h, ':'); - print_byte(h, ' '); - if (su != NULL) { - v = su->scale; - if (su->unit == SCALE_MM && (v /= 100.0) == 0.0) - v = 1.0; - else if (su->unit == SCALE_BU) - v /= 24.0; - (void)snprintf(numbuf, sizeof(numbuf), "%.2f", v); - print_word(h, numbuf); - print_word(h, roffscales[su->unit]); - } else - print_word(h, arg1); - print_byte(h, ';'); - have_style = 2; - } - if (have_style == 2) print_byte(h, '"'); - + } va_end(ap); /* Accommodate for "well-formed" singleton escaping. */ @@ -836,6 +745,9 @@ print_text(struct html *h, const char *word) h->metaf = print_otag(h, TAG_B, ""); print_otag(h, TAG_I, ""); break; + case HTMLFONT_CW: + h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); + break; default: print_indent(h); break; @@ -1002,22 +914,4 @@ print_word(struct html *h, const char *cp) { while (*cp != '\0') print_byte(h, *cp++); -} - -/* - * Calculate the scaling unit passed in a `-width' argument. This uses - * either a native scaling unit (e.g., 1i, 2m) or the string length of - * the value. - */ -static void -a2width(const char *p, struct roffsu *su) -{ - const char *end; - - end = a2roffsu(p, su, SCALE_MAX); - if (end == NULL || *end != '\0') { - su->unit = SCALE_EN; - su->scale = html_strlen(p); - } else if (su->scale < 0.0) - su->scale = 0.0; }