diff options
Diffstat (limited to '')
| -rw-r--r-- | source/cgit.c | 2 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/cgit.h | 2 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/html.c | 79 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/html.h | 13 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/ui-blame.c | 18 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/ui-patch.c | 12 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -rw-r--r-- | source/ui-tree.c | 4 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
7 files changed, 121 insertions, 9 deletions
diff --git a/source/cgit.c b/source/cgit.c index 5f8ecfa..a877e92 100644 --- a/source/cgit.c +++ b/source/cgit.c @@ -54,6 +54,8 @@ struct refmatch { int found; }; +const char *cgit_version = CGIT_VERSION; + /* * Isolate git from the calling user's configuration, which could otherwise * break once HOME is unset below. It runs as a constructor because git reads diff --git a/source/cgit.h b/source/cgit.h index 5b92632..c04193f 100644 --- a/source/cgit.h +++ b/source/cgit.h @@ -324,6 +324,8 @@ struct cgit_context { int empty_repo; }; +extern const char *cgit_version; + extern struct cgit_repolist cgit_repolist; extern struct cgit_context ctx; 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; diff --git a/source/html.h b/source/html.h index c6c0e19..9cad3a6 100644 --- a/source/html.h +++ b/source/html.h @@ -61,6 +61,19 @@ extern void html_txt(const char *txt); */ extern ssize_t html_ntxt(const char *txt, size_t len); +/* + * The escape sequence a blob's text shows for the character at p when a + * browser would start a new line at it though git does not, or NULL for any + * other character. The character's length in bytes is stored in width. + */ +extern const char *html_line_break_escape(const char *p, const char *end, size_t *width); + +/* + * Escape the text of a blob for the page beside its line numbers, writing + * each character html_line_break_escape names as its escape sequence. + */ +extern void html_blob_txt(const char *txt, size_t len); + extern void html_attr(const char *txt); extern void html_url_path(const char *txt); diff --git a/source/ui-blame.c b/source/ui-blame.c index 8fd6c83..f60020c 100644 --- a/source/ui-blame.c +++ b/source/ui-blame.c @@ -211,6 +211,15 @@ static size_t line_width(struct blame_scoreboard *sb, int line) size_t width = 0; while (pos < end) { + size_t len; + const char *esc = html_line_break_escape(pos, end, &len); + + // An escaped character takes the room of its escape sequence. + if (esc) { + width += strlen(esc); + pos += len; + continue; + } width++; if (*pos++ == '\t') width = (width + TAB_WIDTH - 1) & ~(TAB_WIDTH - 1); @@ -297,9 +306,10 @@ static void print_blame_page(const struct object_id *oid, const char *path, cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path); html(")</div>\n"); - // A NUL past the window buffer_is_binary sniffs would end html_txt - // early while the hash and line number columns still cover the whole - // file, so a blob holding one anywhere is treated as binary too. + // A NUL past the window buffer_is_binary sniffs would end a Lua + // filter's output early while the hash and line number columns still + // cover the whole file, so a blob holding one anywhere is treated as + // binary too. if (buffer_is_binary(buf, size) || memchr(buf, 0, size)) { struct blame_entry *ent = sb.ent, *next; @@ -334,7 +344,7 @@ static void print_blame_page(const struct object_id *oid, const char *path, cgit_close_filter(ctx.repo->source_filter); free(filter_arg); } else { - html_txt(buf); + html_blob_txt(buf, size); } html("</code></pre>"); diff --git a/source/ui-patch.c b/source/ui-patch.c index 525e660..78c850c 100644 --- a/source/ui-patch.c +++ b/source/ui-patch.c @@ -62,6 +62,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, const char *pref struct commit *commit; struct object_id new_oid, old_oid; char rev_range[REV_RANGE_LEN]; + const char *version; // setup_revisions reads these the way git reads a command line, so the // first entry stands in for the program name and is skipped, and the // array has to stay null terminated because the path after the double @@ -130,11 +131,16 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, const char *pref ctx.page.filename = cgit_fmt("%s.patch", rev_range); cgit_print_http_headers(); + // git format-patch signs with its bare version number, so the v that + // cgit's tags carry is left off to read the same way. + if (!skip_prefix(cgit_version, "v", &version)) + version = cgit_version; + while ((commit = get_revision(&rev))) { log_tree_commit(&rev, commit); // Two dashes and a space is the mail signature separator, so - // git am and mail readers drop the line below rather than - // carrying it into the commit message. - printf("-- \ncgit\n\n"); + // git am and mail readers drop the version note below rather + // than carrying it into the commit message. + printf("-- \ncgit %s\n\n", version); } } diff --git a/source/ui-tree.c b/source/ui-tree.c index 13a9787..1d8bb3d 100644 --- a/source/ui-tree.c +++ b/source/ui-tree.c @@ -113,7 +113,7 @@ static void print_text_buffer(const char *filename, char *buf, size_t size) } html("<td class='lines'><pre><code>"); - html_txt(buf); + html_blob_txt(buf, size); html("</code></pre></td></tr></table>\n"); } @@ -188,7 +188,7 @@ static bool print_object(const struct object_id *oid, const char *path, return false; } // buffer_is_binary only sniffs the front of the blob, and a NUL past - // that window would end the escaped text render early while the line + // that window would end a Lua filter's output early while the line // number column still counts the whole file, so check the rest too. is_binary = buffer_is_binary(buf, size) || memchr(buf, 0, size); |
