Diff options
| -rw-r--r-- | source/ui-tree.c | 41 | |
| -rwxr-xr-x | tests/t0104-tree.sh | 29 |
2 files changed, 66 insertions, 4 deletions
diff --git a/source/ui-tree.c b/source/ui-tree.c index 8e17cc4..63b8984 100644 --- a/source/ui-tree.c +++ b/source/ui-tree.c @@ -17,6 +17,8 @@ #include "ui-shared.h" #include "ui-tree.h" +#include <odb/streaming.h> + #define HEXDUMP_ROW_BYTES 32 #define HEXDUMP_ROW_HALF (HEXDUMP_ROW_BYTES / 2) #define HEXDUMP_GAP_WIDTH 4 @@ -25,6 +27,10 @@ // max-blob-size would make a page several times its own size. #define HEXDUMP_MAX_BYTES (64 * 1024) +// How far into a buffer buffer_is_binary looks, the window git judges a file +// binary by. +#define BINARY_SNIFF_BYTES 8000 + // How many single-child directories a listing row follows into one path. #define MAX_DIR_CHAIN_LEVELS 15 @@ -147,7 +153,7 @@ static void print_binary_buffer(char *buf, size_t size, const char *path, const * false means a complete error page went out in place of the blob, so nothing * may be added to it. */ -static bool print_object(const struct object_id *oid, const char *path, +static bool print_object(const struct object_id *oid, unsigned mode, const char *path, const char *filename, const char *rev) { enum object_type type; @@ -182,7 +188,7 @@ static bool print_object(const struct object_id *oid, const char *path, cgit_print_layout_start(); htmlf("<div class='blob-header'>blob: %s (", oid_to_hex(oid)); cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path); - if (ctx.repo->enable_blame && !is_binary) { + if (ctx.repo->enable_blame && S_ISREG(mode) && !is_binary) { html(") ("); cgit_blame_link("blame", NULL, NULL, ctx.qry.head, rev, path); } @@ -259,6 +265,26 @@ static void print_dir_chain(const struct object_id *oid, const char *name, strbuf_setlen(fullpath, initial_length); } +/* + * Whether git counts the blob as binary, read through a stream so that only + * the bytes buffer_is_binary looks at are inflated, however large the blob. + */ +static bool blob_starts_binary(const struct object_id *oid) +{ + char buf[BINARY_SNIFF_BYTES]; + struct odb_stream *stream; + size_t got = 0; + ssize_t n; + + stream = odb_stream_from_object(the_repository->objects, oid, NULL); + if (!stream) + return false; + while (got < sizeof(buf) && (n = odb_stream_read(stream, buf + got, sizeof(buf) - got)) > 0) + got += n; + odb_stream_close(stream); + return buffer_is_binary(buf, got); +} + static void print_ls_row(const struct object_id *oid, const char *name, unsigned mode, struct walk_tree_context *walk) { @@ -345,7 +371,14 @@ static void print_ls_row(const struct object_id *oid, const char *name, cgit_stats_link("stats", NULL, "button", ctx.qry.head, fullpath.buf); if (!S_ISGITLINK(mode)) cgit_plain_link("plain", NULL, "button", ctx.qry.head, walk->rev, fullpath.buf); - if (!S_ISDIR(mode) && !S_ISGITLINK(mode) && ctx.repo->enable_blame) + // The blame page refuses a binary file and one past max-blob-size, so + // neither is offered it. + if ( + S_ISREG(mode) && + ctx.repo->enable_blame && + !cgit_blob_too_large(size) && + !blob_starts_binary(oid) + ) cgit_blame_link("blame", NULL, "button", ctx.qry.head, walk->rev, fullpath.buf); html("</td></tr>\n"); @@ -438,7 +471,7 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base, ls_head(); return READ_TREE_RECURSIVE; } else { - bool shown = print_object(oid, fullpath.buf, pathname, walk->rev); + bool shown = print_object(oid, mode, fullpath.buf, pathname, walk->rev); walk->state = shown ? WALK_BLOB_SHOWN : WALK_ERROR_SHOWN; strbuf_release(&fullpath); diff --git a/tests/t0104-tree.sh b/tests/t0104-tree.sh index 490abb3..ca29bdc 100755 --- a/tests/t0104-tree.sh +++ b/tests/t0104-tree.sh @@ -190,6 +190,17 @@ test_expect_success 'only a link target inside the tree is linked' ' ! grep "href=./links/tree/etc/passwd" tmp ' +# Blame takes a regular file alone, so a symlink is not offered it. +test_expect_success 'a symlink is not offered blame' ' + cgitrc_repo linksblamerc links enable-blame=1 && + cgit_rc linksblamerc "url=links/tree/" >tmp && + grep "href=./links/blame/target.txt.>blame</a>" tmp && + ! grep "href=./links/blame/rel.>blame</a>" tmp && + cgit_rc linksblamerc "url=links/tree/rel" >tmp && + grep "<div class=.blob-header.>" tmp && + ! grep ">blame</a>" tmp +' + # A row follows a chain of single directories for a set number of levels and # stops on the last one it links, with no separator left hanging after it. test_expect_success 'a long directory chain ends on a link' ' @@ -265,4 +276,22 @@ test_expect_success 'a large binary is dumped only in part' ' grep "<div class=.truncated.>Only the first 64 KB of this file are shown. <a href=./bin/plain/big.bin.>View the whole file</a>.</div>" tmp ' +# The blame page refuses a file git counts as binary and one past +# max-blob-size, so a listing offers blame for neither. +test_expect_success 'a listing offers blame only where it works' ' + ( + cd repos/bin && + echo hello >small.txt && + test_seq 1 2000 >large.txt && + git add small.txt large.txt && + git commit -m "text files" + ) && + cgitrc_repo binblamerc bin enable-blame=1 max-blob-size=4 && + cgit_rc binblamerc "url=bin/tree/" >tmp && + grep "href=./bin/blame/small.txt.>blame</a>" tmp && + ! grep "href=./bin/blame/large.txt.>blame</a>" tmp && + ! grep "href=./bin/blame/blob.bin.>blame</a>" tmp && + ! grep "href=./bin/blame/big.bin.>blame</a>" tmp +' + test_done |
