diff options
Diffstat (limited to '')
-rw-r--r--source/html.c79
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 = "&lt;";
+ else if (*p == '>')
+ entity = "&gt;";
+ else if (*p == '&')
+ entity = "&amp;";
+ 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;