diff options
Diffstat (limited to 'source/html.c')
| -rw-r--r-- | source/html.c | 79 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
1 file changed, 79 insertions, 0 deletions
diff --git a/source/html.c b/source/html.c index d1e28f8..d13ac5b 100644 --- a/source/html.c +++ b/source/html.c @@ -258,6 +258,85 @@ ssize_t html_ntxt(const char *txt, size_t len) return left; } +/* + * git ends a line only at a line feed. A browser also starts one at a carriage + * return with no line feed after it, since the HTML parser turns that into a + * line feed, and may at the other characters Unicode makes mandatory breaks. + * Escaping those keeps one line on the page for every line git counts, so the + * numbers beside the text stay in step with it. + */ +const char *html_line_break_escape(const char *p, const char *end, size_t *width) +{ + const char *esc; + + switch ((unsigned char)*p) { + case '\r': + // The parser reads CR LF as the single line feed git ends the + // line with. + if (p + 1 < end && p[1] == '\n') + return NULL; + *width = 1; + return "\\r"; + case '\v': + *width = 1; + return "\\v"; + case '\f': + *width = 1; + return "\\f"; + case 0xc2: + if (end - p < 2 || memcmp(p, "\xc2\x85", 2)) + return NULL; + *width = 2; + return "\\u0085"; + case 0xe2: + if (end - p >= 3 && !memcmp(p, "\xe2\x80\xa8", 3)) + esc = "\\u2028"; + else if (end - p >= 3 && !memcmp(p, "\xe2\x80\xa9", 3)) + esc = "\\u2029"; + else + return NULL; + *width = 3; + return esc; + default: + return NULL; + } +} + +void html_blob_txt(const char *txt, size_t len) +{ + const char *end = txt + len; + const char *p; + + for (p = txt; p < end; p++) { + const char *entity = NULL, *esc = NULL; + size_t width = 1; + + if (*p == '<') + entity = "<"; + else if (*p == '>') + entity = ">"; + else if (*p == '&') + entity = "&"; + else + esc = html_line_break_escape(p, end, &width); + if (!entity && !esc) + continue; + html_raw(txt, p - txt); + if (entity) { + html(entity); + } else { + // The span lets a stylesheet tell the escape from the + // same characters typed into the file. + html("<span class='escape'>"); + html(esc); + html("</span>"); + } + p += width - 1; + txt = p + 1; + } + html_raw(txt, end - txt); +} + void html_attrf(const char *format, ...) { va_list args; |
