From 5870098c4b45c7dace76eadb93a49d0317edb456 Mon Sep 17 00:00:00 2001 From: KatolaZ Date: Sat, 28 Jul 2018 16:07:25 +0100 Subject: tree and file presentation --- cgit_70.mk | 3 +- ui_70-refs.c | 4 +- ui_70-shared.c | 215 ++++++++++++++++++++++++------------- ui_70-shared.h | 6 ++ ui_70-tree.c | 328 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 479 insertions(+), 77 deletions(-) create mode 100644 ui_70-tree.c diff --git a/cgit_70.mk b/cgit_70.mk index 4e6857a..ab4a3ae 100644 --- a/cgit_70.mk +++ b/cgit_70.mk @@ -100,7 +100,8 @@ CGIT_OBJ_NAMES += ui-stats.o ##CGIT_OBJ_NAMES += ui-summary.o CGIT_OBJ_NAMES += ui_70-summary.o CGIT_OBJ_NAMES += ui-tag.o -CGIT_OBJ_NAMES += ui-tree.o +##CGIT_OBJ_NAMES += ui-tree.o +CGIT_OBJ_NAMES += ui_70-tree.o CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES)) diff --git a/ui_70-refs.c b/ui_70-refs.c index da18f8a..6b7c900 100644 --- a/ui_70-refs.c +++ b/ui_70-refs.c @@ -71,10 +71,10 @@ static int print_branch(struct refinfo *ref) html(""); cgit_object_link(ref->object); } - cgit_gopher_text("\t"); + cgit_gopher_tab(); cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL, ctx.qry.showmsg, 0); - cgit_gopher_text("\t"); + cgit_gopher_tab(); cgit_gopher_end_selector(); return 0; } diff --git a/ui_70-shared.c b/ui_70-shared.c index 30a1eaf..b5a6fa8 100644 --- a/ui_70-shared.c +++ b/ui_70-shared.c @@ -73,6 +73,28 @@ void gopher_vtxtf(const char *format, va_list ap) strbuf_release(&buf); } + +void gopherf(const char *format, ...) +{ + va_list args; + struct strbuf buf = STRBUF_INIT; + + va_start(args, format); + strbuf_vaddf(&buf, format, args); + va_end(args); + cgit_gopher_text(buf.buf); + strbuf_release(&buf); +} + + + +void gopher_fileperm(unsigned short mode) +{ + gopherf("%c%c%c", (mode & 4 ? 'r' : '-'), + (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-')); +} + + void cgit_gopher_textf(const char *fmt, va_list ap){ va_list cp; @@ -320,7 +342,7 @@ static void site_url(const char *page, const char *search, const char *sort, int } if (page) { - cgit_gopher_textf("?p=%s", page); + gopherf("?p=%s", page); delim = "&"; } if (search) { @@ -337,7 +359,7 @@ static void site_url(const char *page, const char *search, const char *sort, int } if (ofs) { cgit_gopher_text(delim); - cgit_gopher_textf("ofs=%d", ofs); + gopherf("ofs=%d", ofs); } } @@ -477,9 +499,21 @@ void cgit_tag_link(const char *name, const char *title, const char *class, void cgit_tree_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path) { + reporevlink("tree", name, title, class, head, rev, path); } + +void cgit_gopher_tree_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + cgit_gopher_start_selector(GOPHER_MENU); + cgit_gopher_text(name); + cgit_gopher_tab(); + cgit_tree_link(name, title, class, head, rev, path); + cgit_gopher_end_selector(); +} + void cgit_plain_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path) { @@ -518,7 +552,7 @@ void cgit_log_link(const char *name, const char *title, const char *class, if (ofs > 0) { cgit_gopher_text(delim); cgit_gopher_text("ofs="); - cgit_gopher_textf("%d", ofs); + gopherf("%d", ofs); delim = "&"; } if (showmsg) { @@ -530,6 +564,21 @@ void cgit_log_link(const char *name, const char *title, const char *class, cgit_gopher_text(delim); cgit_gopher_text("follow=1"); } + cgit_gopher_tab(); +} + + +void cgit_gopher_log_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path, + int ofs, const char *grep, const char *pattern, int showmsg, + int follow) +{ + cgit_gopher_start_selector(GOPHER_MENU); + cgit_gopher_text(name); + cgit_gopher_tab(); + cgit_log_link(name, title, class, head, rev, path, ofs, grep, pattern, showmsg, follow); + cgit_gopher_end_selector(); + } void cgit_commit_link(const char *name, const char *title, const char *class, @@ -539,50 +588,64 @@ void cgit_commit_link(const char *name, const char *title, const char *class, delim = repolink(title, class, "commit", head, path); if (rev && ctx.qry.head && strcmp(rev, ctx.qry.head)) { - html(delim); - html("id="); - html_url_arg(rev); - delim = "&"; + cgit_gopher_text(delim); + cgit_gopher_text("id="); + cgit_gopher_text(rev); + delim = "&"; } if (ctx.qry.difftype) { - html(delim); - htmlf("dt=%d", ctx.qry.difftype); - delim = "&"; + cgit_gopher_text(delim); + gopherf("dt=%d", ctx.qry.difftype); + delim = "&"; } if (ctx.qry.context > 0 && ctx.qry.context != 3) { - html(delim); - html("context="); - htmlf("%d", ctx.qry.context); - delim = "&"; + cgit_gopher_text(delim); + cgit_gopher_text("context="); + gopherf("%d", ctx.qry.context); + delim = "&"; } if (ctx.qry.ignorews) { - html(delim); - html("ignorews=1"); - delim = "&"; + cgit_gopher_text(delim); + cgit_gopher_text("ignorews=1"); + delim = "&"; } if (ctx.qry.follow) { - html(delim); - html("follow=1"); + cgit_gopher_text(delim); + cgit_gopher_text("follow=1"); } - html("'>"); if (name[0] != '\0') { - if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { - html_ntxt(name, ctx.cfg.max_msg_len - 3); - html("..."); - } else - html_txt(name); + cgit_gopher_text(name); } else - html_txt("(no commit message)"); - html(""); + cgit_gopher_text("(no commit message)"); + cgit_gopher_tab(); } + + +void cgit_gopher_commit_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) +{ + cgit_gopher_start_selector(GOPHER_MENU); + cgit_gopher_text(name); + cgit_gopher_tab(); + cgit_commit_link(name, title, class, head, rev, path); + cgit_gopher_end_selector(); +} + void cgit_refs_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path) +{ + reporevlink("refs", name, title, class, head, rev, path); +} + + +void cgit_gopher_refs_link(const char *name, const char *title, const char *class, + const char *head, const char *rev, const char *path) { cgit_gopher_start_selector(GOPHER_MENU); cgit_gopher_text(name); cgit_gopher_tab(); - reporevlink("refs", name, title, class, head, rev, path); + cgit_refs_link(name, title, class, head, rev, path); cgit_gopher_end_selector(); } @@ -601,40 +664,52 @@ void cgit_diff_link(const char *name, const char *title, const char *class, delim = repolink(title, class, "diff", head, path); if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) { - html(delim); - html("id="); - html_url_arg(new_rev); - delim = "&"; + cgit_gopher_text(delim); + cgit_gopher_text("id="); + cgit_gopher_text(new_rev); + delim = "&"; } if (old_rev) { - html(delim); - html("id2="); - html_url_arg(old_rev); - delim = "&"; + cgit_gopher_text(delim); + cgit_gopher_text("id2="); + cgit_gopher_text(old_rev); + delim = "&"; } if (ctx.qry.difftype) { - html(delim); - htmlf("dt=%d", ctx.qry.difftype); - delim = "&"; + cgit_gopher_text(delim); + gopherf("dt=%d", ctx.qry.difftype); + delim = "&"; } if (ctx.qry.context > 0 && ctx.qry.context != 3) { - html(delim); - html("context="); - htmlf("%d", ctx.qry.context); - delim = "&"; + cgit_gopher_text(delim); + cgit_gopher_text("context="); + gopherf("%d", ctx.qry.context); + delim = "&"; } if (ctx.qry.ignorews) { - html(delim); - html("ignorews=1"); - delim = "&"; + cgit_gopher_text(delim); + cgit_gopher_text("ignorews=1"); + delim = "&"; } if (ctx.qry.follow) { - html(delim); - html("follow=1"); + cgit_gopher_text(delim); + cgit_gopher_text("follow=1"); } - html("'>"); - html_txt(name); - html(""); + cgit_gopher_tab(); +} + + +void cgit_gopher_diff_link(const char *name, const char *title, const char *class, + const char *head, const char *new_rev, const char *old_rev, + const char *path) +{ + + cgit_gopher_start_selector(GOPHER_MENU); + cgit_gopher_text(name); + cgit_gopher_tab(); + cgit_diff_link(name, title, class, head, new_rev, old_rev, path); + cgit_gopher_end_selector(); + } void cgit_patch_link(const char *name, const char *title, const char *class, @@ -962,7 +1037,7 @@ void cgit_print_layout_start(void) void cgit_print_layout_end(void) { - cgit_print_docend(); + /*cgit_print_docend();*/ } static void add_clone_urls(void (*fn)(const char *), char *txt, char *suffix) @@ -1091,20 +1166,20 @@ void cgit_print_pageheader(void) NULL); cgit_summary_link("summary", NULL, hc("summary"), ctx.qry.head); - cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head, + cgit_gopher_refs_link("refs", NULL, hc("refs"), ctx.qry.head, ctx.qry.sha1, NULL); - cgit_log_link("log", NULL, hc("log"), ctx.qry.head, + cgit_gopher_log_link("log", NULL, hc("log"), ctx.qry.head, NULL, ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, ctx.qry.follow); if (ctx.qry.page && !strcmp(ctx.qry.page, "blame")) cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath); else - cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head, + cgit_gopher_tree_link("tree", NULL, hc("tree"), ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath); - cgit_commit_link("commit", NULL, hc("commit"), + cgit_gopher_commit_link("commit", NULL, hc("commit"), ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath); - cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head, + cgit_gopher_diff_link("diff", NULL, hc("diff"), ctx.qry.head, ctx.qry.sha1, ctx.qry.sha2, ctx.qry.vpath); if (ctx.repo->max_stats) cgit_stats_link("stats", NULL, hc("stats"), @@ -1114,15 +1189,7 @@ void cgit_print_pageheader(void) html_attr(ctx.repo->homepage); html("'>homepage"); } - html(""); - html("
"); html("path: "); cgit_print_path_crumbs(ctx.qry.vpath); @@ -1158,16 +1225,16 @@ void cgit_print_pageheader(void) void cgit_print_filemode(unsigned short mode) { if (S_ISDIR(mode)) - html("d"); + cgit_gopher_text("d"); else if (S_ISLNK(mode)) - html("l"); + cgit_gopher_text("l"); else if (S_ISGITLINK(mode)) - html("m"); + cgit_gopher_text("m"); else - html("-"); - html_fileperm(mode >> 6); - html_fileperm(mode >> 3); - html_fileperm(mode); + cgit_gopher_text("-"); + gopher_fileperm(mode >> 6); + gopher_fileperm(mode >> 3); + gopher_fileperm(mode); } void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base, diff --git a/ui_70-shared.h b/ui_70-shared.h index 7dd2d35..8abc3c6 100644 --- a/ui_70-shared.h +++ b/ui_70-shared.h @@ -24,6 +24,7 @@ #define GOPHER_SUMMARY_DATE_LEN 20 #define GOPHER_SUMMARY_AUTH_LEN 20 #define GOPHER_SUMMARY_AGE_LEN 10 +#define GOPHER_SUMMARY_MODE_LEN 12 #define GOPHER_PAD_CHAR ' ' @@ -39,6 +40,7 @@ void cgit_gopher_start_selector(char type); void cgit_gopher_selector_descr(const char *descr); void cgit_gopher_selector_link(const char *sel); void cgit_gopher_text(const char *txt); +void cgit_gopher_tab(); void cgit_gopher_text_pad(const char *txt, int len); void cgit_gopher_end_selector(); @@ -76,6 +78,10 @@ extern void cgit_log_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path, int ofs, const char *grep, const char *pattern, int showmsg, int follow); +extern void cgit_gopher_log_link(const char *name, const char *title, + const char *class, const char *head, const char *rev, + const char *path, int ofs, const char *grep, + const char *pattern, int showmsg, int follow); extern void cgit_commit_link(const char *name, const char *title, const char *class, const char *head, const char *rev, const char *path); diff --git a/ui_70-tree.c b/ui_70-tree.c new file mode 100644 index 0000000..ecef53e --- /dev/null +++ b/ui_70-tree.c @@ -0,0 +1,328 @@ +/* ui-tree.c: functions for tree output + * + * Copyright (C) 2006-2017 cgit Development Team + * + * Licensed under GNU General Public License v2 + * (see COPYING for full license text) + */ + +#include "cgit.h" +#include "ui-tree.h" +#include "html.h" +#include "ui_70-shared.h" + +struct walk_tree_context { + char *curr_rev; + char *match_path; + int state; +}; + +static void print_text_buffer(const char *name, char *buf, unsigned long size) +{ + cgit_gopher_text(buf); +} + +#define ROWLEN 32 + +static void print_binary_buffer(char *buf, unsigned long size) +{ + unsigned long ofs, idx; + static char ascii[ROWLEN + 1]; + + html("\n"); + html(""); + for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { + htmlf("\n"); + } + html("
ofshex dumpascii
%04lx", ofs); + for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) + htmlf("%*s%02x", + idx == 16 ? 4 : 1, "", + buf[idx] & 0xff); + html(" "); + for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) + ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; + ascii[idx] = '\0'; + html_txt(ascii); + html("
\n"); +} + +static void print_object(const struct object_id *oid, 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_gopher_error("Bad object name"); + return; + } + + buf = read_object_file(oid, &type, &size); + if (!buf) { + cgit_gopher_error("Error reading object"); + return; + } + + cgit_set_title_from_path(path); + + /*cgit_print_layout_start();*/ + + 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; + char *name; + size_t count; +}; + +static int single_tree_cb(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, int stage, + void *cbdata) +{ + struct single_tree_ctx *ctx = cbdata; + + if (++ctx->count > 1) + return -1; + + if (!S_ISDIR(mode)) { + ctx->count = 2; + return -1; + } + + ctx->name = xstrdup(pathname); + oidcpy(&ctx->oid, oid); + strbuf_addf(ctx->path, "/%s", pathname); + return 0; +} + +static void write_tree_link(const struct object_id *oid, char *name, + char *rev, struct strbuf *fullpath) +{ + size_t initial_length = fullpath->len; + struct tree *tree; + struct single_tree_ctx tree_ctx = { + .path = fullpath, + .count = 1, + }; + struct pathspec paths = { + .nr = 0 + }; + + oidcpy(&tree_ctx.oid, oid); + + while (tree_ctx.count == 1) { + cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev, + fullpath->buf); + + tree = lookup_tree(&tree_ctx.oid); + if (!tree) + return; + + free(tree_ctx.name); + tree_ctx.name = NULL; + tree_ctx.count = 0; + + read_tree_recursive(tree, "", 0, 1, &paths, single_tree_cb, + &tree_ctx); + + if (tree_ctx.count != 1) + break; + + html(" / "); + name = tree_ctx.name; + } + + strbuf_setlen(fullpath, initial_length); +} + +static int ls_item(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, int stage, void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + char *name; + struct strbuf fullpath = STRBUF_INIT; + struct strbuf class = STRBUF_INIT; + enum object_type type; + unsigned long size = 0; + + name = xstrdup(pathname); + strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "", + ctx.qry.path ? "/" : "", name); + + if (!S_ISGITLINK(mode)) { + type = oid_object_info(the_repository, oid, &size); + if (type == OBJ_BAD) { + cgit_gopher_error("Bad object"); + free(name); + return 0; + } + } + + cgit_gopher_start_selector(GOPHER_TXT); + cgit_print_filemode(mode); + cgit_gopher_text(" "); + cgit_gopher_text_pad(name, GOPHER_SUMMARY_NAME_LEN); + gopherf("%10ld", size); + cgit_gopher_tab(); + + if (S_ISGITLINK(mode)) { + cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid)); + } else if (S_ISDIR(mode)) { + write_tree_link(oid, name, walk_tree_ctx->curr_rev, + &fullpath); + } else { + char *ext = strrchr(name, '.'); + strbuf_addstr(&class, "ls-blob"); + if (ext) + strbuf_addf(&class, " %s", ext + 1); + cgit_tree_link(name, NULL, class.buf, ctx.qry.head, + walk_tree_ctx->curr_rev, fullpath.buf); + } +/* if (ctx.repo->max_stats) + 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_tree_ctx->curr_rev, fullpath.buf); + if (!S_ISDIR(mode) && ctx.cfg.enable_blame) + cgit_blame_link("blame", NULL, "button", ctx.qry.head, + walk_tree_ctx->curr_rev, fullpath.buf); +*/ + cgit_gopher_tab(); + cgit_gopher_end_selector(); + free(name); + strbuf_release(&fullpath); + strbuf_release(&class); + return 0; +} + +static void ls_head(void) +{ + cgit_print_layout_start(); + cgit_gopher_start_selector(GOPHER_INFO); + cgit_gopher_text_pad("Mode", GOPHER_SUMMARY_MODE_LEN); + cgit_gopher_text_pad("Name", GOPHER_SUMMARY_NAME_LEN); + cgit_gopher_text_pad("Size", GOPHER_SUMMARY_AGE_LEN); + cgit_gopher_tab(); + cgit_gopher_selector_link("Err"); + cgit_gopher_end_selector(); +} + +static void ls_tail(void) +{ +/* html("\n"); + cgit_print_layout_end(); +*/ +} + +static void ls_tree(const struct object_id *oid, char *path, struct walk_tree_context *walk_tree_ctx) +{ + struct tree *tree; + struct pathspec paths = { + .nr = 0 + }; + + tree = parse_tree_indirect(oid); + if (!tree) { + cgit_gopher_error("Not a tree object"); + return; + } + + ls_head(); + read_tree_recursive(tree, "", 0, 1, &paths, ls_item, walk_tree_ctx); + ls_tail(); +} + + +static int walk_tree(const struct object_id *oid, struct strbuf *base, + const char *pathname, unsigned mode, int stage, void *cbdata) +{ + struct walk_tree_context *walk_tree_ctx = cbdata; + + if (walk_tree_ctx->state == 0) { + struct strbuf buffer = STRBUF_INIT; + + strbuf_addbuf(&buffer, base); + strbuf_addstr(&buffer, pathname); + if (strcmp(walk_tree_ctx->match_path, buffer.buf)) + return READ_TREE_RECURSIVE; + + if (S_ISDIR(mode)) { + walk_tree_ctx->state = 1; + cgit_set_title_from_path(buffer.buf); + strbuf_release(&buffer); + ls_head(); + return READ_TREE_RECURSIVE; + } else { + walk_tree_ctx->state = 2; + print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev); + strbuf_release(&buffer); + return 0; + } + } + ls_item(oid, base, pathname, mode, stage, walk_tree_ctx); + return 0; +} + +/* + * Show a tree or a blob + * rev: the commit pointing at the root tree object + * path: path to tree or blob + */ +void cgit_print_tree(const char *rev, char *path) +{ + struct object_id oid; + struct commit *commit; + struct pathspec_item path_items = { + .match = path, + .len = path ? strlen(path) : 0 + }; + struct pathspec paths = { + .nr = path ? 1 : 0, + .items = &path_items + }; + struct walk_tree_context walk_tree_ctx = { + .match_path = path, + .state = 0 + }; + + if (!rev) + rev = ctx.qry.head; + + if (get_oid(rev, &oid)) { + cgit_gopher_error("Invalid revision name"); + return; + } + commit = lookup_commit_reference(&oid); + if (!commit || parse_commit(commit)) { + cgit_gopher_error("Invalid commit reference"); + return; + } + + walk_tree_ctx.curr_rev = xstrdup(rev); + + if (path == NULL) { + ls_tree(&commit->maybe_tree->object.oid, NULL, &walk_tree_ctx); + goto cleanup; + } + + read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); + if (walk_tree_ctx.state == 1) + ls_tail(); + else if (walk_tree_ctx.state == 2) + ; + else + cgit_print_error_page(404, "Not found", "Path not found"); + +cleanup: + free(walk_tree_ctx.curr_rev); +} -- cgit v1.2.3