+ unsigned long lineno, idx;
+ const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
+
+ html("<table summary='blob content' class='blob'>\n");
+
+ if (ctx.cfg.enable_tree_linenumbers) {
+ html("<tr><td class='linenumbers'><pre>");
+ idx = 0;
+ lineno = 0;
+
+ if (size) {
+ htmlf(numberfmt, ++lineno);
+ while (idx < size - 1) { // skip absolute last newline
+ if (buf[idx] == '\n')
+ htmlf(numberfmt, ++lineno);
+ idx++;
+ }
+ }
+ html("</pre></td>\n");
+ }
+ else {
+ html("<tr>\n");
+ }
+
+ if (ctx.repo->source_filter) {
+ char *filter_arg = xstrdup(name);
+ html("<td class='lines'><pre><code>");
+ cgit_open_filter(ctx.repo->source_filter, filter_arg);
+ html_raw(buf, size);
+ cgit_close_filter(ctx.repo->source_filter);
+ free(filter_arg);
+ html("</code></pre></td></tr></table>\n");
+ return;
+ }
+
+ html("<td class='lines'><pre><code>");
+ html_txt(buf);
+ html("</code></pre></td></tr></table>\n");
+}
+
+#define ROWLEN 32
+
+static void print_binary_buffer(char *buf, unsigned long size)
+{
+ unsigned long ofs, idx;
+ static char ascii[ROWLEN + 1];
+
+ html("<table summary='blob content' class='bin-blob'>\n");
+ html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>");
+ for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) {
+ htmlf("<tr><td class='right'>%04lx</td><td class='hex'>", ofs);
+ for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
+ htmlf("%*s%02x",
+ idx == 16 ? 4 : 1, "",
+ buf[idx] & 0xff);
+ html(" </td><td class='hex'>");
+ for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
+ ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.';
+ ascii[idx] = '\0';
+ html_txt(ascii);
+ html("</td></tr>\n");
+ }
+ html("</table>\n");
+}
+
+static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev)
+{
+ enum object_type type;
+ char *buf;
+ unsigned long size;
+
+ type = oid_object_info(the_repository, oid, &size);
+ if (type == OBJ_BAD) {
+ cgit_print_error_page(404, "Not found",
+ "Bad object name: %s", oid_to_hex(oid));
+ return;
+ }
+
+ buf = read_object_file(oid, &type, &size);
+ if (!buf) {
+ cgit_print_error_page(500, "Internal server error",
+ "Error reading object %s", oid_to_hex(oid));
+ return;
+ }
+
+ cgit_set_title_from_path(path);
+
+ cgit_print_layout_start();
+ htmlf("blob: %s (", oid_to_hex(oid));
+ cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
+ rev, path);
+ if (ctx.repo->enable_blame) {
+ html(") (");
+ cgit_blame_link("blame", NULL, NULL, ctx.qry.head,
+ rev, path);
+ }
+ html(")\n");
+
+ if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
+ htmlf("<div class='error'>blob size (%ldKB) exceeds display size limit (%dKB).</div>",
+ size / 1024, ctx.cfg.max_blob_size);
+ return;
+ }
+
+ if (buffer_is_binary(buf, size))
+ print_binary_buffer(buf, size);
+ else
+ print_text_buffer(basename, buf, size);
+
+ free(buf);
+}
+
+struct single_tree_ctx {
+ struct strbuf *path;
+ struct object_id oid;