summaryrefslogtreecommitdiffabout
path: root/cgit.c
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 /cgit.c
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 (limited to 'cgit.c') (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
1 files changed, 2 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}