summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2009-07-31 15:42:57 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2009-07-31 15:42:57 (UTC)
commitf35db1cd2b75aac6952aa07713e44ca01fd89727 (patch) (unidiff)
treeaffc8325bf67ae51044c809c0b693cbe55dd097a
parent46b7abed99e957008c01c02cf612aa526ba92f04 (diff)
downloadcgit-f35db1cd2b75aac6952aa07713e44ca01fd89727.zip
cgit-f35db1cd2b75aac6952aa07713e44ca01fd89727.tar.gz
cgit-f35db1cd2b75aac6952aa07713e44ca01fd89727.tar.bz2
ui-commit: add support for 'commit-filter' option
This new option specifies a filter which is executed on the commit message, i.e. the commit message is written to the filters STDIN and the filters STDOUT is included verbatim as the commit message. This can be used to implement commit linking by creating a simple shell script in e.g. /usr/bin/cgit-commit-filter.sh like this: #/bin/sh sed -re 's|\b([0-9a-fA-F]{6,40})\b|<a href="./?id=\1">\1</a>|g' 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--cgitrc.5.txt6
-rw-r--r--ui-commit.c8
4 files changed, 17 insertions, 0 deletions
diff --git a/cgit.c b/cgit.c
index eb7b45d..2cda554 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,515 +1,517 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
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 "cache.h" 10#include "cache.h"
11#include "cmd.h" 11#include "cmd.h"
12#include "configfile.h" 12#include "configfile.h"
13#include "html.h" 13#include "html.h"
14#include "ui-shared.h" 14#include "ui-shared.h"
15#include "ui-stats.h" 15#include "ui-stats.h"
16#include "scan-tree.h" 16#include "scan-tree.h"
17 17
18const char *cgit_version = CGIT_VERSION; 18const char *cgit_version = CGIT_VERSION;
19 19
20struct cgit_filter *new_filter(const char *cmd, int extra_args) 20struct cgit_filter *new_filter(const char *cmd, int extra_args)
21{ 21{
22 struct cgit_filter *f; 22 struct cgit_filter *f;
23 23
24 if (!cmd) 24 if (!cmd)
25 return NULL; 25 return NULL;
26 26
27 f = xmalloc(sizeof(struct cgit_filter)); 27 f = xmalloc(sizeof(struct cgit_filter));
28 f->cmd = xstrdup(cmd); 28 f->cmd = xstrdup(cmd);
29 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 29 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
30 f->argv[0] = f->cmd; 30 f->argv[0] = f->cmd;
31 f->argv[1] = NULL; 31 f->argv[1] = NULL;
32 return f; 32 return f;
33} 33}
34 34
35void config_cb(const char *name, const char *value) 35void config_cb(const char *name, const char *value)
36{ 36{
37 if (!strcmp(name, "root-title")) 37 if (!strcmp(name, "root-title"))
38 ctx.cfg.root_title = xstrdup(value); 38 ctx.cfg.root_title = xstrdup(value);
39 else if (!strcmp(name, "root-desc")) 39 else if (!strcmp(name, "root-desc"))
40 ctx.cfg.root_desc = xstrdup(value); 40 ctx.cfg.root_desc = xstrdup(value);
41 else if (!strcmp(name, "root-readme")) 41 else if (!strcmp(name, "root-readme"))
42 ctx.cfg.root_readme = xstrdup(value); 42 ctx.cfg.root_readme = xstrdup(value);
43 else if (!strcmp(name, "css")) 43 else if (!strcmp(name, "css"))
44 ctx.cfg.css = xstrdup(value); 44 ctx.cfg.css = xstrdup(value);
45 else if (!strcmp(name, "favicon")) 45 else if (!strcmp(name, "favicon"))
46 ctx.cfg.favicon = xstrdup(value); 46 ctx.cfg.favicon = xstrdup(value);
47 else if (!strcmp(name, "footer")) 47 else if (!strcmp(name, "footer"))
48 ctx.cfg.footer = xstrdup(value); 48 ctx.cfg.footer = xstrdup(value);
49 else if (!strcmp(name, "head-include")) 49 else if (!strcmp(name, "head-include"))
50 ctx.cfg.head_include = xstrdup(value); 50 ctx.cfg.head_include = xstrdup(value);
51 else if (!strcmp(name, "header")) 51 else if (!strcmp(name, "header"))
52 ctx.cfg.header = xstrdup(value); 52 ctx.cfg.header = xstrdup(value);
53 else if (!strcmp(name, "logo")) 53 else if (!strcmp(name, "logo"))
54 ctx.cfg.logo = xstrdup(value); 54 ctx.cfg.logo = xstrdup(value);
55 else if (!strcmp(name, "index-header")) 55 else if (!strcmp(name, "index-header"))
56 ctx.cfg.index_header = xstrdup(value); 56 ctx.cfg.index_header = xstrdup(value);
57 else if (!strcmp(name, "index-info")) 57 else if (!strcmp(name, "index-info"))
58 ctx.cfg.index_info = xstrdup(value); 58 ctx.cfg.index_info = xstrdup(value);
59 else if (!strcmp(name, "logo-link")) 59 else if (!strcmp(name, "logo-link"))
60 ctx.cfg.logo_link = xstrdup(value); 60 ctx.cfg.logo_link = xstrdup(value);
61 else if (!strcmp(name, "module-link")) 61 else if (!strcmp(name, "module-link"))
62 ctx.cfg.module_link = xstrdup(value); 62 ctx.cfg.module_link = xstrdup(value);
63 else if (!strcmp(name, "virtual-root")) { 63 else if (!strcmp(name, "virtual-root")) {
64 ctx.cfg.virtual_root = trim_end(value, '/'); 64 ctx.cfg.virtual_root = trim_end(value, '/');
65 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 65 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
66 ctx.cfg.virtual_root = ""; 66 ctx.cfg.virtual_root = "";
67 } else if (!strcmp(name, "nocache")) 67 } else if (!strcmp(name, "nocache"))
68 ctx.cfg.nocache = atoi(value); 68 ctx.cfg.nocache = atoi(value);
69 else if (!strcmp(name, "noheader")) 69 else if (!strcmp(name, "noheader"))
70 ctx.cfg.noheader = atoi(value); 70 ctx.cfg.noheader = atoi(value);
71 else if (!strcmp(name, "snapshots")) 71 else if (!strcmp(name, "snapshots"))
72 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 72 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
73 else if (!strcmp(name, "enable-index-links")) 73 else if (!strcmp(name, "enable-index-links"))
74 ctx.cfg.enable_index_links = atoi(value); 74 ctx.cfg.enable_index_links = atoi(value);
75 else if (!strcmp(name, "enable-log-filecount")) 75 else if (!strcmp(name, "enable-log-filecount"))
76 ctx.cfg.enable_log_filecount = atoi(value); 76 ctx.cfg.enable_log_filecount = atoi(value);
77 else if (!strcmp(name, "enable-log-linecount")) 77 else if (!strcmp(name, "enable-log-linecount"))
78 ctx.cfg.enable_log_linecount = atoi(value); 78 ctx.cfg.enable_log_linecount = atoi(value);
79 else if (!strcmp(name, "max-stats")) 79 else if (!strcmp(name, "max-stats"))
80 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 80 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
81 else if (!strcmp(name, "cache-size")) 81 else if (!strcmp(name, "cache-size"))
82 ctx.cfg.cache_size = atoi(value); 82 ctx.cfg.cache_size = atoi(value);
83 else if (!strcmp(name, "cache-root")) 83 else if (!strcmp(name, "cache-root"))
84 ctx.cfg.cache_root = xstrdup(value); 84 ctx.cfg.cache_root = xstrdup(value);
85 else if (!strcmp(name, "cache-root-ttl")) 85 else if (!strcmp(name, "cache-root-ttl"))
86 ctx.cfg.cache_root_ttl = atoi(value); 86 ctx.cfg.cache_root_ttl = atoi(value);
87 else if (!strcmp(name, "cache-repo-ttl")) 87 else if (!strcmp(name, "cache-repo-ttl"))
88 ctx.cfg.cache_repo_ttl = atoi(value); 88 ctx.cfg.cache_repo_ttl = atoi(value);
89 else if (!strcmp(name, "cache-static-ttl")) 89 else if (!strcmp(name, "cache-static-ttl"))
90 ctx.cfg.cache_static_ttl = atoi(value); 90 ctx.cfg.cache_static_ttl = atoi(value);
91 else if (!strcmp(name, "cache-dynamic-ttl")) 91 else if (!strcmp(name, "cache-dynamic-ttl"))
92 ctx.cfg.cache_dynamic_ttl = atoi(value); 92 ctx.cfg.cache_dynamic_ttl = atoi(value);
93 else if (!strcmp(name, "commit-filter"))
94 ctx.cfg.commit_filter = new_filter(value, 0);
93 else if (!strcmp(name, "embedded")) 95 else if (!strcmp(name, "embedded"))
94 ctx.cfg.embedded = atoi(value); 96 ctx.cfg.embedded = atoi(value);
95 else if (!strcmp(name, "max-message-length")) 97 else if (!strcmp(name, "max-message-length"))
96 ctx.cfg.max_msg_len = atoi(value); 98 ctx.cfg.max_msg_len = atoi(value);
97 else if (!strcmp(name, "max-repodesc-length")) 99 else if (!strcmp(name, "max-repodesc-length"))
98 ctx.cfg.max_repodesc_len = atoi(value); 100 ctx.cfg.max_repodesc_len = atoi(value);
99 else if (!strcmp(name, "max-repo-count")) 101 else if (!strcmp(name, "max-repo-count"))
100 ctx.cfg.max_repo_count = atoi(value); 102 ctx.cfg.max_repo_count = atoi(value);
101 else if (!strcmp(name, "max-commit-count")) 103 else if (!strcmp(name, "max-commit-count"))
102 ctx.cfg.max_commit_count = atoi(value); 104 ctx.cfg.max_commit_count = atoi(value);
103 else if (!strcmp(name, "source-filter")) 105 else if (!strcmp(name, "source-filter"))
104 ctx.cfg.source_filter = new_filter(value, 1); 106 ctx.cfg.source_filter = new_filter(value, 1);
105 else if (!strcmp(name, "summary-log")) 107 else if (!strcmp(name, "summary-log"))
106 ctx.cfg.summary_log = atoi(value); 108 ctx.cfg.summary_log = atoi(value);
107 else if (!strcmp(name, "summary-branches")) 109 else if (!strcmp(name, "summary-branches"))
108 ctx.cfg.summary_branches = atoi(value); 110 ctx.cfg.summary_branches = atoi(value);
109 else if (!strcmp(name, "summary-tags")) 111 else if (!strcmp(name, "summary-tags"))
110 ctx.cfg.summary_tags = atoi(value); 112 ctx.cfg.summary_tags = atoi(value);
111 else if (!strcmp(name, "agefile")) 113 else if (!strcmp(name, "agefile"))
112 ctx.cfg.agefile = xstrdup(value); 114 ctx.cfg.agefile = xstrdup(value);
113 else if (!strcmp(name, "renamelimit")) 115 else if (!strcmp(name, "renamelimit"))
114 ctx.cfg.renamelimit = atoi(value); 116 ctx.cfg.renamelimit = atoi(value);
115 else if (!strcmp(name, "robots")) 117 else if (!strcmp(name, "robots"))
116 ctx.cfg.robots = xstrdup(value); 118 ctx.cfg.robots = xstrdup(value);
117 else if (!strcmp(name, "clone-prefix")) 119 else if (!strcmp(name, "clone-prefix"))
118 ctx.cfg.clone_prefix = xstrdup(value); 120 ctx.cfg.clone_prefix = xstrdup(value);
119 else if (!strcmp(name, "local-time")) 121 else if (!strcmp(name, "local-time"))
120 ctx.cfg.local_time = atoi(value); 122 ctx.cfg.local_time = atoi(value);
121 else if (!strcmp(name, "repo.group")) 123 else if (!strcmp(name, "repo.group"))
122 ctx.cfg.repo_group = xstrdup(value); 124 ctx.cfg.repo_group = xstrdup(value);
123 else if (!strcmp(name, "repo.url")) 125 else if (!strcmp(name, "repo.url"))
124 ctx.repo = cgit_add_repo(value); 126 ctx.repo = cgit_add_repo(value);
125 else if (!strcmp(name, "repo.name")) 127 else if (!strcmp(name, "repo.name"))
126 ctx.repo->name = xstrdup(value); 128 ctx.repo->name = xstrdup(value);
127 else if (ctx.repo && !strcmp(name, "repo.path")) 129 else if (ctx.repo && !strcmp(name, "repo.path"))
128 ctx.repo->path = trim_end(value, '/'); 130 ctx.repo->path = trim_end(value, '/');
129 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 131 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
130 ctx.repo->clone_url = xstrdup(value); 132 ctx.repo->clone_url = xstrdup(value);
131 else if (ctx.repo && !strcmp(name, "repo.desc")) 133 else if (ctx.repo && !strcmp(name, "repo.desc"))
132 ctx.repo->desc = xstrdup(value); 134 ctx.repo->desc = xstrdup(value);
133 else if (ctx.repo && !strcmp(name, "repo.owner")) 135 else if (ctx.repo && !strcmp(name, "repo.owner"))
134 ctx.repo->owner = xstrdup(value); 136 ctx.repo->owner = xstrdup(value);
135 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 137 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
136 ctx.repo->defbranch = xstrdup(value); 138 ctx.repo->defbranch = xstrdup(value);
137 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 139 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
138 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 140 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
139 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 141 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
140 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 142 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
141 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 143 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
142 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 144 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
143 else if (ctx.repo && !strcmp(name, "repo.max-stats")) 145 else if (ctx.repo && !strcmp(name, "repo.max-stats"))
144 ctx.repo->max_stats = cgit_find_stats_period(value, NULL); 146 ctx.repo->max_stats = cgit_find_stats_period(value, NULL);
145 else if (ctx.repo && !strcmp(name, "repo.module-link")) 147 else if (ctx.repo && !strcmp(name, "repo.module-link"))
146 ctx.repo->module_link= xstrdup(value); 148 ctx.repo->module_link= xstrdup(value);
147 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 149 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
148 if (*value == '/') 150 if (*value == '/')
149 ctx.repo->readme = xstrdup(value); 151 ctx.repo->readme = xstrdup(value);
150 else 152 else
151 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 153 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
152 } else if (!strcmp(name, "include")) 154 } else if (!strcmp(name, "include"))
153 parse_configfile(value, config_cb); 155 parse_configfile(value, config_cb);
154} 156}
155 157
156static void querystring_cb(const char *name, const char *value) 158static void querystring_cb(const char *name, const char *value)
157{ 159{
158 if (!strcmp(name,"r")) { 160 if (!strcmp(name,"r")) {
159 ctx.qry.repo = xstrdup(value); 161 ctx.qry.repo = xstrdup(value);
160 ctx.repo = cgit_get_repoinfo(value); 162 ctx.repo = cgit_get_repoinfo(value);
161 } else if (!strcmp(name, "p")) { 163 } else if (!strcmp(name, "p")) {
162 ctx.qry.page = xstrdup(value); 164 ctx.qry.page = xstrdup(value);
163 } else if (!strcmp(name, "url")) { 165 } else if (!strcmp(name, "url")) {
164 ctx.qry.url = xstrdup(value); 166 ctx.qry.url = xstrdup(value);
165 cgit_parse_url(value); 167 cgit_parse_url(value);
166 } else if (!strcmp(name, "qt")) { 168 } else if (!strcmp(name, "qt")) {
167 ctx.qry.grep = xstrdup(value); 169 ctx.qry.grep = xstrdup(value);
168 } else if (!strcmp(name, "q")) { 170 } else if (!strcmp(name, "q")) {
169 ctx.qry.search = xstrdup(value); 171 ctx.qry.search = xstrdup(value);
170 } else if (!strcmp(name, "h")) { 172 } else if (!strcmp(name, "h")) {
171 ctx.qry.head = xstrdup(value); 173 ctx.qry.head = xstrdup(value);
172 ctx.qry.has_symref = 1; 174 ctx.qry.has_symref = 1;
173 } else if (!strcmp(name, "id")) { 175 } else if (!strcmp(name, "id")) {
174 ctx.qry.sha1 = xstrdup(value); 176 ctx.qry.sha1 = xstrdup(value);
175 ctx.qry.has_sha1 = 1; 177 ctx.qry.has_sha1 = 1;
176 } else if (!strcmp(name, "id2")) { 178 } else if (!strcmp(name, "id2")) {
177 ctx.qry.sha2 = xstrdup(value); 179 ctx.qry.sha2 = xstrdup(value);
178 ctx.qry.has_sha1 = 1; 180 ctx.qry.has_sha1 = 1;
179 } else if (!strcmp(name, "ofs")) { 181 } else if (!strcmp(name, "ofs")) {
180 ctx.qry.ofs = atoi(value); 182 ctx.qry.ofs = atoi(value);
181 } else if (!strcmp(name, "path")) { 183 } else if (!strcmp(name, "path")) {
182 ctx.qry.path = trim_end(value, '/'); 184 ctx.qry.path = trim_end(value, '/');
183 } else if (!strcmp(name, "name")) { 185 } else if (!strcmp(name, "name")) {
184 ctx.qry.name = xstrdup(value); 186 ctx.qry.name = xstrdup(value);
185 } else if (!strcmp(name, "mimetype")) { 187 } else if (!strcmp(name, "mimetype")) {
186 ctx.qry.mimetype = xstrdup(value); 188 ctx.qry.mimetype = xstrdup(value);
187 } else if (!strcmp(name, "s")){ 189 } else if (!strcmp(name, "s")){
188 ctx.qry.sort = xstrdup(value); 190 ctx.qry.sort = xstrdup(value);
189 } else if (!strcmp(name, "showmsg")) { 191 } else if (!strcmp(name, "showmsg")) {
190 ctx.qry.showmsg = atoi(value); 192 ctx.qry.showmsg = atoi(value);
191 } else if (!strcmp(name, "period")) { 193 } else if (!strcmp(name, "period")) {
192 ctx.qry.period = xstrdup(value); 194 ctx.qry.period = xstrdup(value);
193 } 195 }
194} 196}
195 197
196static void prepare_context(struct cgit_context *ctx) 198static void prepare_context(struct cgit_context *ctx)
197{ 199{
198 memset(ctx, 0, sizeof(ctx)); 200 memset(ctx, 0, sizeof(ctx));
199 ctx->cfg.agefile = "info/web/last-modified"; 201 ctx->cfg.agefile = "info/web/last-modified";
200 ctx->cfg.nocache = 0; 202 ctx->cfg.nocache = 0;
201 ctx->cfg.cache_size = 0; 203 ctx->cfg.cache_size = 0;
202 ctx->cfg.cache_dynamic_ttl = 5; 204 ctx->cfg.cache_dynamic_ttl = 5;
203 ctx->cfg.cache_max_create_time = 5; 205 ctx->cfg.cache_max_create_time = 5;
204 ctx->cfg.cache_repo_ttl = 5; 206 ctx->cfg.cache_repo_ttl = 5;
205 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 207 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
206 ctx->cfg.cache_root_ttl = 5; 208 ctx->cfg.cache_root_ttl = 5;
207 ctx->cfg.cache_static_ttl = -1; 209 ctx->cfg.cache_static_ttl = -1;
208 ctx->cfg.css = "/cgit.css"; 210 ctx->cfg.css = "/cgit.css";
209 ctx->cfg.logo = "/git-logo.png"; 211 ctx->cfg.logo = "/git-logo.png";
210 ctx->cfg.local_time = 0; 212 ctx->cfg.local_time = 0;
211 ctx->cfg.max_repo_count = 50; 213 ctx->cfg.max_repo_count = 50;
212 ctx->cfg.max_commit_count = 50; 214 ctx->cfg.max_commit_count = 50;
213 ctx->cfg.max_lock_attempts = 5; 215 ctx->cfg.max_lock_attempts = 5;
214 ctx->cfg.max_msg_len = 80; 216 ctx->cfg.max_msg_len = 80;
215 ctx->cfg.max_repodesc_len = 80; 217 ctx->cfg.max_repodesc_len = 80;
216 ctx->cfg.max_stats = 0; 218 ctx->cfg.max_stats = 0;
217 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 219 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
218 ctx->cfg.renamelimit = -1; 220 ctx->cfg.renamelimit = -1;
219 ctx->cfg.robots = "index, nofollow"; 221 ctx->cfg.robots = "index, nofollow";
220 ctx->cfg.root_title = "Git repository browser"; 222 ctx->cfg.root_title = "Git repository browser";
221 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 223 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
222 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 224 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
223 ctx->cfg.summary_branches = 10; 225 ctx->cfg.summary_branches = 10;
224 ctx->cfg.summary_log = 10; 226 ctx->cfg.summary_log = 10;
225 ctx->cfg.summary_tags = 10; 227 ctx->cfg.summary_tags = 10;
226 ctx->page.mimetype = "text/html"; 228 ctx->page.mimetype = "text/html";
227 ctx->page.charset = PAGE_ENCODING; 229 ctx->page.charset = PAGE_ENCODING;
228 ctx->page.filename = NULL; 230 ctx->page.filename = NULL;
229 ctx->page.size = 0; 231 ctx->page.size = 0;
230 ctx->page.modified = time(NULL); 232 ctx->page.modified = time(NULL);
231 ctx->page.expires = ctx->page.modified; 233 ctx->page.expires = ctx->page.modified;
232 ctx->page.etag = NULL; 234 ctx->page.etag = NULL;
233} 235}
234 236
235struct refmatch { 237struct refmatch {
236 char *req_ref; 238 char *req_ref;
237 char *first_ref; 239 char *first_ref;
238 int match; 240 int match;
239}; 241};
240 242
241int find_current_ref(const char *refname, const unsigned char *sha1, 243int find_current_ref(const char *refname, const unsigned char *sha1,
242 int flags, void *cb_data) 244 int flags, void *cb_data)
243{ 245{
244 struct refmatch *info; 246 struct refmatch *info;
245 247
246 info = (struct refmatch *)cb_data; 248 info = (struct refmatch *)cb_data;
247 if (!strcmp(refname, info->req_ref)) 249 if (!strcmp(refname, info->req_ref))
248 info->match = 1; 250 info->match = 1;
249 if (!info->first_ref) 251 if (!info->first_ref)
250 info->first_ref = xstrdup(refname); 252 info->first_ref = xstrdup(refname);
251 return info->match; 253 return info->match;
252} 254}
253 255
254char *find_default_branch(struct cgit_repo *repo) 256char *find_default_branch(struct cgit_repo *repo)
255{ 257{
256 struct refmatch info; 258 struct refmatch info;
257 char *ref; 259 char *ref;
258 260
259 info.req_ref = repo->defbranch; 261 info.req_ref = repo->defbranch;
260 info.first_ref = NULL; 262 info.first_ref = NULL;
261 info.match = 0; 263 info.match = 0;
262 for_each_branch_ref(find_current_ref, &info); 264 for_each_branch_ref(find_current_ref, &info);
263 if (info.match) 265 if (info.match)
264 ref = info.req_ref; 266 ref = info.req_ref;
265 else 267 else
266 ref = info.first_ref; 268 ref = info.first_ref;
267 if (ref) 269 if (ref)
268 ref = xstrdup(ref); 270 ref = xstrdup(ref);
269 return ref; 271 return ref;
270} 272}
271 273
272static int prepare_repo_cmd(struct cgit_context *ctx) 274static int prepare_repo_cmd(struct cgit_context *ctx)
273{ 275{
274 char *tmp; 276 char *tmp;
275 unsigned char sha1[20]; 277 unsigned char sha1[20];
276 int nongit = 0; 278 int nongit = 0;
277 279
278 setenv("GIT_DIR", ctx->repo->path, 1); 280 setenv("GIT_DIR", ctx->repo->path, 1);
279 setup_git_directory_gently(&nongit); 281 setup_git_directory_gently(&nongit);
280 if (nongit) { 282 if (nongit) {
281 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 283 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
282 "config error"); 284 "config error");
283 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 285 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
284 ctx->repo = NULL; 286 ctx->repo = NULL;
285 cgit_print_http_headers(ctx); 287 cgit_print_http_headers(ctx);
286 cgit_print_docstart(ctx); 288 cgit_print_docstart(ctx);
287 cgit_print_pageheader(ctx); 289 cgit_print_pageheader(ctx);
288 cgit_print_error(tmp); 290 cgit_print_error(tmp);
289 cgit_print_docend(); 291 cgit_print_docend();
290 return 1; 292 return 1;
291 } 293 }
292 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 294 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
293 295
294 if (!ctx->qry.head) { 296 if (!ctx->qry.head) {
295 ctx->qry.nohead = 1; 297 ctx->qry.nohead = 1;
296 ctx->qry.head = find_default_branch(ctx->repo); 298 ctx->qry.head = find_default_branch(ctx->repo);
297 ctx->repo->defbranch = ctx->qry.head; 299 ctx->repo->defbranch = ctx->qry.head;
298 } 300 }
299 301
300 if (!ctx->qry.head) { 302 if (!ctx->qry.head) {
301 cgit_print_http_headers(ctx); 303 cgit_print_http_headers(ctx);
302 cgit_print_docstart(ctx); 304 cgit_print_docstart(ctx);
303 cgit_print_pageheader(ctx); 305 cgit_print_pageheader(ctx);
304 cgit_print_error("Repository seems to be empty"); 306 cgit_print_error("Repository seems to be empty");
305 cgit_print_docend(); 307 cgit_print_docend();
306 return 1; 308 return 1;
307 } 309 }
308 310
309 if (get_sha1(ctx->qry.head, sha1)) { 311 if (get_sha1(ctx->qry.head, sha1)) {
310 tmp = xstrdup(ctx->qry.head); 312 tmp = xstrdup(ctx->qry.head);
311 ctx->qry.head = ctx->repo->defbranch; 313 ctx->qry.head = ctx->repo->defbranch;
312 ctx->page.status = 404; 314 ctx->page.status = 404;
313 ctx->page.statusmsg = "not found"; 315 ctx->page.statusmsg = "not found";
314 cgit_print_http_headers(ctx); 316 cgit_print_http_headers(ctx);
315 cgit_print_docstart(ctx); 317 cgit_print_docstart(ctx);
316 cgit_print_pageheader(ctx); 318 cgit_print_pageheader(ctx);
317 cgit_print_error(fmt("Invalid branch: %s", tmp)); 319 cgit_print_error(fmt("Invalid branch: %s", tmp));
318 cgit_print_docend(); 320 cgit_print_docend();
319 return 1; 321 return 1;
320 } 322 }
321 return 0; 323 return 0;
322} 324}
323 325
324static void process_request(void *cbdata) 326static void process_request(void *cbdata)
325{ 327{
326 struct cgit_context *ctx = cbdata; 328 struct cgit_context *ctx = cbdata;
327 struct cgit_cmd *cmd; 329 struct cgit_cmd *cmd;
328 330
329 cmd = cgit_get_cmd(ctx); 331 cmd = cgit_get_cmd(ctx);
330 if (!cmd) { 332 if (!cmd) {
331 ctx->page.title = "cgit error"; 333 ctx->page.title = "cgit error";
332 cgit_print_http_headers(ctx); 334 cgit_print_http_headers(ctx);
333 cgit_print_docstart(ctx); 335 cgit_print_docstart(ctx);
334 cgit_print_pageheader(ctx); 336 cgit_print_pageheader(ctx);
335 cgit_print_error("Invalid request"); 337 cgit_print_error("Invalid request");
336 cgit_print_docend(); 338 cgit_print_docend();
337 return; 339 return;
338 } 340 }
339 341
340 if (cmd->want_repo && !ctx->repo) { 342 if (cmd->want_repo && !ctx->repo) {
341 cgit_print_http_headers(ctx); 343 cgit_print_http_headers(ctx);
342 cgit_print_docstart(ctx); 344 cgit_print_docstart(ctx);
343 cgit_print_pageheader(ctx); 345 cgit_print_pageheader(ctx);
344 cgit_print_error(fmt("No repository selected")); 346 cgit_print_error(fmt("No repository selected"));
345 cgit_print_docend(); 347 cgit_print_docend();
346 return; 348 return;
347 } 349 }
348 350
349 if (ctx->repo && prepare_repo_cmd(ctx)) 351 if (ctx->repo && prepare_repo_cmd(ctx))
350 return; 352 return;
351 353
352 if (cmd->want_layout) { 354 if (cmd->want_layout) {
353 cgit_print_http_headers(ctx); 355 cgit_print_http_headers(ctx);
354 cgit_print_docstart(ctx); 356 cgit_print_docstart(ctx);
355 cgit_print_pageheader(ctx); 357 cgit_print_pageheader(ctx);
356 } 358 }
357 359
358 cmd->fn(ctx); 360 cmd->fn(ctx);
359 361
360 if (cmd->want_layout) 362 if (cmd->want_layout)
361 cgit_print_docend(); 363 cgit_print_docend();
362} 364}
363 365
364int cmp_repos(const void *a, const void *b) 366int cmp_repos(const void *a, const void *b)
365{ 367{
366 const struct cgit_repo *ra = a, *rb = b; 368 const struct cgit_repo *ra = a, *rb = b;
367 return strcmp(ra->url, rb->url); 369 return strcmp(ra->url, rb->url);
368} 370}
369 371
370void print_repo(struct cgit_repo *repo) 372void print_repo(struct cgit_repo *repo)
371{ 373{
372 printf("repo.url=%s\n", repo->url); 374 printf("repo.url=%s\n", repo->url);
373 printf("repo.name=%s\n", repo->name); 375 printf("repo.name=%s\n", repo->name);
374 printf("repo.path=%s\n", repo->path); 376 printf("repo.path=%s\n", repo->path);
375 if (repo->owner) 377 if (repo->owner)
376 printf("repo.owner=%s\n", repo->owner); 378 printf("repo.owner=%s\n", repo->owner);
377 if (repo->desc) 379 if (repo->desc)
378 printf("repo.desc=%s\n", repo->desc); 380 printf("repo.desc=%s\n", repo->desc);
379 if (repo->readme) 381 if (repo->readme)
380 printf("repo.readme=%s\n", repo->readme); 382 printf("repo.readme=%s\n", repo->readme);
381 printf("\n"); 383 printf("\n");
382} 384}
383 385
384void print_repolist(struct cgit_repolist *list) 386void print_repolist(struct cgit_repolist *list)
385{ 387{
386 int i; 388 int i;
387 389
388 for(i = 0; i < list->count; i++) 390 for(i = 0; i < list->count; i++)
389 print_repo(&list->repos[i]); 391 print_repo(&list->repos[i]);
390} 392}
391 393
392 394
393static void cgit_parse_args(int argc, const char **argv) 395static void cgit_parse_args(int argc, const char **argv)
394{ 396{
395 int i; 397 int i;
396 int scan = 0; 398 int scan = 0;
397 399
398 for (i = 1; i < argc; i++) { 400 for (i = 1; i < argc; i++) {
399 if (!strncmp(argv[i], "--cache=", 8)) { 401 if (!strncmp(argv[i], "--cache=", 8)) {
400 ctx.cfg.cache_root = xstrdup(argv[i]+8); 402 ctx.cfg.cache_root = xstrdup(argv[i]+8);
401 } 403 }
402 if (!strcmp(argv[i], "--nocache")) { 404 if (!strcmp(argv[i], "--nocache")) {
403 ctx.cfg.nocache = 1; 405 ctx.cfg.nocache = 1;
404 } 406 }
405 if (!strncmp(argv[i], "--query=", 8)) { 407 if (!strncmp(argv[i], "--query=", 8)) {
406 ctx.qry.raw = xstrdup(argv[i]+8); 408 ctx.qry.raw = xstrdup(argv[i]+8);
407 } 409 }
408 if (!strncmp(argv[i], "--repo=", 7)) { 410 if (!strncmp(argv[i], "--repo=", 7)) {
409 ctx.qry.repo = xstrdup(argv[i]+7); 411 ctx.qry.repo = xstrdup(argv[i]+7);
410 } 412 }
411 if (!strncmp(argv[i], "--page=", 7)) { 413 if (!strncmp(argv[i], "--page=", 7)) {
412 ctx.qry.page = xstrdup(argv[i]+7); 414 ctx.qry.page = xstrdup(argv[i]+7);
413 } 415 }
414 if (!strncmp(argv[i], "--head=", 7)) { 416 if (!strncmp(argv[i], "--head=", 7)) {
415 ctx.qry.head = xstrdup(argv[i]+7); 417 ctx.qry.head = xstrdup(argv[i]+7);
416 ctx.qry.has_symref = 1; 418 ctx.qry.has_symref = 1;
417 } 419 }
418 if (!strncmp(argv[i], "--sha1=", 7)) { 420 if (!strncmp(argv[i], "--sha1=", 7)) {
419 ctx.qry.sha1 = xstrdup(argv[i]+7); 421 ctx.qry.sha1 = xstrdup(argv[i]+7);
420 ctx.qry.has_sha1 = 1; 422 ctx.qry.has_sha1 = 1;
421 } 423 }
422 if (!strncmp(argv[i], "--ofs=", 6)) { 424 if (!strncmp(argv[i], "--ofs=", 6)) {
423 ctx.qry.ofs = atoi(argv[i]+6); 425 ctx.qry.ofs = atoi(argv[i]+6);
424 } 426 }
425 if (!strncmp(argv[i], "--scan-tree=", 12)) { 427 if (!strncmp(argv[i], "--scan-tree=", 12)) {
426 scan++; 428 scan++;
427 scan_tree(argv[i] + 12); 429 scan_tree(argv[i] + 12);
428 } 430 }
429 } 431 }
430 if (scan) { 432 if (scan) {
431 qsort(cgit_repolist.repos, cgit_repolist.count, 433 qsort(cgit_repolist.repos, cgit_repolist.count,
432 sizeof(struct cgit_repo), cmp_repos); 434 sizeof(struct cgit_repo), cmp_repos);
433 print_repolist(&cgit_repolist); 435 print_repolist(&cgit_repolist);
434 exit(0); 436 exit(0);
435 } 437 }
436} 438}
437 439
438static int calc_ttl() 440static int calc_ttl()
439{ 441{
440 if (!ctx.repo) 442 if (!ctx.repo)
441 return ctx.cfg.cache_root_ttl; 443 return ctx.cfg.cache_root_ttl;
442 444
443 if (!ctx.qry.page) 445 if (!ctx.qry.page)
444 return ctx.cfg.cache_repo_ttl; 446 return ctx.cfg.cache_repo_ttl;
445 447
446 if (ctx.qry.has_symref) 448 if (ctx.qry.has_symref)
447 return ctx.cfg.cache_dynamic_ttl; 449 return ctx.cfg.cache_dynamic_ttl;
448 450
449 if (ctx.qry.has_sha1) 451 if (ctx.qry.has_sha1)
450 return ctx.cfg.cache_static_ttl; 452 return ctx.cfg.cache_static_ttl;
451 453
452 return ctx.cfg.cache_repo_ttl; 454 return ctx.cfg.cache_repo_ttl;
453} 455}
454 456
455int main(int argc, const char **argv) 457int main(int argc, const char **argv)
456{ 458{
457 const char *cgit_config_env = getenv("CGIT_CONFIG"); 459 const char *cgit_config_env = getenv("CGIT_CONFIG");
458 const char *method = getenv("REQUEST_METHOD"); 460 const char *method = getenv("REQUEST_METHOD");
459 const char *path; 461 const char *path;
460 char *qry; 462 char *qry;
461 int err, ttl; 463 int err, ttl;
462 464
463 prepare_context(&ctx); 465 prepare_context(&ctx);
464 cgit_repolist.length = 0; 466 cgit_repolist.length = 0;
465 cgit_repolist.count = 0; 467 cgit_repolist.count = 0;
466 cgit_repolist.repos = NULL; 468 cgit_repolist.repos = NULL;
467 469
468 if (getenv("SCRIPT_NAME")) 470 if (getenv("SCRIPT_NAME"))
469 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); 471 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
470 if (getenv("QUERY_STRING")) 472 if (getenv("QUERY_STRING"))
471 ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); 473 ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
472 cgit_parse_args(argc, argv); 474 cgit_parse_args(argc, argv);
473 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 475 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
474 config_cb); 476 config_cb);
475 ctx.repo = NULL; 477 ctx.repo = NULL;
476 http_parse_querystring(ctx.qry.raw, querystring_cb); 478 http_parse_querystring(ctx.qry.raw, querystring_cb);
477 479
478 /* If virtual-root isn't specified in cgitrc, lets pretend 480 /* If virtual-root isn't specified in cgitrc, lets pretend
479 * that virtual-root equals SCRIPT_NAME. 481 * that virtual-root equals SCRIPT_NAME.
480 */ 482 */
481 if (!ctx.cfg.virtual_root) 483 if (!ctx.cfg.virtual_root)
482 ctx.cfg.virtual_root = ctx.cfg.script_name; 484 ctx.cfg.virtual_root = ctx.cfg.script_name;
483 485
484 /* If no url parameter is specified on the querystring, lets 486 /* If no url parameter is specified on the querystring, lets
485 * use PATH_INFO as url. This allows cgit to work with virtual 487 * use PATH_INFO as url. This allows cgit to work with virtual
486 * urls without the need for rewriterules in the webserver (as 488 * urls without the need for rewriterules in the webserver (as
487 * long as PATH_INFO is included in the cache lookup key). 489 * long as PATH_INFO is included in the cache lookup key).
488 */ 490 */
489 path = getenv("PATH_INFO"); 491 path = getenv("PATH_INFO");
490 if (!ctx.qry.url && path) { 492 if (!ctx.qry.url && path) {
491 if (path[0] == '/') 493 if (path[0] == '/')
492 path++; 494 path++;
493 ctx.qry.url = xstrdup(path); 495 ctx.qry.url = xstrdup(path);
494 if (ctx.qry.raw) { 496 if (ctx.qry.raw) {
495 qry = ctx.qry.raw; 497 qry = ctx.qry.raw;
496 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 498 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
497 free(qry); 499 free(qry);
498 } else 500 } else
499 ctx.qry.raw = ctx.qry.url; 501 ctx.qry.raw = ctx.qry.url;
500 cgit_parse_url(ctx.qry.url); 502 cgit_parse_url(ctx.qry.url);
501 } 503 }
502 504
503 ttl = calc_ttl(); 505 ttl = calc_ttl();
504 ctx.page.expires += ttl*60; 506 ctx.page.expires += ttl*60;
505 if (method && !strcmp(method, "HEAD")) 507 if (method && !strcmp(method, "HEAD"))
506 ctx.cfg.nocache = 1; 508 ctx.cfg.nocache = 1;
507 if (ctx.cfg.nocache) 509 if (ctx.cfg.nocache)
508 ctx.cfg.cache_size = 0; 510 ctx.cfg.cache_size = 0;
509 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 511 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
510 ctx.qry.raw, ttl, process_request, &ctx); 512 ctx.qry.raw, ttl, process_request, &ctx);
511 if (err) 513 if (err)
512 cgit_print_error(fmt("Error processing page: %s (%d)", 514 cgit_print_error(fmt("Error processing page: %s (%d)",
513 strerror(err), err)); 515 strerror(err), err));
514 return err; 516 return err;
515} 517}
diff --git a/cgit.h b/cgit.h
index f9cf0df..438301d 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,265 +1,266 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff-interface.h> 18#include <xdiff-interface.h>
19#include <xdiff/xdiff.h> 19#include <xdiff/xdiff.h>
20#include <utf8.h> 20#include <utf8.h>
21 21
22 22
23/* 23/*
24 * Dateformats used on misc. pages 24 * Dateformats used on misc. pages
25 */ 25 */
26#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 26#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
27#define FMT_SHORTDATE "%Y-%m-%d" 27#define FMT_SHORTDATE "%Y-%m-%d"
28#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 28#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
29 29
30 30
31/* 31/*
32 * Limits used for relative dates 32 * Limits used for relative dates
33 */ 33 */
34#define TM_MIN 60 34#define TM_MIN 60
35#define TM_HOUR (TM_MIN * 60) 35#define TM_HOUR (TM_MIN * 60)
36#define TM_DAY (TM_HOUR * 24) 36#define TM_DAY (TM_HOUR * 24)
37#define TM_WEEK (TM_DAY * 7) 37#define TM_WEEK (TM_DAY * 7)
38#define TM_YEAR (TM_DAY * 365) 38#define TM_YEAR (TM_DAY * 365)
39#define TM_MONTH (TM_YEAR / 12.0) 39#define TM_MONTH (TM_YEAR / 12.0)
40 40
41 41
42/* 42/*
43 * Default encoding 43 * Default encoding
44 */ 44 */
45#define PAGE_ENCODING "UTF-8" 45#define PAGE_ENCODING "UTF-8"
46 46
47typedef void (*configfn)(const char *name, const char *value); 47typedef void (*configfn)(const char *name, const char *value);
48typedef void (*filepair_fn)(struct diff_filepair *pair); 48typedef void (*filepair_fn)(struct diff_filepair *pair);
49typedef void (*linediff_fn)(char *line, int len); 49typedef void (*linediff_fn)(char *line, int len);
50 50
51struct cgit_repo { 51struct cgit_repo {
52 char *url; 52 char *url;
53 char *name; 53 char *name;
54 char *path; 54 char *path;
55 char *desc; 55 char *desc;
56 char *owner; 56 char *owner;
57 char *defbranch; 57 char *defbranch;
58 char *group; 58 char *group;
59 char *module_link; 59 char *module_link;
60 char *readme; 60 char *readme;
61 char *clone_url; 61 char *clone_url;
62 int snapshots; 62 int snapshots;
63 int enable_log_filecount; 63 int enable_log_filecount;
64 int enable_log_linecount; 64 int enable_log_linecount;
65 int max_stats; 65 int max_stats;
66 time_t mtime; 66 time_t mtime;
67}; 67};
68 68
69struct cgit_repolist { 69struct cgit_repolist {
70 int length; 70 int length;
71 int count; 71 int count;
72 struct cgit_repo *repos; 72 struct cgit_repo *repos;
73}; 73};
74 74
75struct commitinfo { 75struct commitinfo {
76 struct commit *commit; 76 struct commit *commit;
77 char *author; 77 char *author;
78 char *author_email; 78 char *author_email;
79 unsigned long author_date; 79 unsigned long author_date;
80 char *committer; 80 char *committer;
81 char *committer_email; 81 char *committer_email;
82 unsigned long committer_date; 82 unsigned long committer_date;
83 char *subject; 83 char *subject;
84 char *msg; 84 char *msg;
85 char *msg_encoding; 85 char *msg_encoding;
86}; 86};
87 87
88struct taginfo { 88struct taginfo {
89 char *tagger; 89 char *tagger;
90 char *tagger_email; 90 char *tagger_email;
91 unsigned long tagger_date; 91 unsigned long tagger_date;
92 char *msg; 92 char *msg;
93}; 93};
94 94
95struct refinfo { 95struct refinfo {
96 const char *refname; 96 const char *refname;
97 struct object *object; 97 struct object *object;
98 union { 98 union {
99 struct taginfo *tag; 99 struct taginfo *tag;
100 struct commitinfo *commit; 100 struct commitinfo *commit;
101 }; 101 };
102}; 102};
103 103
104struct reflist { 104struct reflist {
105 struct refinfo **refs; 105 struct refinfo **refs;
106 int alloc; 106 int alloc;
107 int count; 107 int count;
108}; 108};
109 109
110struct cgit_query { 110struct cgit_query {
111 int has_symref; 111 int has_symref;
112 int has_sha1; 112 int has_sha1;
113 char *raw; 113 char *raw;
114 char *repo; 114 char *repo;
115 char *page; 115 char *page;
116 char *search; 116 char *search;
117 char *grep; 117 char *grep;
118 char *head; 118 char *head;
119 char *sha1; 119 char *sha1;
120 char *sha2; 120 char *sha2;
121 char *path; 121 char *path;
122 char *name; 122 char *name;
123 char *mimetype; 123 char *mimetype;
124 char *url; 124 char *url;
125 char *period; 125 char *period;
126 int ofs; 126 int ofs;
127 int nohead; 127 int nohead;
128 char *sort; 128 char *sort;
129 int showmsg; 129 int showmsg;
130}; 130};
131 131
132struct cgit_filter { 132struct cgit_filter {
133 char *cmd; 133 char *cmd;
134 char **argv; 134 char **argv;
135 int old_stdout; 135 int old_stdout;
136 int pipe_fh[2]; 136 int pipe_fh[2];
137 int pid; 137 int pid;
138 int exitstatus; 138 int exitstatus;
139}; 139};
140 140
141struct cgit_config { 141struct cgit_config {
142 char *agefile; 142 char *agefile;
143 char *cache_root; 143 char *cache_root;
144 char *clone_prefix; 144 char *clone_prefix;
145 char *css; 145 char *css;
146 char *favicon; 146 char *favicon;
147 char *footer; 147 char *footer;
148 char *head_include; 148 char *head_include;
149 char *header; 149 char *header;
150 char *index_header; 150 char *index_header;
151 char *index_info; 151 char *index_info;
152 char *logo; 152 char *logo;
153 char *logo_link; 153 char *logo_link;
154 char *module_link; 154 char *module_link;
155 char *repo_group; 155 char *repo_group;
156 char *robots; 156 char *robots;
157 char *root_title; 157 char *root_title;
158 char *root_desc; 158 char *root_desc;
159 char *root_readme; 159 char *root_readme;
160 char *script_name; 160 char *script_name;
161 char *virtual_root; 161 char *virtual_root;
162 int cache_size; 162 int cache_size;
163 int cache_dynamic_ttl; 163 int cache_dynamic_ttl;
164 int cache_max_create_time; 164 int cache_max_create_time;
165 int cache_repo_ttl; 165 int cache_repo_ttl;
166 int cache_root_ttl; 166 int cache_root_ttl;
167 int cache_static_ttl; 167 int cache_static_ttl;
168 int embedded; 168 int embedded;
169 int enable_index_links; 169 int enable_index_links;
170 int enable_log_filecount; 170 int enable_log_filecount;
171 int enable_log_linecount; 171 int enable_log_linecount;
172 int local_time; 172 int local_time;
173 int max_repo_count; 173 int max_repo_count;
174 int max_commit_count; 174 int max_commit_count;
175 int max_lock_attempts; 175 int max_lock_attempts;
176 int max_msg_len; 176 int max_msg_len;
177 int max_repodesc_len; 177 int max_repodesc_len;
178 int max_stats; 178 int max_stats;
179 int nocache; 179 int nocache;
180 int noheader; 180 int noheader;
181 int renamelimit; 181 int renamelimit;
182 int snapshots; 182 int snapshots;
183 int summary_branches; 183 int summary_branches;
184 int summary_log; 184 int summary_log;
185 int summary_tags; 185 int summary_tags;
186 struct cgit_filter *commit_filter;
186 struct cgit_filter *source_filter; 187 struct cgit_filter *source_filter;
187}; 188};
188 189
189struct cgit_page { 190struct cgit_page {
190 time_t modified; 191 time_t modified;
191 time_t expires; 192 time_t expires;
192 size_t size; 193 size_t size;
193 char *mimetype; 194 char *mimetype;
194 char *charset; 195 char *charset;
195 char *filename; 196 char *filename;
196 char *etag; 197 char *etag;
197 char *title; 198 char *title;
198 int status; 199 int status;
199 char *statusmsg; 200 char *statusmsg;
200}; 201};
201 202
202struct cgit_context { 203struct cgit_context {
203 struct cgit_query qry; 204 struct cgit_query qry;
204 struct cgit_config cfg; 205 struct cgit_config cfg;
205 struct cgit_repo *repo; 206 struct cgit_repo *repo;
206 struct cgit_page page; 207 struct cgit_page page;
207}; 208};
208 209
209struct cgit_snapshot_format { 210struct cgit_snapshot_format {
210 const char *suffix; 211 const char *suffix;
211 const char *mimetype; 212 const char *mimetype;
212 write_archive_fn_t write_func; 213 write_archive_fn_t write_func;
213 int bit; 214 int bit;
214}; 215};
215 216
216extern const char *cgit_version; 217extern const char *cgit_version;
217 218
218extern struct cgit_repolist cgit_repolist; 219extern struct cgit_repolist cgit_repolist;
219extern struct cgit_context ctx; 220extern struct cgit_context ctx;
220extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 221extern const struct cgit_snapshot_format cgit_snapshot_formats[];
221 222
222extern struct cgit_repo *cgit_add_repo(const char *url); 223extern struct cgit_repo *cgit_add_repo(const char *url);
223extern struct cgit_repo *cgit_get_repoinfo(const char *url); 224extern struct cgit_repo *cgit_get_repoinfo(const char *url);
224extern void cgit_repo_config_cb(const char *name, const char *value); 225extern void cgit_repo_config_cb(const char *name, const char *value);
225 226
226extern int chk_zero(int result, char *msg); 227extern int chk_zero(int result, char *msg);
227extern int chk_positive(int result, char *msg); 228extern int chk_positive(int result, char *msg);
228extern int chk_non_negative(int result, char *msg); 229extern int chk_non_negative(int result, char *msg);
229 230
230extern char *trim_end(const char *str, char c); 231extern char *trim_end(const char *str, char c);
231extern char *strlpart(char *txt, int maxlen); 232extern char *strlpart(char *txt, int maxlen);
232extern char *strrpart(char *txt, int maxlen); 233extern char *strrpart(char *txt, int maxlen);
233 234
234extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 235extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
235extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 236extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
236 int flags, void *cb_data); 237 int flags, void *cb_data);
237 238
238extern void *cgit_free_commitinfo(struct commitinfo *info); 239extern void *cgit_free_commitinfo(struct commitinfo *info);
239 240
240extern int cgit_diff_files(const unsigned char *old_sha1, 241extern int cgit_diff_files(const unsigned char *old_sha1,
241 const unsigned char *new_sha1, 242 const unsigned char *new_sha1,
242 unsigned long *old_size, unsigned long *new_size, 243 unsigned long *old_size, unsigned long *new_size,
243 int *binary, linediff_fn fn); 244 int *binary, linediff_fn fn);
244 245
245extern void cgit_diff_tree(const unsigned char *old_sha1, 246extern void cgit_diff_tree(const unsigned char *old_sha1,
246 const unsigned char *new_sha1, 247 const unsigned char *new_sha1,
247 filepair_fn fn, const char *prefix); 248 filepair_fn fn, const char *prefix);
248 249
249extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 250extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
250 251
251extern char *fmt(const char *format,...); 252extern char *fmt(const char *format,...);
252 253
253extern struct commitinfo *cgit_parse_commit(struct commit *commit); 254extern struct commitinfo *cgit_parse_commit(struct commit *commit);
254extern struct taginfo *cgit_parse_tag(struct tag *tag); 255extern struct taginfo *cgit_parse_tag(struct tag *tag);
255extern void cgit_parse_url(const char *url); 256extern void cgit_parse_url(const char *url);
256 257
257extern const char *cgit_repobasename(const char *reponame); 258extern const char *cgit_repobasename(const char *reponame);
258 259
259extern int cgit_parse_snapshots_mask(const char *str); 260extern int cgit_parse_snapshots_mask(const char *str);
260 261
261extern int cgit_open_filter(struct cgit_filter *filter); 262extern int cgit_open_filter(struct cgit_filter *filter);
262extern int cgit_close_filter(struct cgit_filter *filter); 263extern int cgit_close_filter(struct cgit_filter *filter);
263 264
264 265
265#endif /* CGIT_H */ 266#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index d420ad4..2efd6aa 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,411 +1,417 @@
1CGITRC(5) 1CGITRC(5)
2======== 2========
3 3
4 4
5NAME 5NAME
6---- 6----
7cgitrc - runtime configuration for cgit 7cgitrc - runtime configuration for cgit
8 8
9 9
10SYNOPSIS 10SYNOPSIS
11-------- 11--------
12Cgitrc contains all runtime settings for cgit, including the list of git 12Cgitrc contains all runtime settings for cgit, including the list of git
13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank 13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank
14lines, and lines starting with '#', are ignored. 14lines, and lines starting with '#', are ignored.
15 15
16 16
17GLOBAL SETTINGS 17GLOBAL SETTINGS
18--------------- 18---------------
19agefile:: 19agefile::
20 Specifies a path, relative to each repository path, which can be used 20 Specifies a path, relative to each repository path, which can be used
21 to specify the date and time of the youngest commit in the repository. 21 to specify the date and time of the youngest commit in the repository.
22 The first line in the file is used as input to the "parse_date" 22 The first line in the file is used as input to the "parse_date"
23 function in libgit. Recommended timestamp-format is "yyyy-mm-dd 23 function in libgit. Recommended timestamp-format is "yyyy-mm-dd
24 hh:mm:ss". Default value: "info/web/last-modified". 24 hh:mm:ss". Default value: "info/web/last-modified".
25 25
26cache-root:: 26cache-root::
27 Path used to store the cgit cache entries. Default value: 27 Path used to store the cgit cache entries. Default value:
28 "/var/cache/cgit". 28 "/var/cache/cgit".
29 29
30cache-dynamic-ttl:: 30cache-dynamic-ttl::
31 Number which specifies the time-to-live, in minutes, for the cached 31 Number which specifies the time-to-live, in minutes, for the cached
32 version of repository pages accessed without a fixed SHA1. Default 32 version of repository pages accessed without a fixed SHA1. Default
33 value: "5". 33 value: "5".
34 34
35cache-repo-ttl:: 35cache-repo-ttl::
36 Number which specifies the time-to-live, in minutes, for the cached 36 Number which specifies the time-to-live, in minutes, for the cached
37 version of the repository summary page. Default value: "5". 37 version of the repository summary page. Default value: "5".
38 38
39cache-root-ttl:: 39cache-root-ttl::
40 Number which specifies the time-to-live, in minutes, for the cached 40 Number which specifies the time-to-live, in minutes, for the cached
41 version of the repository index page. Default value: "5". 41 version of the repository index page. Default value: "5".
42 42
43cache-size:: 43cache-size::
44 The maximum number of entries in the cgit cache. Default value: "0" 44 The maximum number of entries in the cgit cache. Default value: "0"
45 (i.e. caching is disabled). 45 (i.e. caching is disabled).
46 46
47cache-static-ttl:: 47cache-static-ttl::
48 Number which specifies the time-to-live, in minutes, for the cached 48 Number which specifies the time-to-live, in minutes, for the cached
49 version of repository pages accessed with a fixed SHA1. Default value: 49 version of repository pages accessed with a fixed SHA1. Default value:
50 "5". 50 "5".
51 51
52clone-prefix:: 52clone-prefix::
53 Space-separated list of common prefixes which, when combined with a 53 Space-separated list of common prefixes which, when combined with a
54 repository url, generates valid clone urls for the repository. This 54 repository url, generates valid clone urls for the repository. This
55 setting is only used if `repo.clone-url` is unspecified. Default value: 55 setting is only used if `repo.clone-url` is unspecified. Default value:
56 none. 56 none.
57 57
58commit-filter::
59 Specifies a command which will be invoked to format commit messages.
60 The command will get the message on its STDIN, and the STDOUT from the
61 command will be included verbatim as the commit message, i.e. this can
62 be used to implement bugtracker integration. Default value: none.
63
58css:: 64css::
59 Url which specifies the css document to include in all cgit pages. 65 Url which specifies the css document to include in all cgit pages.
60 Default value: "/cgit.css". 66 Default value: "/cgit.css".
61 67
62embedded:: 68embedded::
63 Flag which, when set to "1", will make cgit generate a html fragment 69 Flag which, when set to "1", will make cgit generate a html fragment
64 suitable for embedding in other html pages. Default value: none. See 70 suitable for embedding in other html pages. Default value: none. See
65 also: "noheader". 71 also: "noheader".
66 72
67enable-index-links:: 73enable-index-links::
68 Flag which, when set to "1", will make cgit generate extra links for 74 Flag which, when set to "1", will make cgit generate extra links for
69 each repo in the repository index (specifically, to the "summary", 75 each repo in the repository index (specifically, to the "summary",
70 "commit" and "tree" pages). Default value: "0". 76 "commit" and "tree" pages). Default value: "0".
71 77
72enable-log-filecount:: 78enable-log-filecount::
73 Flag which, when set to "1", will make cgit print the number of 79 Flag which, when set to "1", will make cgit print the number of
74 modified files for each commit on the repository log page. Default 80 modified files for each commit on the repository log page. Default
75 value: "0". 81 value: "0".
76 82
77enable-log-linecount:: 83enable-log-linecount::
78 Flag which, when set to "1", will make cgit print the number of added 84 Flag which, when set to "1", will make cgit print the number of added
79 and removed lines for each commit on the repository log page. Default 85 and removed lines for each commit on the repository log page. Default
80 value: "0". 86 value: "0".
81 87
82favicon:: 88favicon::
83 Url used as link to a shortcut icon for cgit. If specified, it is 89 Url used as link to a shortcut icon for cgit. If specified, it is
84 suggested to use the value "/favicon.ico" since certain browsers will 90 suggested to use the value "/favicon.ico" since certain browsers will
85 ignore other values. Default value: none. 91 ignore other values. Default value: none.
86 92
87footer:: 93footer::
88 The content of the file specified with this option will be included 94 The content of the file specified with this option will be included
89 verbatim at the bottom of all pages (i.e. it replaces the standard 95 verbatim at the bottom of all pages (i.e. it replaces the standard
90 "generated by..." message. Default value: none. 96 "generated by..." message. Default value: none.
91 97
92head-include:: 98head-include::
93 The content of the file specified with this option will be included 99 The content of the file specified with this option will be included
94 verbatim in the html HEAD section on all pages. Default value: none. 100 verbatim in the html HEAD section on all pages. Default value: none.
95 101
96header:: 102header::
97 The content of the file specified with this option will be included 103 The content of the file specified with this option will be included
98 verbatim at the top of all pages. Default value: none. 104 verbatim at the top of all pages. Default value: none.
99 105
100include:: 106include::
101 Name of a configfile to include before the rest of the current config- 107 Name of a configfile to include before the rest of the current config-
102 file is parsed. Default value: none. 108 file is parsed. Default value: none.
103 109
104index-header:: 110index-header::
105 The content of the file specified with this option will be included 111 The content of the file specified with this option will be included
106 verbatim above the repository index. This setting is deprecated, and 112 verbatim above the repository index. This setting is deprecated, and
107 will not be supported by cgit-1.0 (use root-readme instead). Default 113 will not be supported by cgit-1.0 (use root-readme instead). Default
108 value: none. 114 value: none.
109 115
110index-info:: 116index-info::
111 The content of the file specified with this option will be included 117 The content of the file specified with this option will be included
112 verbatim below the heading on the repository index page. This setting 118 verbatim below the heading on the repository index page. This setting
113 is deprecated, and will not be supported by cgit-1.0 (use root-desc 119 is deprecated, and will not be supported by cgit-1.0 (use root-desc
114 instead). Default value: none. 120 instead). Default value: none.
115 121
116local-time:: 122local-time::
117 Flag which, if set to "1", makes cgit print commit and tag times in the 123 Flag which, if set to "1", makes cgit print commit and tag times in the
118 servers timezone. Default value: "0". 124 servers timezone. Default value: "0".
119 125
120logo:: 126logo::
121 Url which specifies the source of an image which will be used as a logo 127 Url which specifies the source of an image which will be used as a logo
122 on all cgit pages. 128 on all cgit pages.
123 129
124logo-link:: 130logo-link::
125 Url loaded when clicking on the cgit logo image. If unspecified the 131 Url loaded when clicking on the cgit logo image. If unspecified the
126 calculated url of the repository index page will be used. Default 132 calculated url of the repository index page will be used. Default
127 value: none. 133 value: none.
128 134
129max-commit-count:: 135max-commit-count::
130 Specifies the number of entries to list per page in "log" view. Default 136 Specifies the number of entries to list per page in "log" view. Default
131 value: "50". 137 value: "50".
132 138
133max-message-length:: 139max-message-length::
134 Specifies the maximum number of commit message characters to display in 140 Specifies the maximum number of commit message characters to display in
135 "log" view. Default value: "80". 141 "log" view. Default value: "80".
136 142
137max-repo-count:: 143max-repo-count::
138 Specifies the number of entries to list per page on therepository 144 Specifies the number of entries to list per page on therepository
139 index page. Default value: "50". 145 index page. Default value: "50".
140 146
141max-repodesc-length:: 147max-repodesc-length::
142 Specifies the maximum number of repo description characters to display 148 Specifies the maximum number of repo description characters to display
143 on the repository index page. Default value: "80". 149 on the repository index page. Default value: "80".
144 150
145max-stats:: 151max-stats::
146 Set the default maximum statistics period. Valid values are "week", 152 Set the default maximum statistics period. Valid values are "week",
147 "month", "quarter" and "year". If unspecified, statistics are 153 "month", "quarter" and "year". If unspecified, statistics are
148 disabled. Default value: none. See also: "repo.max-stats". 154 disabled. Default value: none. See also: "repo.max-stats".
149 155
150module-link:: 156module-link::
151 Text which will be used as the formatstring for a hyperlink when a 157 Text which will be used as the formatstring for a hyperlink when a
152 submodule is printed in a directory listing. The arguments for the 158 submodule is printed in a directory listing. The arguments for the
153 formatstring are the path and SHA1 of the submodule commit. Default 159 formatstring are the path and SHA1 of the submodule commit. Default
154 value: "./?repo=%s&page=commit&id=%s" 160 value: "./?repo=%s&page=commit&id=%s"
155 161
156nocache:: 162nocache::
157 If set to the value "1" caching will be disabled. This settings is 163 If set to the value "1" caching will be disabled. This settings is
158 deprecated, and will not be honored starting with cgit-1.0. Default 164 deprecated, and will not be honored starting with cgit-1.0. Default
159 value: "0". 165 value: "0".
160 166
161noheader:: 167noheader::
162 Flag which, when set to "1", will make cgit omit the standard header 168 Flag which, when set to "1", will make cgit omit the standard header
163 on all pages. Default value: none. See also: "embedded". 169 on all pages. Default value: none. See also: "embedded".
164 170
165renamelimit:: 171renamelimit::
166 Maximum number of files to consider when detecting renames. The value 172 Maximum number of files to consider when detecting renames. The value
167 "-1" uses the compiletime value in git (for further info, look at 173 "-1" uses the compiletime value in git (for further info, look at
168 `man git-diff`). Default value: "-1". 174 `man git-diff`). Default value: "-1".
169 175
170repo.group:: 176repo.group::
171 A value for the current repository group, which all repositories 177 A value for the current repository group, which all repositories
172 specified after this setting will inherit. Default value: none. 178 specified after this setting will inherit. Default value: none.
173 179
174robots:: 180robots::
175 Text used as content for the "robots" meta-tag. Default value: 181 Text used as content for the "robots" meta-tag. Default value:
176 "index, nofollow". 182 "index, nofollow".
177 183
178root-desc:: 184root-desc::
179 Text printed below the heading on the repository index page. Default 185 Text printed below the heading on the repository index page. Default
180 value: "a fast webinterface for the git dscm". 186 value: "a fast webinterface for the git dscm".
181 187
182root-readme:: 188root-readme::
183 The content of the file specified with this option will be included 189 The content of the file specified with this option will be included
184 verbatim below the "about" link on the repository index page. Default 190 verbatim below the "about" link on the repository index page. Default
185 value: none. 191 value: none.
186 192
187root-title:: 193root-title::
188 Text printed as heading on the repository index page. Default value: 194 Text printed as heading on the repository index page. Default value:
189 "Git Repository Browser". 195 "Git Repository Browser".
190 196
191snapshots:: 197snapshots::
192 Text which specifies the default (and allowed) set of snapshot formats 198 Text which specifies the default (and allowed) set of snapshot formats
193 supported by cgit. The value is a space-separated list of zero or more 199 supported by cgit. The value is a space-separated list of zero or more
194 of the following values: 200 of the following values:
195 "tar" uncompressed tar-file 201 "tar" uncompressed tar-file
196 "tar.gz"gzip-compressed tar-file 202 "tar.gz"gzip-compressed tar-file
197 "tar.bz2"bzip-compressed tar-file 203 "tar.bz2"bzip-compressed tar-file
198 "zip" zip-file 204 "zip" zip-file
199 Default value: none. 205 Default value: none.
200 206
201source-filter:: 207source-filter::
202 Specifies a command which will be invoked to format plaintext blobs 208 Specifies a command which will be invoked to format plaintext blobs
203 in the tree view. The command will get the blob content on its STDIN 209 in the tree view. The command will get the blob content on its STDIN
204 and the name of the blob as its only command line argument. The STDOUT 210 and the name of the blob as its only command line argument. The STDOUT
205 from the command will be included verbatim as the blob contents, i.e. 211 from the command will be included verbatim as the blob contents, i.e.
206 this can be used to implement e.g. syntax highlighting. Default value: 212 this can be used to implement e.g. syntax highlighting. Default value:
207 none. 213 none.
208 214
209summary-branches:: 215summary-branches::
210 Specifies the number of branches to display in the repository "summary" 216 Specifies the number of branches to display in the repository "summary"
211 view. Default value: "10". 217 view. Default value: "10".
212 218
213summary-log:: 219summary-log::
214 Specifies the number of log entries to display in the repository 220 Specifies the number of log entries to display in the repository
215 "summary" view. Default value: "10". 221 "summary" view. Default value: "10".
216 222
217summary-tags:: 223summary-tags::
218 Specifies the number of tags to display in the repository "summary" 224 Specifies the number of tags to display in the repository "summary"
219 view. Default value: "10". 225 view. Default value: "10".
220 226
221virtual-root:: 227virtual-root::
222 Url which, if specified, will be used as root for all cgit links. It 228 Url which, if specified, will be used as root for all cgit links. It
223 will also cause cgit to generate 'virtual urls', i.e. urls like 229 will also cause cgit to generate 'virtual urls', i.e. urls like
224 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 230 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
225 value: none. 231 value: none.
226 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 232 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
227 same kind of virtual urls, so this option will probably be deprecated. 233 same kind of virtual urls, so this option will probably be deprecated.
228 234
229REPOSITORY SETTINGS 235REPOSITORY SETTINGS
230------------------- 236-------------------
231repo.clone-url:: 237repo.clone-url::
232 A list of space-separated urls which can be used to clone this repo. 238 A list of space-separated urls which can be used to clone this repo.
233 Default value: none. 239 Default value: none.
234 240
235repo.defbranch:: 241repo.defbranch::
236 The name of the default branch for this repository. If no such branch 242 The name of the default branch for this repository. If no such branch
237 exists in the repository, the first branch name (when sorted) is used 243 exists in the repository, the first branch name (when sorted) is used
238 as default instead. Default value: "master". 244 as default instead. Default value: "master".
239 245
240repo.desc:: 246repo.desc::
241 The value to show as repository description. Default value: none. 247 The value to show as repository description. Default value: none.
242 248
243repo.enable-log-filecount:: 249repo.enable-log-filecount::
244 A flag which can be used to disable the global setting 250 A flag which can be used to disable the global setting
245 `enable-log-filecount'. Default value: none. 251 `enable-log-filecount'. Default value: none.
246 252
247repo.enable-log-linecount:: 253repo.enable-log-linecount::
248 A flag which can be used to disable the global setting 254 A flag which can be used to disable the global setting
249 `enable-log-linecount'. Default value: none. 255 `enable-log-linecount'. Default value: none.
250 256
251repo.max-stats:: 257repo.max-stats::
252 Override the default maximum statistics period. Valid values are equal 258 Override the default maximum statistics period. Valid values are equal
253 to the values specified for the global "max-stats" setting. Default 259 to the values specified for the global "max-stats" setting. Default
254 value: none. 260 value: none.
255 261
256repo.name:: 262repo.name::
257 The value to show as repository name. Default value: <repo.url>. 263 The value to show as repository name. Default value: <repo.url>.
258 264
259repo.owner:: 265repo.owner::
260 A value used to identify the owner of the repository. Default value: 266 A value used to identify the owner of the repository. Default value:
261 none. 267 none.
262 268
263repo.path:: 269repo.path::
264 An absolute path to the repository directory. For non-bare repositories 270 An absolute path to the repository directory. For non-bare repositories
265 this is the .git-directory. Default value: none. 271 this is the .git-directory. Default value: none.
266 272
267repo.readme:: 273repo.readme::
268 A path (relative to <repo.path>) which specifies a file to include 274 A path (relative to <repo.path>) which specifies a file to include
269 verbatim as the "About" page for this repo. Default value: none. 275 verbatim as the "About" page for this repo. Default value: none.
270 276
271repo.snapshots:: 277repo.snapshots::
272 A mask of allowed snapshot-formats for this repo, restricted by the 278 A mask of allowed snapshot-formats for this repo, restricted by the
273 "snapshots" global setting. Default value: <snapshots>. 279 "snapshots" global setting. Default value: <snapshots>.
274 280
275repo.url:: 281repo.url::
276 The relative url used to access the repository. This must be the first 282 The relative url used to access the repository. This must be the first
277 setting specified for each repo. Default value: none. 283 setting specified for each repo. Default value: none.
278 284
279 285
280EXAMPLE CGITRC FILE 286EXAMPLE CGITRC FILE
281------------------- 287-------------------
282 288
283.... 289....
284# Enable caching of up to 1000 output entriess 290# Enable caching of up to 1000 output entriess
285cache-size=1000 291cache-size=1000
286 292
287 293
288# Specify some default clone prefixes 294# Specify some default clone prefixes
289clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git 295clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
290 296
291# Specify the css url 297# Specify the css url
292css=/css/cgit.css 298css=/css/cgit.css
293 299
294 300
295# Show extra links for each repository on the index page 301# Show extra links for each repository on the index page
296enable-index-links=1 302enable-index-links=1
297 303
298 304
299# Show number of affected files per commit on the log pages 305# Show number of affected files per commit on the log pages
300enable-log-filecount=1 306enable-log-filecount=1
301 307
302 308
303# Show number of added/removed lines per commit on the log pages 309# Show number of added/removed lines per commit on the log pages
304enable-log-linecount=1 310enable-log-linecount=1
305 311
306 312
307# Add a cgit favicon 313# Add a cgit favicon
308favicon=/favicon.ico 314favicon=/favicon.ico
309 315
310 316
311# Use a custom logo 317# Use a custom logo
312logo=/img/mylogo.png 318logo=/img/mylogo.png
313 319
314 320
315# Enable statistics per week, month and quarter 321# Enable statistics per week, month and quarter
316max-stats=quarter 322max-stats=quarter
317 323
318 324
319# Set the title and heading of the repository index page 325# Set the title and heading of the repository index page
320root-title=foobar.com git repositories 326root-title=foobar.com git repositories
321 327
322 328
323# Set a subheading for the repository index page 329# Set a subheading for the repository index page
324root-desc=tracking the foobar development 330root-desc=tracking the foobar development
325 331
326 332
327# Include some more info about foobar.com on the index page 333# Include some more info about foobar.com on the index page
328root-readme=/var/www/htdocs/about.html 334root-readme=/var/www/htdocs/about.html
329 335
330 336
331# Allow download of tar.gz, tar.bz2 and zip-files 337# Allow download of tar.gz, tar.bz2 and zip-files
332snapshots=tar.gz tar.bz2 zip 338snapshots=tar.gz tar.bz2 zip
333 339
334 340
335## 341##
336## List of repositories. 342## List of repositories.
337## PS: Any repositories listed when repo.group is unset will not be 343## PS: Any repositories listed when repo.group is unset will not be
338## displayed under a group heading 344## displayed under a group heading
339## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') 345## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
340## and included like this: 346## and included like this:
341## include=/etc/cgitrepos 347## include=/etc/cgitrepos
342## 348##
343 349
344 350
345repo.url=foo 351repo.url=foo
346repo.path=/pub/git/foo.git 352repo.path=/pub/git/foo.git
347repo.desc=the master foo repository 353repo.desc=the master foo repository
348repo.owner=fooman@foobar.com 354repo.owner=fooman@foobar.com
349repo.readme=info/web/about.html 355repo.readme=info/web/about.html
350 356
351 357
352repo.url=bar 358repo.url=bar
353repo.path=/pub/git/bar.git 359repo.path=/pub/git/bar.git
354repo.desc=the bars for your foo 360repo.desc=the bars for your foo
355repo.owner=barman@foobar.com 361repo.owner=barman@foobar.com
356repo.readme=info/web/about.html 362repo.readme=info/web/about.html
357 363
358 364
359# The next repositories will be displayed under the 'extras' heading 365# The next repositories will be displayed under the 'extras' heading
360repo.group=extras 366repo.group=extras
361 367
362 368
363repo.url=baz 369repo.url=baz
364repo.path=/pub/git/baz.git 370repo.path=/pub/git/baz.git
365repo.desc=a set of extensions for bar users 371repo.desc=a set of extensions for bar users
366 372
367repo.url=wiz 373repo.url=wiz
368repo.path=/pub/git/wiz.git 374repo.path=/pub/git/wiz.git
369repo.desc=the wizard of foo 375repo.desc=the wizard of foo
370 376
371 377
372# Add some mirrored repositories 378# Add some mirrored repositories
373repo.group=mirrors 379repo.group=mirrors
374 380
375 381
376repo.url=git 382repo.url=git
377repo.path=/pub/git/git.git 383repo.path=/pub/git/git.git
378repo.desc=the dscm 384repo.desc=the dscm
379 385
380 386
381repo.url=linux 387repo.url=linux
382repo.path=/pub/git/linux.git 388repo.path=/pub/git/linux.git
383repo.desc=the kernel 389repo.desc=the kernel
384 390
385# Disable adhoc downloads of this repo 391# Disable adhoc downloads of this repo
386repo.snapshots=0 392repo.snapshots=0
387 393
388# Disable line-counts for this repo 394# Disable line-counts for this repo
389repo.enable-log-linecount=0 395repo.enable-log-linecount=0
390 396
391# Restrict the max statistics period for this repo 397# Restrict the max statistics period for this repo
392repo.max-stats=month 398repo.max-stats=month
393.... 399....
394 400
395 401
396BUGS 402BUGS
397---- 403----
398Comments currently cannot appear on the same line as a setting; the comment 404Comments currently cannot appear on the same line as a setting; the comment
399will be included as part of the value. E.g. this line: 405will be included as part of the value. E.g. this line:
400 406
401 robots=index # allow indexing 407 robots=index # allow indexing
402 408
403will generate the following html element: 409will generate the following html element:
404 410
405 <meta name='robots' content='index # allow indexing'/> 411 <meta name='robots' content='index # allow indexing'/>
406 412
407 413
408 414
409AUTHOR 415AUTHOR
410------ 416------
411Lars Hjemli <hjemli@gmail.com> 417Lars Hjemli <hjemli@gmail.com>
diff --git a/ui-commit.c b/ui-commit.c
index 41ce70e..ee0e139 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,106 +1,114 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
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#include "ui-diff.h" 12#include "ui-diff.h"
13#include "ui-log.h" 13#include "ui-log.h"
14 14
15void cgit_print_commit(char *hex) 15void cgit_print_commit(char *hex)
16{ 16{
17 struct commit *commit, *parent; 17 struct commit *commit, *parent;
18 struct commitinfo *info; 18 struct commitinfo *info;
19 struct commit_list *p; 19 struct commit_list *p;
20 unsigned char sha1[20]; 20 unsigned char sha1[20];
21 char *tmp; 21 char *tmp;
22 int parents = 0; 22 int parents = 0;
23 23
24 if (!hex) 24 if (!hex)
25 hex = ctx.qry.head; 25 hex = ctx.qry.head;
26 26
27 if (get_sha1(hex, sha1)) { 27 if (get_sha1(hex, sha1)) {
28 cgit_print_error(fmt("Bad object id: %s", hex)); 28 cgit_print_error(fmt("Bad object id: %s", hex));
29 return; 29 return;
30 } 30 }
31 commit = lookup_commit_reference(sha1); 31 commit = lookup_commit_reference(sha1);
32 if (!commit) { 32 if (!commit) {
33 cgit_print_error(fmt("Bad commit reference: %s", hex)); 33 cgit_print_error(fmt("Bad commit reference: %s", hex));
34 return; 34 return;
35 } 35 }
36 info = cgit_parse_commit(commit); 36 info = cgit_parse_commit(commit);
37 37
38 load_ref_decorations(); 38 load_ref_decorations();
39 39
40 html("<table summary='commit info' class='commit-info'>\n"); 40 html("<table summary='commit info' class='commit-info'>\n");
41 html("<tr><th>author</th><td>"); 41 html("<tr><th>author</th><td>");
42 html_txt(info->author); 42 html_txt(info->author);
43 html(" "); 43 html(" ");
44 html_txt(info->author_email); 44 html_txt(info->author_email);
45 html("</td><td class='right'>"); 45 html("</td><td class='right'>");
46 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time); 46 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time);
47 html("</td></tr>\n"); 47 html("</td></tr>\n");
48 html("<tr><th>committer</th><td>"); 48 html("<tr><th>committer</th><td>");
49 html_txt(info->committer); 49 html_txt(info->committer);
50 html(" "); 50 html(" ");
51 html_txt(info->committer_email); 51 html_txt(info->committer_email);
52 html("</td><td class='right'>"); 52 html("</td><td class='right'>");
53 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time); 53 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time);
54 html("</td></tr>\n"); 54 html("</td></tr>\n");
55 html("<tr><th>commit</th><td colspan='2' class='sha1'>"); 55 html("<tr><th>commit</th><td colspan='2' class='sha1'>");
56 tmp = sha1_to_hex(commit->object.sha1); 56 tmp = sha1_to_hex(commit->object.sha1);
57 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp); 57 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp);
58 html(" ("); 58 html(" (");
59 cgit_patch_link("patch", NULL, NULL, NULL, tmp); 59 cgit_patch_link("patch", NULL, NULL, NULL, tmp);
60 html(")</td></tr>\n"); 60 html(")</td></tr>\n");
61 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 61 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
62 tmp = xstrdup(hex); 62 tmp = xstrdup(hex);
63 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, 63 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
64 ctx.qry.head, tmp, NULL); 64 ctx.qry.head, tmp, NULL);
65 html("</td></tr>\n"); 65 html("</td></tr>\n");
66 for (p = commit->parents; p ; p = p->next) { 66 for (p = commit->parents; p ; p = p->next) {
67 parent = lookup_commit_reference(p->item->object.sha1); 67 parent = lookup_commit_reference(p->item->object.sha1);
68 if (!parent) { 68 if (!parent) {
69 html("<tr><td colspan='3'>"); 69 html("<tr><td colspan='3'>");
70 cgit_print_error("Error reading parent commit"); 70 cgit_print_error("Error reading parent commit");
71 html("</td></tr>"); 71 html("</td></tr>");
72 continue; 72 continue;
73 } 73 }
74 html("<tr><th>parent</th>" 74 html("<tr><th>parent</th>"
75 "<td colspan='2' class='sha1'>"); 75 "<td colspan='2' class='sha1'>");
76 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 76 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
77 ctx.qry.head, sha1_to_hex(p->item->object.sha1)); 77 ctx.qry.head, sha1_to_hex(p->item->object.sha1));
78 html(" ("); 78 html(" (");
79 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, 79 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
80 sha1_to_hex(p->item->object.sha1), NULL); 80 sha1_to_hex(p->item->object.sha1), NULL);
81 html(")</td></tr>"); 81 html(")</td></tr>");
82 parents++; 82 parents++;
83 } 83 }
84 if (ctx.repo->snapshots) { 84 if (ctx.repo->snapshots) {
85 html("<tr><th>download</th><td colspan='2' class='sha1'>"); 85 html("<tr><th>download</th><td colspan='2' class='sha1'>");
86 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, 86 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
87 hex, ctx.repo->snapshots); 87 hex, ctx.repo->snapshots);
88 html("</td></tr>"); 88 html("</td></tr>");
89 } 89 }
90 html("</table>\n"); 90 html("</table>\n");
91 html("<div class='commit-subject'>"); 91 html("<div class='commit-subject'>");
92 if (ctx.cfg.commit_filter)
93 cgit_open_filter(ctx.cfg.commit_filter);
92 html_txt(info->subject); 94 html_txt(info->subject);
95 if (ctx.cfg.commit_filter)
96 cgit_close_filter(ctx.cfg.commit_filter);
93 show_commit_decorations(commit); 97 show_commit_decorations(commit);
94 html("</div>"); 98 html("</div>");
95 html("<div class='commit-msg'>"); 99 html("<div class='commit-msg'>");
100 if (ctx.cfg.commit_filter)
101 cgit_open_filter(ctx.cfg.commit_filter);
96 html_txt(info->msg); 102 html_txt(info->msg);
103 if (ctx.cfg.commit_filter)
104 cgit_close_filter(ctx.cfg.commit_filter);
97 html("</div>"); 105 html("</div>");
98 if (parents < 3) { 106 if (parents < 3) {
99 if (parents) 107 if (parents)
100 tmp = sha1_to_hex(commit->parents->item->object.sha1); 108 tmp = sha1_to_hex(commit->parents->item->object.sha1);
101 else 109 else
102 tmp = NULL; 110 tmp = NULL;
103 cgit_print_diff(ctx.qry.sha1, tmp, NULL); 111 cgit_print_diff(ctx.qry.sha1, tmp, NULL);
104 } 112 }
105 cgit_free_commitinfo(info); 113 cgit_free_commitinfo(info);
106} 114}