summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-11-29 17:39:41 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-11-29 17:39:41 (UTC)
commit0274b57d55a12ed38259757dbfae96b79cfa2e0b (patch) (unidiff)
tree34c7204f88168f791ef48f603bb8ab66e9df523c
parent7b5cee65fd9cf31e4f19ce4ff613778cb95512a9 (diff)
downloadcgit-0274b57d55a12ed38259757dbfae96b79cfa2e0b.zip
cgit-0274b57d55a12ed38259757dbfae96b79cfa2e0b.tar.gz
cgit-0274b57d55a12ed38259757dbfae96b79cfa2e0b.tar.bz2
ui-log: add support for showing the full commit message
Some users prefer to see the full message, so to make these users happy the new querystring parameter "showmsg" can be used to print the full commit message per log entry. A link is provided in the log heading to make this function accessible, and all links and forms tries to preserve the users preference. Note: the new link is not displayed on the summary page since the point of the summary page is to be a summary, but it is still obeyed if specified manually. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.h1
-rw-r--r--ui-log.c32
-rw-r--r--ui-refs.c3
-rw-r--r--ui-repolist.c2
-rw-r--r--ui-shared.c12
-rw-r--r--ui-shared.h2
-rw-r--r--ui-tree.c2
8 files changed, 45 insertions, 11 deletions
diff --git a/cgit.c b/cgit.c
index c82587b..db5d342 100644
--- a/cgit.c
+++ b/cgit.c
@@ -109,96 +109,98 @@ void config_cb(const char *name, const char *value)
109 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 109 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
110 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 110 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
111 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 111 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
112 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 112 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
113 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 113 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
114 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 114 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
115 else if (ctx.repo && !strcmp(name, "repo.module-link")) 115 else if (ctx.repo && !strcmp(name, "repo.module-link"))
116 ctx.repo->module_link= xstrdup(value); 116 ctx.repo->module_link= xstrdup(value);
117 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 117 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
118 if (*value == '/') 118 if (*value == '/')
119 ctx.repo->readme = xstrdup(value); 119 ctx.repo->readme = xstrdup(value);
120 else 120 else
121 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 121 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
122 } else if (!strcmp(name, "include")) 122 } else if (!strcmp(name, "include"))
123 parse_configfile(value, config_cb); 123 parse_configfile(value, config_cb);
124} 124}
125 125
126static void querystring_cb(const char *name, const char *value) 126static void querystring_cb(const char *name, const char *value)
127{ 127{
128 if (!strcmp(name,"r")) { 128 if (!strcmp(name,"r")) {
129 ctx.qry.repo = xstrdup(value); 129 ctx.qry.repo = xstrdup(value);
130 ctx.repo = cgit_get_repoinfo(value); 130 ctx.repo = cgit_get_repoinfo(value);
131 } else if (!strcmp(name, "p")) { 131 } else if (!strcmp(name, "p")) {
132 ctx.qry.page = xstrdup(value); 132 ctx.qry.page = xstrdup(value);
133 } else if (!strcmp(name, "url")) { 133 } else if (!strcmp(name, "url")) {
134 ctx.qry.url = xstrdup(value); 134 ctx.qry.url = xstrdup(value);
135 cgit_parse_url(value); 135 cgit_parse_url(value);
136 } else if (!strcmp(name, "qt")) { 136 } else if (!strcmp(name, "qt")) {
137 ctx.qry.grep = xstrdup(value); 137 ctx.qry.grep = xstrdup(value);
138 } else if (!strcmp(name, "q")) { 138 } else if (!strcmp(name, "q")) {
139 ctx.qry.search = xstrdup(value); 139 ctx.qry.search = xstrdup(value);
140 } else if (!strcmp(name, "h")) { 140 } else if (!strcmp(name, "h")) {
141 ctx.qry.head = xstrdup(value); 141 ctx.qry.head = xstrdup(value);
142 ctx.qry.has_symref = 1; 142 ctx.qry.has_symref = 1;
143 } else if (!strcmp(name, "id")) { 143 } else if (!strcmp(name, "id")) {
144 ctx.qry.sha1 = xstrdup(value); 144 ctx.qry.sha1 = xstrdup(value);
145 ctx.qry.has_sha1 = 1; 145 ctx.qry.has_sha1 = 1;
146 } else if (!strcmp(name, "id2")) { 146 } else if (!strcmp(name, "id2")) {
147 ctx.qry.sha2 = xstrdup(value); 147 ctx.qry.sha2 = xstrdup(value);
148 ctx.qry.has_sha1 = 1; 148 ctx.qry.has_sha1 = 1;
149 } else if (!strcmp(name, "ofs")) { 149 } else if (!strcmp(name, "ofs")) {
150 ctx.qry.ofs = atoi(value); 150 ctx.qry.ofs = atoi(value);
151 } else if (!strcmp(name, "path")) { 151 } else if (!strcmp(name, "path")) {
152 ctx.qry.path = trim_end(value, '/'); 152 ctx.qry.path = trim_end(value, '/');
153 } else if (!strcmp(name, "name")) { 153 } else if (!strcmp(name, "name")) {
154 ctx.qry.name = xstrdup(value); 154 ctx.qry.name = xstrdup(value);
155 } else if (!strcmp(name, "mimetype")) { 155 } else if (!strcmp(name, "mimetype")) {
156 ctx.qry.mimetype = xstrdup(value); 156 ctx.qry.mimetype = xstrdup(value);
157 } else if (!strcmp(name, "showmsg")) {
158 ctx.qry.showmsg = atoi(value);
157 } 159 }
158} 160}
159 161
160static void prepare_context(struct cgit_context *ctx) 162static void prepare_context(struct cgit_context *ctx)
161{ 163{
162 memset(ctx, 0, sizeof(ctx)); 164 memset(ctx, 0, sizeof(ctx));
163 ctx->cfg.agefile = "info/web/last-modified"; 165 ctx->cfg.agefile = "info/web/last-modified";
164 ctx->cfg.nocache = 0; 166 ctx->cfg.nocache = 0;
165 ctx->cfg.cache_size = 0; 167 ctx->cfg.cache_size = 0;
166 ctx->cfg.cache_dynamic_ttl = 5; 168 ctx->cfg.cache_dynamic_ttl = 5;
167 ctx->cfg.cache_max_create_time = 5; 169 ctx->cfg.cache_max_create_time = 5;
168 ctx->cfg.cache_repo_ttl = 5; 170 ctx->cfg.cache_repo_ttl = 5;
169 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 171 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
170 ctx->cfg.cache_root_ttl = 5; 172 ctx->cfg.cache_root_ttl = 5;
171 ctx->cfg.cache_static_ttl = -1; 173 ctx->cfg.cache_static_ttl = -1;
172 ctx->cfg.css = "/cgit.css"; 174 ctx->cfg.css = "/cgit.css";
173 ctx->cfg.logo = "/git-logo.png"; 175 ctx->cfg.logo = "/git-logo.png";
174 ctx->cfg.local_time = 0; 176 ctx->cfg.local_time = 0;
175 ctx->cfg.max_repo_count = 50; 177 ctx->cfg.max_repo_count = 50;
176 ctx->cfg.max_commit_count = 50; 178 ctx->cfg.max_commit_count = 50;
177 ctx->cfg.max_lock_attempts = 5; 179 ctx->cfg.max_lock_attempts = 5;
178 ctx->cfg.max_msg_len = 80; 180 ctx->cfg.max_msg_len = 80;
179 ctx->cfg.max_repodesc_len = 80; 181 ctx->cfg.max_repodesc_len = 80;
180 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 182 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
181 ctx->cfg.renamelimit = -1; 183 ctx->cfg.renamelimit = -1;
182 ctx->cfg.robots = "index, nofollow"; 184 ctx->cfg.robots = "index, nofollow";
183 ctx->cfg.root_title = "Git repository browser"; 185 ctx->cfg.root_title = "Git repository browser";
184 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 186 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
185 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 187 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
186 ctx->cfg.summary_branches = 10; 188 ctx->cfg.summary_branches = 10;
187 ctx->cfg.summary_log = 10; 189 ctx->cfg.summary_log = 10;
188 ctx->cfg.summary_tags = 10; 190 ctx->cfg.summary_tags = 10;
189 ctx->page.mimetype = "text/html"; 191 ctx->page.mimetype = "text/html";
190 ctx->page.charset = PAGE_ENCODING; 192 ctx->page.charset = PAGE_ENCODING;
191 ctx->page.filename = NULL; 193 ctx->page.filename = NULL;
192 ctx->page.size = 0; 194 ctx->page.size = 0;
193 ctx->page.modified = time(NULL); 195 ctx->page.modified = time(NULL);
194 ctx->page.expires = ctx->page.modified; 196 ctx->page.expires = ctx->page.modified;
195} 197}
196 198
197struct refmatch { 199struct refmatch {
198 char *req_ref; 200 char *req_ref;
199 char *first_ref; 201 char *first_ref;
200 int match; 202 int match;
201}; 203};
202 204
203int find_current_ref(const char *refname, const unsigned char *sha1, 205int find_current_ref(const char *refname, const unsigned char *sha1,
204 int flags, void *cb_data) 206 int flags, void *cb_data)
diff --git a/cgit.h b/cgit.h
index 91db98a..aab898b 100644
--- a/cgit.h
+++ b/cgit.h
@@ -76,96 +76,97 @@ struct commitinfo {
76 unsigned long author_date; 76 unsigned long author_date;
77 char *committer; 77 char *committer;
78 char *committer_email; 78 char *committer_email;
79 unsigned long committer_date; 79 unsigned long committer_date;
80 char *subject; 80 char *subject;
81 char *msg; 81 char *msg;
82 char *msg_encoding; 82 char *msg_encoding;
83}; 83};
84 84
85struct taginfo { 85struct taginfo {
86 char *tagger; 86 char *tagger;
87 char *tagger_email; 87 char *tagger_email;
88 unsigned long tagger_date; 88 unsigned long tagger_date;
89 char *msg; 89 char *msg;
90}; 90};
91 91
92struct refinfo { 92struct refinfo {
93 const char *refname; 93 const char *refname;
94 struct object *object; 94 struct object *object;
95 union { 95 union {
96 struct taginfo *tag; 96 struct taginfo *tag;
97 struct commitinfo *commit; 97 struct commitinfo *commit;
98 }; 98 };
99}; 99};
100 100
101struct reflist { 101struct reflist {
102 struct refinfo **refs; 102 struct refinfo **refs;
103 int alloc; 103 int alloc;
104 int count; 104 int count;
105}; 105};
106 106
107struct cgit_query { 107struct cgit_query {
108 int has_symref; 108 int has_symref;
109 int has_sha1; 109 int has_sha1;
110 char *raw; 110 char *raw;
111 char *repo; 111 char *repo;
112 char *page; 112 char *page;
113 char *search; 113 char *search;
114 char *grep; 114 char *grep;
115 char *head; 115 char *head;
116 char *sha1; 116 char *sha1;
117 char *sha2; 117 char *sha2;
118 char *path; 118 char *path;
119 char *name; 119 char *name;
120 char *mimetype; 120 char *mimetype;
121 char *url; 121 char *url;
122 int ofs; 122 int ofs;
123 int nohead; 123 int nohead;
124 int showmsg;
124}; 125};
125 126
126struct cgit_config { 127struct cgit_config {
127 char *agefile; 128 char *agefile;
128 char *cache_root; 129 char *cache_root;
129 char *clone_prefix; 130 char *clone_prefix;
130 char *css; 131 char *css;
131 char *favicon; 132 char *favicon;
132 char *footer; 133 char *footer;
133 char *index_header; 134 char *index_header;
134 char *index_info; 135 char *index_info;
135 char *logo; 136 char *logo;
136 char *logo_link; 137 char *logo_link;
137 char *module_link; 138 char *module_link;
138 char *repo_group; 139 char *repo_group;
139 char *robots; 140 char *robots;
140 char *root_title; 141 char *root_title;
141 char *root_desc; 142 char *root_desc;
142 char *root_readme; 143 char *root_readme;
143 char *script_name; 144 char *script_name;
144 char *virtual_root; 145 char *virtual_root;
145 int cache_size; 146 int cache_size;
146 int cache_dynamic_ttl; 147 int cache_dynamic_ttl;
147 int cache_max_create_time; 148 int cache_max_create_time;
148 int cache_repo_ttl; 149 int cache_repo_ttl;
149 int cache_root_ttl; 150 int cache_root_ttl;
150 int cache_static_ttl; 151 int cache_static_ttl;
151 int enable_index_links; 152 int enable_index_links;
152 int enable_log_filecount; 153 int enable_log_filecount;
153 int enable_log_linecount; 154 int enable_log_linecount;
154 int local_time; 155 int local_time;
155 int max_repo_count; 156 int max_repo_count;
156 int max_commit_count; 157 int max_commit_count;
157 int max_lock_attempts; 158 int max_lock_attempts;
158 int max_msg_len; 159 int max_msg_len;
159 int max_repodesc_len; 160 int max_repodesc_len;
160 int nocache; 161 int nocache;
161 int renamelimit; 162 int renamelimit;
162 int snapshots; 163 int snapshots;
163 int summary_branches; 164 int summary_branches;
164 int summary_log; 165 int summary_log;
165 int summary_tags; 166 int summary_tags;
166}; 167};
167 168
168struct cgit_page { 169struct cgit_page {
169 time_t modified; 170 time_t modified;
170 time_t expires; 171 time_t expires;
171 size_t size; 172 size_t size;
diff --git a/ui-log.c b/ui-log.c
index 8dd8b89..631e46d 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,155 +1,177 @@
1/* ui-log.c: functions for log output 1/* ui-log.c: functions for log output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13int files, add_lines, rem_lines; 13int files, add_lines, rem_lines;
14 14
15void count_lines(char *line, int size) 15void count_lines(char *line, int size)
16{ 16{
17 if (size <= 0) 17 if (size <= 0)
18 return; 18 return;
19 19
20 if (line[0] == '+') 20 if (line[0] == '+')
21 add_lines++; 21 add_lines++;
22 22
23 else if (line[0] == '-') 23 else if (line[0] == '-')
24 rem_lines++; 24 rem_lines++;
25} 25}
26 26
27void inspect_files(struct diff_filepair *pair) 27void inspect_files(struct diff_filepair *pair)
28{ 28{
29 files++; 29 files++;
30 if (ctx.repo->enable_log_linecount) 30 if (ctx.repo->enable_log_linecount)
31 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines); 31 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines);
32} 32}
33 33
34void print_commit(struct commit *commit) 34void print_commit(struct commit *commit)
35{ 35{
36 struct commitinfo *info; 36 struct commitinfo *info;
37 char *tmp; 37 char *tmp;
38 38
39 info = cgit_parse_commit(commit); 39 info = cgit_parse_commit(commit);
40 html("<tr><td>"); 40 html("<tr><td>");
41 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); 41 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
42 tmp = cgit_pageurl(ctx.repo->url, "commit", tmp); 42 tmp = cgit_pageurl(ctx.repo->url, "commit", tmp);
43 html_link_open(tmp, NULL, NULL); 43 html_link_open(tmp, NULL, NULL);
44 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 44 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
45 html_link_close(); 45 html_link_close();
46 html("</td><td>"); 46 html("</td><td>");
47 if (ctx.qry.showmsg)
48 html("<u>");
47 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, 49 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
48 sha1_to_hex(commit->object.sha1)); 50 sha1_to_hex(commit->object.sha1));
51 if (ctx.qry.showmsg)
52 html("</u>");
49 html("</td><td>"); 53 html("</td><td>");
50 html_txt(info->author); 54 html_txt(info->author);
51 if (ctx.repo->enable_log_filecount) { 55 if (ctx.repo->enable_log_filecount) {
52 files = 0; 56 files = 0;
53 add_lines = 0; 57 add_lines = 0;
54 rem_lines = 0; 58 rem_lines = 0;
55 cgit_diff_commit(commit, inspect_files); 59 cgit_diff_commit(commit, inspect_files);
56 html("</td><td>"); 60 html("</td><td>");
57 htmlf("%d", files); 61 htmlf("%d", files);
58 if (ctx.repo->enable_log_linecount) { 62 if (ctx.repo->enable_log_linecount) {
59 html("</td><td>"); 63 html("</td><td>");
60 htmlf("-%d/+%d", rem_lines, add_lines); 64 htmlf("-%d/+%d", rem_lines, add_lines);
61 } 65 }
62 } 66 }
63 html("</td></tr>\n"); 67 html("</td></tr>\n");
68 if (ctx.qry.showmsg) {
69 html("<tr class='nohover'><td></td><td><div class='commit-msg'>");
70 html_txt(info->msg);
71 html("</div><br/></td><td></td>");
72 if (ctx.repo->enable_log_filecount) {
73 html("<td></td>");
74 if (ctx.repo->enable_log_linecount)
75 html("<td></td>");
76 }
77 html("</tr>\n");
78 }
64 cgit_free_commitinfo(info); 79 cgit_free_commitinfo(info);
65} 80}
66 81
67 82
68void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, 83void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
69 char *path, int pager) 84 char *path, int pager)
70{ 85{
71 struct rev_info rev; 86 struct rev_info rev;
72 struct commit *commit; 87 struct commit *commit;
73 const char *argv[] = {NULL, tip, NULL, NULL, NULL}; 88 const char *argv[] = {NULL, tip, NULL, NULL, NULL};
74 int argc = 2; 89 int argc = 2;
75 int i, columns = 3; 90 int i, columns = 3;
76 91
77 if (!tip) 92 if (!tip)
78 argv[1] = ctx.qry.head; 93 argv[1] = ctx.qry.head;
79 94
80 if (grep && pattern && (!strcmp(grep, "grep") || 95 if (grep && pattern && (!strcmp(grep, "grep") ||
81 !strcmp(grep, "author") || 96 !strcmp(grep, "author") ||
82 !strcmp(grep, "committer"))) 97 !strcmp(grep, "committer")))
83 argv[argc++] = fmt("--%s=%s", grep, pattern); 98 argv[argc++] = fmt("--%s=%s", grep, pattern);
84 99
85 if (path) { 100 if (path) {
86 argv[argc++] = "--"; 101 argv[argc++] = "--";
87 argv[argc++] = path; 102 argv[argc++] = path;
88 } 103 }
89 init_revisions(&rev, NULL); 104 init_revisions(&rev, NULL);
90 rev.abbrev = DEFAULT_ABBREV; 105 rev.abbrev = DEFAULT_ABBREV;
91 rev.commit_format = CMIT_FMT_DEFAULT; 106 rev.commit_format = CMIT_FMT_DEFAULT;
92 rev.verbose_header = 1; 107 rev.verbose_header = 1;
93 rev.show_root_diff = 0; 108 rev.show_root_diff = 0;
94 setup_revisions(argc, argv, &rev, NULL); 109 setup_revisions(argc, argv, &rev, NULL);
95 rev.grep_filter.regflags |= REG_ICASE; 110 rev.grep_filter.regflags |= REG_ICASE;
96 compile_grep_patterns(&rev.grep_filter); 111 compile_grep_patterns(&rev.grep_filter);
97 prepare_revision_walk(&rev); 112 prepare_revision_walk(&rev);
98 113
99 if (pager) 114 if (pager)
100 html("<table class='list nowrap'>"); 115 html("<table class='list nowrap'>");
101 116
102 html("<tr class='nohover'><th class='left'>Age</th>" 117 html("<tr class='nohover'><th class='left'>Age</th>"
103 "<th class='left'>Commit message</th>" 118 "<th class='left'>Commit message");
104 "<th class='left'>Author</th>"); 119 if (pager) {
120 html(" (");
121 cgit_log_link("toggle", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
122 ctx.qry.path, ctx.qry.ofs, ctx.qry.grep,
123 ctx.qry.search, ctx.qry.showmsg ? 0 : 1);
124 html(")");
125 }
126 html("</th><th class='left'>Author</th>");
105 if (ctx.repo->enable_log_filecount) { 127 if (ctx.repo->enable_log_filecount) {
106 html("<th class='left'>Files</th>"); 128 html("<th class='left'>Files</th>");
107 columns++; 129 columns++;
108 if (ctx.repo->enable_log_linecount) { 130 if (ctx.repo->enable_log_linecount) {
109 html("<th class='left'>Lines</th>"); 131 html("<th class='left'>Lines</th>");
110 columns++; 132 columns++;
111 } 133 }
112 } 134 }
113 html("</tr>\n"); 135 html("</tr>\n");
114 136
115 if (ofs<0) 137 if (ofs<0)
116 ofs = 0; 138 ofs = 0;
117 139
118 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { 140 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
119 free(commit->buffer); 141 free(commit->buffer);
120 commit->buffer = NULL; 142 commit->buffer = NULL;
121 free_commit_list(commit->parents); 143 free_commit_list(commit->parents);
122 commit->parents = NULL; 144 commit->parents = NULL;
123 } 145 }
124 146
125 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { 147 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
126 print_commit(commit); 148 print_commit(commit);
127 free(commit->buffer); 149 free(commit->buffer);
128 commit->buffer = NULL; 150 commit->buffer = NULL;
129 free_commit_list(commit->parents); 151 free_commit_list(commit->parents);
130 commit->parents = NULL; 152 commit->parents = NULL;
131 } 153 }
132 if (pager) { 154 if (pager) {
133 htmlf("</table><div class='pager'>", 155 htmlf("</table><div class='pager'>",
134 columns); 156 columns);
135 if (ofs > 0) { 157 if (ofs > 0) {
136 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, 158 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
137 ctx.qry.sha1, ctx.qry.path, 159 ctx.qry.sha1, ctx.qry.path,
138 ofs - cnt, ctx.qry.grep, 160 ofs - cnt, ctx.qry.grep,
139 ctx.qry.search); 161 ctx.qry.search, ctx.qry.showmsg);
140 html("&nbsp;"); 162 html("&nbsp;");
141 } 163 }
142 if ((commit = get_revision(&rev)) != NULL) { 164 if ((commit = get_revision(&rev)) != NULL) {
143 cgit_log_link("[next]", NULL, NULL, ctx.qry.head, 165 cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
144 ctx.qry.sha1, ctx.qry.path, 166 ctx.qry.sha1, ctx.qry.path,
145 ofs + cnt, ctx.qry.grep, 167 ofs + cnt, ctx.qry.grep,
146 ctx.qry.search); 168 ctx.qry.search, ctx.qry.showmsg);
147 } 169 }
148 html("</div>"); 170 html("</div>");
149 } else if ((commit = get_revision(&rev)) != NULL) { 171 } else if ((commit = get_revision(&rev)) != NULL) {
150 html("<tr class='nohover'><td colspan='3'>"); 172 html("<tr class='nohover'><td colspan='3'>");
151 cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, NULL, 0, 173 cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, NULL, 0,
152 NULL, NULL); 174 NULL, NULL, ctx.qry.showmsg);
153 html("</td></tr>\n"); 175 html("</td></tr>\n");
154 } 176 }
155} 177}
diff --git a/ui-refs.c b/ui-refs.c
index 32e0429..7eb16d5 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -13,97 +13,98 @@
13static int header; 13static int header;
14 14
15static int cmp_age(int age1, int age2) 15static int cmp_age(int age1, int age2)
16{ 16{
17 if (age1 != 0 && age2 != 0) 17 if (age1 != 0 && age2 != 0)
18 return age2 - age1; 18 return age2 - age1;
19 19
20 if (age1 == 0 && age2 == 0) 20 if (age1 == 0 && age2 == 0)
21 return 0; 21 return 0;
22 22
23 if (age1 == 0) 23 if (age1 == 0)
24 return +1; 24 return +1;
25 25
26 return -1; 26 return -1;
27} 27}
28 28
29static int cmp_ref_name(const void *a, const void *b) 29static int cmp_ref_name(const void *a, const void *b)
30{ 30{
31 struct refinfo *r1 = *(struct refinfo **)a; 31 struct refinfo *r1 = *(struct refinfo **)a;
32 struct refinfo *r2 = *(struct refinfo **)b; 32 struct refinfo *r2 = *(struct refinfo **)b;
33 33
34 return strcmp(r1->refname, r2->refname); 34 return strcmp(r1->refname, r2->refname);
35} 35}
36 36
37static int cmp_branch_age(const void *a, const void *b) 37static int cmp_branch_age(const void *a, const void *b)
38{ 38{
39 struct refinfo *r1 = *(struct refinfo **)a; 39 struct refinfo *r1 = *(struct refinfo **)a;
40 struct refinfo *r2 = *(struct refinfo **)b; 40 struct refinfo *r2 = *(struct refinfo **)b;
41 41
42 return cmp_age(r1->commit->committer_date, r2->commit->committer_date); 42 return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
43} 43}
44 44
45static int cmp_tag_age(const void *a, const void *b) 45static int cmp_tag_age(const void *a, const void *b)
46{ 46{
47 struct refinfo *r1 = *(struct refinfo **)a; 47 struct refinfo *r1 = *(struct refinfo **)a;
48 struct refinfo *r2 = *(struct refinfo **)b; 48 struct refinfo *r2 = *(struct refinfo **)b;
49 49
50 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date); 50 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
51} 51}
52 52
53static int print_branch(struct refinfo *ref) 53static int print_branch(struct refinfo *ref)
54{ 54{
55 struct commitinfo *info = ref->commit; 55 struct commitinfo *info = ref->commit;
56 char *name = (char *)ref->refname; 56 char *name = (char *)ref->refname;
57 57
58 if (!info) 58 if (!info)
59 return 1; 59 return 1;
60 html("<tr><td>"); 60 html("<tr><td>");
61 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL); 61 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL,
62 ctx.qry.showmsg);
62 html("</td><td>"); 63 html("</td><td>");
63 64
64 if (ref->object->type == OBJ_COMMIT) { 65 if (ref->object->type == OBJ_COMMIT) {
65 cgit_commit_link(info->subject, NULL, NULL, name, NULL); 66 cgit_commit_link(info->subject, NULL, NULL, name, NULL);
66 html("</td><td>"); 67 html("</td><td>");
67 html_txt(info->author); 68 html_txt(info->author);
68 html("</td><td colspan='2'>"); 69 html("</td><td colspan='2'>");
69 cgit_print_age(info->commit->date, -1, NULL); 70 cgit_print_age(info->commit->date, -1, NULL);
70 } else { 71 } else {
71 html("</td><td></td><td>"); 72 html("</td><td></td><td>");
72 cgit_object_link(ref->object); 73 cgit_object_link(ref->object);
73 } 74 }
74 html("</td></tr>\n"); 75 html("</td></tr>\n");
75 return 0; 76 return 0;
76} 77}
77 78
78static void print_tag_header() 79static void print_tag_header()
79{ 80{
80 html("<tr class='nohover'><th class='left'>Tag</th>" 81 html("<tr class='nohover'><th class='left'>Tag</th>"
81 "<th class='left'>Reference</th>" 82 "<th class='left'>Reference</th>"
82 "<th class='left'>Author</th>" 83 "<th class='left'>Author</th>"
83 "<th class='left' colspan='2'>Age</th></tr>\n"); 84 "<th class='left' colspan='2'>Age</th></tr>\n");
84 header = 1; 85 header = 1;
85} 86}
86 87
87static int print_tag(struct refinfo *ref) 88static int print_tag(struct refinfo *ref)
88{ 89{
89 struct tag *tag; 90 struct tag *tag;
90 struct taginfo *info; 91 struct taginfo *info;
91 char *name = (char *)ref->refname; 92 char *name = (char *)ref->refname;
92 93
93 if (ref->object->type == OBJ_TAG) { 94 if (ref->object->type == OBJ_TAG) {
94 tag = (struct tag *)ref->object; 95 tag = (struct tag *)ref->object;
95 info = ref->tag; 96 info = ref->tag;
96 if (!tag || !info) 97 if (!tag || !info)
97 return 1; 98 return 1;
98 html("<tr><td>"); 99 html("<tr><td>");
99 cgit_tag_link(name, NULL, NULL, ctx.qry.head, name); 100 cgit_tag_link(name, NULL, NULL, ctx.qry.head, name);
100 html("</td><td>"); 101 html("</td><td>");
101 cgit_object_link(tag->tagged); 102 cgit_object_link(tag->tagged);
102 html("</td><td>"); 103 html("</td><td>");
103 if (info->tagger) 104 if (info->tagger)
104 html(info->tagger); 105 html(info->tagger);
105 html("</td><td colspan='2'>"); 106 html("</td><td colspan='2'>");
106 if (info->tagger_date > 0) 107 if (info->tagger_date > 0)
107 cgit_print_age(info->tagger_date, -1, NULL); 108 cgit_print_age(info->tagger_date, -1, NULL);
108 html("</td></tr>\n"); 109 html("</td></tr>\n");
109 } else { 110 } else {
diff --git a/ui-repolist.c b/ui-repolist.c
index c23232c..5833140 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -101,67 +101,67 @@ void cgit_print_repolist()
101 columns++; 101 columns++;
102 102
103 ctx.page.title = ctx.cfg.root_title; 103 ctx.page.title = ctx.cfg.root_title;
104 cgit_print_http_headers(&ctx); 104 cgit_print_http_headers(&ctx);
105 cgit_print_docstart(&ctx); 105 cgit_print_docstart(&ctx);
106 cgit_print_pageheader(&ctx); 106 cgit_print_pageheader(&ctx);
107 107
108 if (ctx.cfg.index_header) 108 if (ctx.cfg.index_header)
109 html_include(ctx.cfg.index_header); 109 html_include(ctx.cfg.index_header);
110 110
111 html("<table summary='repository list' class='list nowrap'>"); 111 html("<table summary='repository list' class='list nowrap'>");
112 for (i=0; i<cgit_repolist.count; i++) { 112 for (i=0; i<cgit_repolist.count; i++) {
113 ctx.repo = &cgit_repolist.repos[i]; 113 ctx.repo = &cgit_repolist.repos[i];
114 if (!(is_match(ctx.repo) && is_in_url(ctx.repo))) 114 if (!(is_match(ctx.repo) && is_in_url(ctx.repo)))
115 continue; 115 continue;
116 hits++; 116 hits++;
117 if (hits <= ctx.qry.ofs) 117 if (hits <= ctx.qry.ofs)
118 continue; 118 continue;
119 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count) 119 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
120 continue; 120 continue;
121 if (!header++) 121 if (!header++)
122 print_header(columns); 122 print_header(columns);
123 if ((last_group == NULL && ctx.repo->group != NULL) || 123 if ((last_group == NULL && ctx.repo->group != NULL) ||
124 (last_group != NULL && ctx.repo->group == NULL) || 124 (last_group != NULL && ctx.repo->group == NULL) ||
125 (last_group != NULL && ctx.repo->group != NULL && 125 (last_group != NULL && ctx.repo->group != NULL &&
126 strcmp(ctx.repo->group, last_group))) { 126 strcmp(ctx.repo->group, last_group))) {
127 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", 127 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
128 columns); 128 columns);
129 html_txt(ctx.repo->group); 129 html_txt(ctx.repo->group);
130 html("</td></tr>"); 130 html("</td></tr>");
131 last_group = ctx.repo->group; 131 last_group = ctx.repo->group;
132 } 132 }
133 htmlf("<tr><td class='%s'>", 133 htmlf("<tr><td class='%s'>",
134 ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); 134 ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
135 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); 135 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
136 html("</td><td>"); 136 html("</td><td>");
137 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); 137 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
138 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); 138 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
139 html_link_close(); 139 html_link_close();
140 html("</td><td>"); 140 html("</td><td>");
141 html_txt(ctx.repo->owner); 141 html_txt(ctx.repo->owner);
142 html("</td><td>"); 142 html("</td><td>");
143 print_modtime(ctx.repo); 143 print_modtime(ctx.repo);
144 html("</td>"); 144 html("</td>");
145 if (ctx.cfg.enable_index_links) { 145 if (ctx.cfg.enable_index_links) {
146 html("<td>"); 146 html("<td>");
147 cgit_summary_link("summary", NULL, "button", NULL); 147 cgit_summary_link("summary", NULL, "button", NULL);
148 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 148 cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
149 0, NULL, NULL); 149 0, NULL, NULL, ctx.qry.showmsg);
150 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); 150 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
151 html("</td>"); 151 html("</td>");
152 } 152 }
153 html("</tr>\n"); 153 html("</tr>\n");
154 } 154 }
155 html("</table>"); 155 html("</table>");
156 if (!hits) 156 if (!hits)
157 cgit_print_error("No repositories found"); 157 cgit_print_error("No repositories found");
158 else if (hits > ctx.cfg.max_repo_count) 158 else if (hits > ctx.cfg.max_repo_count)
159 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search); 159 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search);
160 cgit_print_docend(); 160 cgit_print_docend();
161} 161}
162 162
163void cgit_print_site_readme() 163void cgit_print_site_readme()
164{ 164{
165 if (ctx.cfg.root_readme) 165 if (ctx.cfg.root_readme)
166 html_include(ctx.cfg.root_readme); 166 html_include(ctx.cfg.root_readme);
167} 167}
diff --git a/ui-shared.c b/ui-shared.c
index 224e5f3..dc39e64 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -236,120 +236,126 @@ static char *repolink(char *title, char *class, char *page, char *head,
236 html(delim); 236 html(delim);
237 html("h="); 237 html("h=");
238 html_url_arg(head); 238 html_url_arg(head);
239 delim = "&amp;"; 239 delim = "&amp;";
240 } 240 }
241 return fmt("%s", delim); 241 return fmt("%s", delim);
242} 242}
243 243
244static void reporevlink(char *page, char *name, char *title, char *class, 244static void reporevlink(char *page, char *name, char *title, char *class,
245 char *head, char *rev, char *path) 245 char *head, char *rev, char *path)
246{ 246{
247 char *delim; 247 char *delim;
248 248
249 delim = repolink(title, class, page, head, path); 249 delim = repolink(title, class, page, head, path);
250 if (rev && strcmp(rev, ctx.qry.head)) { 250 if (rev && strcmp(rev, ctx.qry.head)) {
251 html(delim); 251 html(delim);
252 html("id="); 252 html("id=");
253 html_url_arg(rev); 253 html_url_arg(rev);
254 } 254 }
255 html("'>"); 255 html("'>");
256 html_txt(name); 256 html_txt(name);
257 html("</a>"); 257 html("</a>");
258} 258}
259 259
260void cgit_summary_link(char *name, char *title, char *class, char *head) 260void cgit_summary_link(char *name, char *title, char *class, char *head)
261{ 261{
262 reporevlink(NULL, name, title, class, head, NULL, NULL); 262 reporevlink(NULL, name, title, class, head, NULL, NULL);
263} 263}
264 264
265void cgit_tag_link(char *name, char *title, char *class, char *head, 265void cgit_tag_link(char *name, char *title, char *class, char *head,
266 char *rev) 266 char *rev)
267{ 267{
268 reporevlink("tag", name, title, class, head, rev, NULL); 268 reporevlink("tag", name, title, class, head, rev, NULL);
269} 269}
270 270
271void cgit_tree_link(char *name, char *title, char *class, char *head, 271void cgit_tree_link(char *name, char *title, char *class, char *head,
272 char *rev, char *path) 272 char *rev, char *path)
273{ 273{
274 reporevlink("tree", name, title, class, head, rev, path); 274 reporevlink("tree", name, title, class, head, rev, path);
275} 275}
276 276
277void cgit_plain_link(char *name, char *title, char *class, char *head, 277void cgit_plain_link(char *name, char *title, char *class, char *head,
278 char *rev, char *path) 278 char *rev, char *path)
279{ 279{
280 reporevlink("plain", name, title, class, head, rev, path); 280 reporevlink("plain", name, title, class, head, rev, path);
281} 281}
282 282
283void cgit_log_link(char *name, char *title, char *class, char *head, 283void cgit_log_link(char *name, char *title, char *class, char *head,
284 char *rev, char *path, int ofs, char *grep, char *pattern) 284 char *rev, char *path, int ofs, char *grep, char *pattern,
285 int showmsg)
285{ 286{
286 char *delim; 287 char *delim;
287 288
288 delim = repolink(title, class, "log", head, path); 289 delim = repolink(title, class, "log", head, path);
289 if (rev && strcmp(rev, ctx.qry.head)) { 290 if (rev && strcmp(rev, ctx.qry.head)) {
290 html(delim); 291 html(delim);
291 html("id="); 292 html("id=");
292 html_url_arg(rev); 293 html_url_arg(rev);
293 delim = "&"; 294 delim = "&";
294 } 295 }
295 if (grep && pattern) { 296 if (grep && pattern) {
296 html(delim); 297 html(delim);
297 html("qt="); 298 html("qt=");
298 html_url_arg(grep); 299 html_url_arg(grep);
299 delim = "&"; 300 delim = "&";
300 html(delim); 301 html(delim);
301 html("q="); 302 html("q=");
302 html_url_arg(pattern); 303 html_url_arg(pattern);
303 } 304 }
304 if (ofs > 0) { 305 if (ofs > 0) {
305 html(delim); 306 html(delim);
306 html("ofs="); 307 html("ofs=");
307 htmlf("%d", ofs); 308 htmlf("%d", ofs);
309 delim = "&";
310 }
311 if (showmsg) {
312 html(delim);
313 html("showmsg=1");
308 } 314 }
309 html("'>"); 315 html("'>");
310 html_txt(name); 316 html_txt(name);
311 html("</a>"); 317 html("</a>");
312} 318}
313 319
314void cgit_commit_link(char *name, char *title, char *class, char *head, 320void cgit_commit_link(char *name, char *title, char *class, char *head,
315 char *rev) 321 char *rev)
316{ 322{
317 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { 323 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
318 name[ctx.cfg.max_msg_len] = '\0'; 324 name[ctx.cfg.max_msg_len] = '\0';
319 name[ctx.cfg.max_msg_len - 1] = '.'; 325 name[ctx.cfg.max_msg_len - 1] = '.';
320 name[ctx.cfg.max_msg_len - 2] = '.'; 326 name[ctx.cfg.max_msg_len - 2] = '.';
321 name[ctx.cfg.max_msg_len - 3] = '.'; 327 name[ctx.cfg.max_msg_len - 3] = '.';
322 } 328 }
323 reporevlink("commit", name, title, class, head, rev, NULL); 329 reporevlink("commit", name, title, class, head, rev, NULL);
324} 330}
325 331
326void cgit_refs_link(char *name, char *title, char *class, char *head, 332void cgit_refs_link(char *name, char *title, char *class, char *head,
327 char *rev, char *path) 333 char *rev, char *path)
328{ 334{
329 reporevlink("refs", name, title, class, head, rev, path); 335 reporevlink("refs", name, title, class, head, rev, path);
330} 336}
331 337
332void cgit_snapshot_link(char *name, char *title, char *class, char *head, 338void cgit_snapshot_link(char *name, char *title, char *class, char *head,
333 char *rev, char *archivename) 339 char *rev, char *archivename)
334{ 340{
335 reporevlink("snapshot", name, title, class, head, rev, archivename); 341 reporevlink("snapshot", name, title, class, head, rev, archivename);
336} 342}
337 343
338void cgit_diff_link(char *name, char *title, char *class, char *head, 344void cgit_diff_link(char *name, char *title, char *class, char *head,
339 char *new_rev, char *old_rev, char *path) 345 char *new_rev, char *old_rev, char *path)
340{ 346{
341 char *delim; 347 char *delim;
342 348
343 delim = repolink(title, class, "diff", head, path); 349 delim = repolink(title, class, "diff", head, path);
344 if (new_rev && strcmp(new_rev, ctx.qry.head)) { 350 if (new_rev && strcmp(new_rev, ctx.qry.head)) {
345 html(delim); 351 html(delim);
346 html("id="); 352 html("id=");
347 html_url_arg(new_rev); 353 html_url_arg(new_rev);
348 delim = "&amp;"; 354 delim = "&amp;";
349 } 355 }
350 if (old_rev) { 356 if (old_rev) {
351 html(delim); 357 html(delim);
352 html("id2="); 358 html("id2=");
353 html_url_arg(old_rev); 359 html_url_arg(old_rev);
354 } 360 }
355 html("'>"); 361 html("'>");
@@ -523,163 +529,165 @@ int print_archive_ref(const char *refname, const unsigned char *sha1,
523 if (prefixcmp(refname, "refs/archives")) 529 if (prefixcmp(refname, "refs/archives"))
524 return 0; 530 return 0;
525 strncpy(buf, refname+14, sizeof(buf)); 531 strncpy(buf, refname+14, sizeof(buf));
526 obj = parse_object(sha1); 532 obj = parse_object(sha1);
527 if (!obj) 533 if (!obj)
528 return 1; 534 return 1;
529 if (obj->type == OBJ_TAG) { 535 if (obj->type == OBJ_TAG) {
530 tag = lookup_tag(sha1); 536 tag = lookup_tag(sha1);
531 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 537 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
532 return 0; 538 return 0;
533 hashcpy(fileid, tag->tagged->sha1); 539 hashcpy(fileid, tag->tagged->sha1);
534 } else if (obj->type != OBJ_BLOB) { 540 } else if (obj->type != OBJ_BLOB) {
535 return 0; 541 return 0;
536 } else { 542 } else {
537 hashcpy(fileid, sha1); 543 hashcpy(fileid, sha1);
538 } 544 }
539 if (!*header) { 545 if (!*header) {
540 html("<h1>download</h1>\n"); 546 html("<h1>download</h1>\n");
541 *header = 1; 547 *header = 1;
542 } 548 }
543 url = cgit_pageurl(ctx.qry.repo, "blob", 549 url = cgit_pageurl(ctx.qry.repo, "blob",
544 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 550 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
545 buf)); 551 buf));
546 html_link_open(url, NULL, "menu"); 552 html_link_open(url, NULL, "menu");
547 html_txt(strlpart(buf, 20)); 553 html_txt(strlpart(buf, 20));
548 html_link_close(); 554 html_link_close();
549 return 0; 555 return 0;
550} 556}
551 557
552void add_hidden_formfields(int incl_head, int incl_search, char *page) 558void add_hidden_formfields(int incl_head, int incl_search, char *page)
553{ 559{
554 char *url; 560 char *url;
555 561
556 if (!ctx.cfg.virtual_root) { 562 if (!ctx.cfg.virtual_root) {
557 url = fmt("%s/%s", ctx.qry.repo, page); 563 url = fmt("%s/%s", ctx.qry.repo, page);
558 if (ctx.qry.path) 564 if (ctx.qry.path)
559 url = fmt("%s/%s", url, ctx.qry.path); 565 url = fmt("%s/%s", url, ctx.qry.path);
560 html_hidden("url", url); 566 html_hidden("url", url);
561 } 567 }
562 568
563 if (incl_head && ctx.qry.head && ctx.repo->defbranch && 569 if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
564 strcmp(ctx.qry.head, ctx.repo->defbranch)) 570 strcmp(ctx.qry.head, ctx.repo->defbranch))
565 html_hidden("h", ctx.qry.head); 571 html_hidden("h", ctx.qry.head);
566 572
567 if (ctx.qry.sha1) 573 if (ctx.qry.sha1)
568 html_hidden("id", ctx.qry.sha1); 574 html_hidden("id", ctx.qry.sha1);
569 if (ctx.qry.sha2) 575 if (ctx.qry.sha2)
570 html_hidden("id2", ctx.qry.sha2); 576 html_hidden("id2", ctx.qry.sha2);
577 if (ctx.qry.showmsg)
578 html_hidden("showmsg", "1");
571 579
572 if (incl_search) { 580 if (incl_search) {
573 if (ctx.qry.grep) 581 if (ctx.qry.grep)
574 html_hidden("qt", ctx.qry.grep); 582 html_hidden("qt", ctx.qry.grep);
575 if (ctx.qry.search) 583 if (ctx.qry.search)
576 html_hidden("q", ctx.qry.search); 584 html_hidden("q", ctx.qry.search);
577 } 585 }
578} 586}
579 587
580char *hc(struct cgit_cmd *cmd, const char *page) 588char *hc(struct cgit_cmd *cmd, const char *page)
581{ 589{
582 return (strcmp(cmd->name, page) ? NULL : "active"); 590 return (strcmp(cmd->name, page) ? NULL : "active");
583} 591}
584 592
585void cgit_print_pageheader(struct cgit_context *ctx) 593void cgit_print_pageheader(struct cgit_context *ctx)
586{ 594{
587 struct cgit_cmd *cmd = cgit_get_cmd(ctx); 595 struct cgit_cmd *cmd = cgit_get_cmd(ctx);
588 596
589 html("<table id='header'>\n"); 597 html("<table id='header'>\n");
590 html("<tr>\n"); 598 html("<tr>\n");
591 html("<td class='logo' rowspan='2'><a href='"); 599 html("<td class='logo' rowspan='2'><a href='");
592 if (ctx->cfg.logo_link) 600 if (ctx->cfg.logo_link)
593 html_attr(ctx->cfg.logo_link); 601 html_attr(ctx->cfg.logo_link);
594 else 602 else
595 html_attr(cgit_rooturl()); 603 html_attr(cgit_rooturl());
596 html("'><img src='"); 604 html("'><img src='");
597 html_attr(ctx->cfg.logo); 605 html_attr(ctx->cfg.logo);
598 html("' alt='cgit logo'/></a></td>\n"); 606 html("' alt='cgit logo'/></a></td>\n");
599 607
600 html("<td class='main'>"); 608 html("<td class='main'>");
601 if (ctx->repo) { 609 if (ctx->repo) {
602 cgit_index_link("index", NULL, NULL, NULL, 0); 610 cgit_index_link("index", NULL, NULL, NULL, 0);
603 html(" : "); 611 html(" : ");
604 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL); 612 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL);
605 html("</td><td class='form'>"); 613 html("</td><td class='form'>");
606 html("<form method='get' action=''>\n"); 614 html("<form method='get' action=''>\n");
607 add_hidden_formfields(0, 1, ctx->qry.page); 615 add_hidden_formfields(0, 1, ctx->qry.page);
608 html("<select name='h' onchange='this.form.submit();'>\n"); 616 html("<select name='h' onchange='this.form.submit();'>\n");
609 for_each_branch_ref(print_branch_option, ctx->qry.head); 617 for_each_branch_ref(print_branch_option, ctx->qry.head);
610 html("</select> "); 618 html("</select> ");
611 html("<input type='submit' name='' value='switch'/>"); 619 html("<input type='submit' name='' value='switch'/>");
612 html("</form>"); 620 html("</form>");
613 } else 621 } else
614 html_txt(ctx->cfg.root_title); 622 html_txt(ctx->cfg.root_title);
615 html("</td></tr>\n"); 623 html("</td></tr>\n");
616 624
617 html("<tr><td class='sub'>"); 625 html("<tr><td class='sub'>");
618 if (ctx->repo) { 626 if (ctx->repo) {
619 html_txt(ctx->repo->desc); 627 html_txt(ctx->repo->desc);
620 html("</td><td class='sub right'>"); 628 html("</td><td class='sub right'>");
621 html_txt(ctx->repo->owner); 629 html_txt(ctx->repo->owner);
622 } else { 630 } else {
623 if (ctx->cfg.root_desc) 631 if (ctx->cfg.root_desc)
624 html_txt(ctx->cfg.root_desc); 632 html_txt(ctx->cfg.root_desc);
625 else if (ctx->cfg.index_info) 633 else if (ctx->cfg.index_info)
626 html_include(ctx->cfg.index_info); 634 html_include(ctx->cfg.index_info);
627 } 635 }
628 html("</td></tr></table>\n"); 636 html("</td></tr></table>\n");
629 637
630 html("<table class='tabs'><tr><td>\n"); 638 html("<table class='tabs'><tr><td>\n");
631 if (ctx->repo) { 639 if (ctx->repo) {
632 cgit_summary_link("summary", NULL, hc(cmd, "summary"), 640 cgit_summary_link("summary", NULL, hc(cmd, "summary"),
633 ctx->qry.head); 641 ctx->qry.head);
634 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head, 642 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head,
635 ctx->qry.sha1, NULL); 643 ctx->qry.sha1, NULL);
636 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head, 644 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head,
637 NULL, NULL, 0, NULL, NULL); 645 NULL, NULL, 0, NULL, NULL, ctx->qry.showmsg);
638 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, 646 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head,
639 ctx->qry.sha1, NULL); 647 ctx->qry.sha1, NULL);
640 cgit_commit_link("commit", NULL, hc(cmd, "commit"), 648 cgit_commit_link("commit", NULL, hc(cmd, "commit"),
641 ctx->qry.head, ctx->qry.sha1); 649 ctx->qry.head, ctx->qry.sha1);
642 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, 650 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
643 ctx->qry.sha1, ctx->qry.sha2, NULL); 651 ctx->qry.sha1, ctx->qry.sha2, NULL);
644 if (ctx->repo->readme) 652 if (ctx->repo->readme)
645 reporevlink("about", "about", NULL, 653 reporevlink("about", "about", NULL,
646 hc(cmd, "about"), ctx->qry.head, NULL, 654 hc(cmd, "about"), ctx->qry.head, NULL,
647 NULL); 655 NULL);
648 html("</td><td class='form'>"); 656 html("</td><td class='form'>");
649 html("<form class='right' method='get' action='"); 657 html("<form class='right' method='get' action='");
650 if (ctx->cfg.virtual_root) 658 if (ctx->cfg.virtual_root)
651 html_url_path(cgit_fileurl(ctx->qry.repo, "log", 659 html_url_path(cgit_fileurl(ctx->qry.repo, "log",
652 ctx->qry.path, NULL)); 660 ctx->qry.path, NULL));
653 html("'>\n"); 661 html("'>\n");
654 add_hidden_formfields(1, 0, "log"); 662 add_hidden_formfields(1, 0, "log");
655 html("<select name='qt'>\n"); 663 html("<select name='qt'>\n");
656 html_option("grep", "log msg", ctx->qry.grep); 664 html_option("grep", "log msg", ctx->qry.grep);
657 html_option("author", "author", ctx->qry.grep); 665 html_option("author", "author", ctx->qry.grep);
658 html_option("committer", "committer", ctx->qry.grep); 666 html_option("committer", "committer", ctx->qry.grep);
659 html("</select>\n"); 667 html("</select>\n");
660 html("<input class='txt' type='text' size='10' name='q' value='"); 668 html("<input class='txt' type='text' size='10' name='q' value='");
661 html_attr(ctx->qry.search); 669 html_attr(ctx->qry.search);
662 html("'/>\n"); 670 html("'/>\n");
663 html("<input type='submit' value='search'/>\n"); 671 html("<input type='submit' value='search'/>\n");
664 html("</form>\n"); 672 html("</form>\n");
665 } else { 673 } else {
666 site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0); 674 site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0);
667 if (ctx->cfg.root_readme) 675 if (ctx->cfg.root_readme)
668 site_link("about", "about", NULL, hc(cmd, "about"), 676 site_link("about", "about", NULL, hc(cmd, "about"),
669 NULL, 0); 677 NULL, 0);
670 html("</td><td class='form'>"); 678 html("</td><td class='form'>");
671 html("<form method='get' action='"); 679 html("<form method='get' action='");
672 html_attr(cgit_rooturl()); 680 html_attr(cgit_rooturl());
673 html("'>\n"); 681 html("'>\n");
674 html("<input type='text' name='q' size='10' value='"); 682 html("<input type='text' name='q' size='10' value='");
675 html_attr(ctx->qry.search); 683 html_attr(ctx->qry.search);
676 html("'/>\n"); 684 html("'/>\n");
677 html("<input type='submit' value='search'/>\n"); 685 html("<input type='submit' value='search'/>\n");
678 html("</form>"); 686 html("</form>");
679 } 687 }
680 html("</td></tr></table>\n"); 688 html("</td></tr></table>\n");
681 html("<div class='content'>"); 689 html("<div class='content'>");
682} 690}
683 691
684void cgit_print_filemode(unsigned short mode) 692void cgit_print_filemode(unsigned short mode)
685{ 693{
diff --git a/ui-shared.h b/ui-shared.h
index 3c8a6d0..2ab53ae 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -1,46 +1,46 @@
1#ifndef UI_SHARED_H 1#ifndef UI_SHARED_H
2#define UI_SHARED_H 2#define UI_SHARED_H
3 3
4extern char *cgit_hosturl(); 4extern char *cgit_hosturl();
5extern char *cgit_repourl(const char *reponame); 5extern char *cgit_repourl(const char *reponame);
6extern char *cgit_fileurl(const char *reponame, const char *pagename, 6extern char *cgit_fileurl(const char *reponame, const char *pagename,
7 const char *filename, const char *query); 7 const char *filename, const char *query);
8extern char *cgit_pageurl(const char *reponame, const char *pagename, 8extern char *cgit_pageurl(const char *reponame, const char *pagename,
9 const char *query); 9 const char *query);
10 10
11extern void cgit_index_link(char *name, char *title, char *class, 11extern void cgit_index_link(char *name, char *title, char *class,
12 char *pattern, int ofs); 12 char *pattern, int ofs);
13extern void cgit_summary_link(char *name, char *title, char *class, char *head); 13extern void cgit_summary_link(char *name, char *title, char *class, char *head);
14extern void cgit_tag_link(char *name, char *title, char *class, char *head, 14extern void cgit_tag_link(char *name, char *title, char *class, char *head,
15 char *rev); 15 char *rev);
16extern void cgit_tree_link(char *name, char *title, char *class, char *head, 16extern void cgit_tree_link(char *name, char *title, char *class, char *head,
17 char *rev, char *path); 17 char *rev, char *path);
18extern void cgit_plain_link(char *name, char *title, char *class, char *head, 18extern void cgit_plain_link(char *name, char *title, char *class, char *head,
19 char *rev, char *path); 19 char *rev, char *path);
20extern void cgit_log_link(char *name, char *title, char *class, char *head, 20extern void cgit_log_link(char *name, char *title, char *class, char *head,
21 char *rev, char *path, int ofs, char *grep, 21 char *rev, char *path, int ofs, char *grep,
22 char *pattern); 22 char *pattern, int showmsg);
23extern void cgit_commit_link(char *name, char *title, char *class, char *head, 23extern void cgit_commit_link(char *name, char *title, char *class, char *head,
24 char *rev); 24 char *rev);
25extern void cgit_patch_link(char *name, char *title, char *class, char *head, 25extern void cgit_patch_link(char *name, char *title, char *class, char *head,
26 char *rev); 26 char *rev);
27extern void cgit_refs_link(char *name, char *title, char *class, char *head, 27extern void cgit_refs_link(char *name, char *title, char *class, char *head,
28 char *rev, char *path); 28 char *rev, char *path);
29extern void cgit_snapshot_link(char *name, char *title, char *class, 29extern void cgit_snapshot_link(char *name, char *title, char *class,
30 char *head, char *rev, char *archivename); 30 char *head, char *rev, char *archivename);
31extern void cgit_diff_link(char *name, char *title, char *class, char *head, 31extern void cgit_diff_link(char *name, char *title, char *class, char *head,
32 char *new_rev, char *old_rev, char *path); 32 char *new_rev, char *old_rev, char *path);
33extern void cgit_object_link(struct object *obj); 33extern void cgit_object_link(struct object *obj);
34 34
35extern void cgit_print_error(char *msg); 35extern void cgit_print_error(char *msg);
36extern void cgit_print_date(time_t secs, char *format, int local_time); 36extern void cgit_print_date(time_t secs, char *format, int local_time);
37extern void cgit_print_age(time_t t, time_t max_relative, char *format); 37extern void cgit_print_age(time_t t, time_t max_relative, char *format);
38extern void cgit_print_http_headers(struct cgit_context *ctx); 38extern void cgit_print_http_headers(struct cgit_context *ctx);
39extern void cgit_print_docstart(struct cgit_context *ctx); 39extern void cgit_print_docstart(struct cgit_context *ctx);
40extern void cgit_print_docend(); 40extern void cgit_print_docend();
41extern void cgit_print_pageheader(struct cgit_context *ctx); 41extern void cgit_print_pageheader(struct cgit_context *ctx);
42extern void cgit_print_filemode(unsigned short mode); 42extern void cgit_print_filemode(unsigned short mode);
43extern void cgit_print_snapshot_links(const char *repo, const char *head, 43extern void cgit_print_snapshot_links(const char *repo, const char *head,
44 const char *hex, int snapshots); 44 const char *hex, int snapshots);
45 45
46#endif /* UI_SHARED_H */ 46#endif /* UI_SHARED_H */
diff --git a/ui-tree.c b/ui-tree.c
index 79332fc..051db7c 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -61,97 +61,97 @@ static void print_object(const unsigned char *sha1, char *path)
61} 61}
62 62
63 63
64static int ls_item(const unsigned char *sha1, const char *base, int baselen, 64static int ls_item(const unsigned char *sha1, const char *base, int baselen,
65 const char *pathname, unsigned int mode, int stage, 65 const char *pathname, unsigned int mode, int stage,
66 void *cbdata) 66 void *cbdata)
67{ 67{
68 char *name; 68 char *name;
69 char *fullpath; 69 char *fullpath;
70 enum object_type type; 70 enum object_type type;
71 unsigned long size = 0; 71 unsigned long size = 0;
72 72
73 name = xstrdup(pathname); 73 name = xstrdup(pathname);
74 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 74 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
75 ctx.qry.path ? "/" : "", name); 75 ctx.qry.path ? "/" : "", name);
76 76
77 if (!S_ISGITLINK(mode)) { 77 if (!S_ISGITLINK(mode)) {
78 type = sha1_object_info(sha1, &size); 78 type = sha1_object_info(sha1, &size);
79 if (type == OBJ_BAD) { 79 if (type == OBJ_BAD) {
80 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 80 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
81 name, 81 name,
82 sha1_to_hex(sha1)); 82 sha1_to_hex(sha1));
83 return 0; 83 return 0;
84 } 84 }
85 } 85 }
86 86
87 html("<tr><td class='ls-mode'>"); 87 html("<tr><td class='ls-mode'>");
88 cgit_print_filemode(mode); 88 cgit_print_filemode(mode);
89 html("</td><td>"); 89 html("</td><td>");
90 if (S_ISGITLINK(mode)) { 90 if (S_ISGITLINK(mode)) {
91 htmlf("<a class='ls-mod' href='"); 91 htmlf("<a class='ls-mod' href='");
92 html_attr(fmt(ctx.repo->module_link, 92 html_attr(fmt(ctx.repo->module_link,
93 name, 93 name,
94 sha1_to_hex(sha1))); 94 sha1_to_hex(sha1)));
95 html("'>"); 95 html("'>");
96 html_txt(name); 96 html_txt(name);
97 html("</a>"); 97 html("</a>");
98 } else if (S_ISDIR(mode)) { 98 } else if (S_ISDIR(mode)) {
99 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, 99 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
100 curr_rev, fullpath); 100 curr_rev, fullpath);
101 } else { 101 } else {
102 cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, 102 cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head,
103 curr_rev, fullpath); 103 curr_rev, fullpath);
104 } 104 }
105 htmlf("</td><td class='ls-size'>%li</td>", size); 105 htmlf("</td><td class='ls-size'>%li</td>", size);
106 106
107 html("<td>"); 107 html("<td>");
108 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, 108 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev,
109 fullpath, 0, NULL, NULL); 109 fullpath, 0, NULL, NULL, ctx.qry.showmsg);
110 html("</td></tr>\n"); 110 html("</td></tr>\n");
111 free(name); 111 free(name);
112 return 0; 112 return 0;
113} 113}
114 114
115static void ls_head() 115static void ls_head()
116{ 116{
117 html("<table summary='tree listing' class='list'>\n"); 117 html("<table summary='tree listing' class='list'>\n");
118 html("<tr class='nohover'>"); 118 html("<tr class='nohover'>");
119 html("<th class='left'>Mode</th>"); 119 html("<th class='left'>Mode</th>");
120 html("<th class='left'>Name</th>"); 120 html("<th class='left'>Name</th>");
121 html("<th class='right'>Size</th>"); 121 html("<th class='right'>Size</th>");
122 html("<th/>"); 122 html("<th/>");
123 html("</tr>\n"); 123 html("</tr>\n");
124 header = 1; 124 header = 1;
125} 125}
126 126
127static void ls_tail() 127static void ls_tail()
128{ 128{
129 if (!header) 129 if (!header)
130 return; 130 return;
131 html("</table>\n"); 131 html("</table>\n");
132 header = 0; 132 header = 0;
133} 133}
134 134
135static void ls_tree(const unsigned char *sha1, char *path) 135static void ls_tree(const unsigned char *sha1, char *path)
136{ 136{
137 struct tree *tree; 137 struct tree *tree;
138 138
139 tree = parse_tree_indirect(sha1); 139 tree = parse_tree_indirect(sha1);
140 if (!tree) { 140 if (!tree) {
141 cgit_print_error(fmt("Not a tree object: %s", 141 cgit_print_error(fmt("Not a tree object: %s",
142 sha1_to_hex(sha1))); 142 sha1_to_hex(sha1)));
143 return; 143 return;
144 } 144 }
145 145
146 ls_head(); 146 ls_head();
147 read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); 147 read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL);
148 ls_tail(); 148 ls_tail();
149} 149}
150 150
151 151
152static int walk_tree(const unsigned char *sha1, const char *base, int baselen, 152static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
153 const char *pathname, unsigned mode, int stage, 153 const char *pathname, unsigned mode, int stage,
154 void *cbdata) 154 void *cbdata)
155{ 155{
156 static int state; 156 static int state;
157 static char buffer[PATH_MAX]; 157 static char buffer[PATH_MAX];