summaryrefslogtreecommitdiffabout
path: root/cgit.c
authorLars Hjemli <hjemli@gmail.com>2008-12-06 16:38:19 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-12-06 16:38:19 (UTC)
commitf86a23ff537258d36bf8f1876fa7a4bede6673d8 (patch) (unidiff)
tree8328d415416058cdc5b0fd2c6564ddcab5766c7a /cgit.c
parent140012d7a8e51df5a9f9c556696778b86ade4fc9 (diff)
downloadcgit-f86a23ff537258d36bf8f1876fa7a4bede6673d8.zip
cgit-f86a23ff537258d36bf8f1876fa7a4bede6673d8.tar.gz
cgit-f86a23ff537258d36bf8f1876fa7a4bede6673d8.tar.bz2
Add a 'stats' page to each repo
This new page, which is disabled by default, can be used to print some statistics about the number of commits per period in the repository, where period can be either weeks, months, quarters or years. The function can be activated globally by setting 'enable-stats=1' in cgitrc and disabled for individual repos by setting 'repo.enable-stats=0'. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (limited to 'cgit.c') (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c6
1 files changed, 6 insertions, 0 deletions
diff --git a/cgit.c b/cgit.c
index c82587b..22b6d7c 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,472 +1,478 @@
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 "scan-tree.h" 15#include "scan-tree.h"
16 16
17const char *cgit_version = CGIT_VERSION; 17const char *cgit_version = CGIT_VERSION;
18 18
19void config_cb(const char *name, const char *value) 19void config_cb(const char *name, const char *value)
20{ 20{
21 if (!strcmp(name, "root-title")) 21 if (!strcmp(name, "root-title"))
22 ctx.cfg.root_title = xstrdup(value); 22 ctx.cfg.root_title = xstrdup(value);
23 else if (!strcmp(name, "root-desc")) 23 else if (!strcmp(name, "root-desc"))
24 ctx.cfg.root_desc = xstrdup(value); 24 ctx.cfg.root_desc = xstrdup(value);
25 else if (!strcmp(name, "root-readme")) 25 else if (!strcmp(name, "root-readme"))
26 ctx.cfg.root_readme = xstrdup(value); 26 ctx.cfg.root_readme = xstrdup(value);
27 else if (!strcmp(name, "css")) 27 else if (!strcmp(name, "css"))
28 ctx.cfg.css = xstrdup(value); 28 ctx.cfg.css = xstrdup(value);
29 else if (!strcmp(name, "favicon")) 29 else if (!strcmp(name, "favicon"))
30 ctx.cfg.favicon = xstrdup(value); 30 ctx.cfg.favicon = xstrdup(value);
31 else if (!strcmp(name, "footer")) 31 else if (!strcmp(name, "footer"))
32 ctx.cfg.footer = xstrdup(value); 32 ctx.cfg.footer = xstrdup(value);
33 else if (!strcmp(name, "logo")) 33 else if (!strcmp(name, "logo"))
34 ctx.cfg.logo = xstrdup(value); 34 ctx.cfg.logo = xstrdup(value);
35 else if (!strcmp(name, "index-header")) 35 else if (!strcmp(name, "index-header"))
36 ctx.cfg.index_header = xstrdup(value); 36 ctx.cfg.index_header = xstrdup(value);
37 else if (!strcmp(name, "index-info")) 37 else if (!strcmp(name, "index-info"))
38 ctx.cfg.index_info = xstrdup(value); 38 ctx.cfg.index_info = xstrdup(value);
39 else if (!strcmp(name, "logo-link")) 39 else if (!strcmp(name, "logo-link"))
40 ctx.cfg.logo_link = xstrdup(value); 40 ctx.cfg.logo_link = xstrdup(value);
41 else if (!strcmp(name, "module-link")) 41 else if (!strcmp(name, "module-link"))
42 ctx.cfg.module_link = xstrdup(value); 42 ctx.cfg.module_link = xstrdup(value);
43 else if (!strcmp(name, "virtual-root")) { 43 else if (!strcmp(name, "virtual-root")) {
44 ctx.cfg.virtual_root = trim_end(value, '/'); 44 ctx.cfg.virtual_root = trim_end(value, '/');
45 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 45 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
46 ctx.cfg.virtual_root = ""; 46 ctx.cfg.virtual_root = "";
47 } else if (!strcmp(name, "nocache")) 47 } else if (!strcmp(name, "nocache"))
48 ctx.cfg.nocache = atoi(value); 48 ctx.cfg.nocache = atoi(value);
49 else if (!strcmp(name, "snapshots")) 49 else if (!strcmp(name, "snapshots"))
50 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 50 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
51 else if (!strcmp(name, "enable-index-links")) 51 else if (!strcmp(name, "enable-index-links"))
52 ctx.cfg.enable_index_links = atoi(value); 52 ctx.cfg.enable_index_links = atoi(value);
53 else if (!strcmp(name, "enable-log-filecount")) 53 else if (!strcmp(name, "enable-log-filecount"))
54 ctx.cfg.enable_log_filecount = atoi(value); 54 ctx.cfg.enable_log_filecount = atoi(value);
55 else if (!strcmp(name, "enable-log-linecount")) 55 else if (!strcmp(name, "enable-log-linecount"))
56 ctx.cfg.enable_log_linecount = atoi(value); 56 ctx.cfg.enable_log_linecount = atoi(value);
57 else if (!strcmp(name, "enable-stats"))
58 ctx.cfg.enable_stats = atoi(value);
57 else if (!strcmp(name, "cache-size")) 59 else if (!strcmp(name, "cache-size"))
58 ctx.cfg.cache_size = atoi(value); 60 ctx.cfg.cache_size = atoi(value);
59 else if (!strcmp(name, "cache-root")) 61 else if (!strcmp(name, "cache-root"))
60 ctx.cfg.cache_root = xstrdup(value); 62 ctx.cfg.cache_root = xstrdup(value);
61 else if (!strcmp(name, "cache-root-ttl")) 63 else if (!strcmp(name, "cache-root-ttl"))
62 ctx.cfg.cache_root_ttl = atoi(value); 64 ctx.cfg.cache_root_ttl = atoi(value);
63 else if (!strcmp(name, "cache-repo-ttl")) 65 else if (!strcmp(name, "cache-repo-ttl"))
64 ctx.cfg.cache_repo_ttl = atoi(value); 66 ctx.cfg.cache_repo_ttl = atoi(value);
65 else if (!strcmp(name, "cache-static-ttl")) 67 else if (!strcmp(name, "cache-static-ttl"))
66 ctx.cfg.cache_static_ttl = atoi(value); 68 ctx.cfg.cache_static_ttl = atoi(value);
67 else if (!strcmp(name, "cache-dynamic-ttl")) 69 else if (!strcmp(name, "cache-dynamic-ttl"))
68 ctx.cfg.cache_dynamic_ttl = atoi(value); 70 ctx.cfg.cache_dynamic_ttl = atoi(value);
69 else if (!strcmp(name, "max-message-length")) 71 else if (!strcmp(name, "max-message-length"))
70 ctx.cfg.max_msg_len = atoi(value); 72 ctx.cfg.max_msg_len = atoi(value);
71 else if (!strcmp(name, "max-repodesc-length")) 73 else if (!strcmp(name, "max-repodesc-length"))
72 ctx.cfg.max_repodesc_len = atoi(value); 74 ctx.cfg.max_repodesc_len = atoi(value);
73 else if (!strcmp(name, "max-repo-count")) 75 else if (!strcmp(name, "max-repo-count"))
74 ctx.cfg.max_repo_count = atoi(value); 76 ctx.cfg.max_repo_count = atoi(value);
75 else if (!strcmp(name, "max-commit-count")) 77 else if (!strcmp(name, "max-commit-count"))
76 ctx.cfg.max_commit_count = atoi(value); 78 ctx.cfg.max_commit_count = atoi(value);
77 else if (!strcmp(name, "summary-log")) 79 else if (!strcmp(name, "summary-log"))
78 ctx.cfg.summary_log = atoi(value); 80 ctx.cfg.summary_log = atoi(value);
79 else if (!strcmp(name, "summary-branches")) 81 else if (!strcmp(name, "summary-branches"))
80 ctx.cfg.summary_branches = atoi(value); 82 ctx.cfg.summary_branches = atoi(value);
81 else if (!strcmp(name, "summary-tags")) 83 else if (!strcmp(name, "summary-tags"))
82 ctx.cfg.summary_tags = atoi(value); 84 ctx.cfg.summary_tags = atoi(value);
83 else if (!strcmp(name, "agefile")) 85 else if (!strcmp(name, "agefile"))
84 ctx.cfg.agefile = xstrdup(value); 86 ctx.cfg.agefile = xstrdup(value);
85 else if (!strcmp(name, "renamelimit")) 87 else if (!strcmp(name, "renamelimit"))
86 ctx.cfg.renamelimit = atoi(value); 88 ctx.cfg.renamelimit = atoi(value);
87 else if (!strcmp(name, "robots")) 89 else if (!strcmp(name, "robots"))
88 ctx.cfg.robots = xstrdup(value); 90 ctx.cfg.robots = xstrdup(value);
89 else if (!strcmp(name, "clone-prefix")) 91 else if (!strcmp(name, "clone-prefix"))
90 ctx.cfg.clone_prefix = xstrdup(value); 92 ctx.cfg.clone_prefix = xstrdup(value);
91 else if (!strcmp(name, "local-time")) 93 else if (!strcmp(name, "local-time"))
92 ctx.cfg.local_time = atoi(value); 94 ctx.cfg.local_time = atoi(value);
93 else if (!strcmp(name, "repo.group")) 95 else if (!strcmp(name, "repo.group"))
94 ctx.cfg.repo_group = xstrdup(value); 96 ctx.cfg.repo_group = xstrdup(value);
95 else if (!strcmp(name, "repo.url")) 97 else if (!strcmp(name, "repo.url"))
96 ctx.repo = cgit_add_repo(value); 98 ctx.repo = cgit_add_repo(value);
97 else if (!strcmp(name, "repo.name")) 99 else if (!strcmp(name, "repo.name"))
98 ctx.repo->name = xstrdup(value); 100 ctx.repo->name = xstrdup(value);
99 else if (ctx.repo && !strcmp(name, "repo.path")) 101 else if (ctx.repo && !strcmp(name, "repo.path"))
100 ctx.repo->path = trim_end(value, '/'); 102 ctx.repo->path = trim_end(value, '/');
101 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 103 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
102 ctx.repo->clone_url = xstrdup(value); 104 ctx.repo->clone_url = xstrdup(value);
103 else if (ctx.repo && !strcmp(name, "repo.desc")) 105 else if (ctx.repo && !strcmp(name, "repo.desc"))
104 ctx.repo->desc = xstrdup(value); 106 ctx.repo->desc = xstrdup(value);
105 else if (ctx.repo && !strcmp(name, "repo.owner")) 107 else if (ctx.repo && !strcmp(name, "repo.owner"))
106 ctx.repo->owner = xstrdup(value); 108 ctx.repo->owner = xstrdup(value);
107 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 109 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
108 ctx.repo->defbranch = xstrdup(value); 110 ctx.repo->defbranch = xstrdup(value);
109 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 111 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
110 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 112 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
111 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 113 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
112 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 114 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
113 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 115 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
114 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 116 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
117 else if (ctx.repo && !strcmp(name, "repo.enable-stats"))
118 ctx.repo->enable_stats = ctx.cfg.enable_stats && atoi(value);
115 else if (ctx.repo && !strcmp(name, "repo.module-link")) 119 else if (ctx.repo && !strcmp(name, "repo.module-link"))
116 ctx.repo->module_link= xstrdup(value); 120 ctx.repo->module_link= xstrdup(value);
117 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 121 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
118 if (*value == '/') 122 if (*value == '/')
119 ctx.repo->readme = xstrdup(value); 123 ctx.repo->readme = xstrdup(value);
120 else 124 else
121 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 125 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
122 } else if (!strcmp(name, "include")) 126 } else if (!strcmp(name, "include"))
123 parse_configfile(value, config_cb); 127 parse_configfile(value, config_cb);
124} 128}
125 129
126static void querystring_cb(const char *name, const char *value) 130static void querystring_cb(const char *name, const char *value)
127{ 131{
128 if (!strcmp(name,"r")) { 132 if (!strcmp(name,"r")) {
129 ctx.qry.repo = xstrdup(value); 133 ctx.qry.repo = xstrdup(value);
130 ctx.repo = cgit_get_repoinfo(value); 134 ctx.repo = cgit_get_repoinfo(value);
131 } else if (!strcmp(name, "p")) { 135 } else if (!strcmp(name, "p")) {
132 ctx.qry.page = xstrdup(value); 136 ctx.qry.page = xstrdup(value);
133 } else if (!strcmp(name, "url")) { 137 } else if (!strcmp(name, "url")) {
134 ctx.qry.url = xstrdup(value); 138 ctx.qry.url = xstrdup(value);
135 cgit_parse_url(value); 139 cgit_parse_url(value);
136 } else if (!strcmp(name, "qt")) { 140 } else if (!strcmp(name, "qt")) {
137 ctx.qry.grep = xstrdup(value); 141 ctx.qry.grep = xstrdup(value);
138 } else if (!strcmp(name, "q")) { 142 } else if (!strcmp(name, "q")) {
139 ctx.qry.search = xstrdup(value); 143 ctx.qry.search = xstrdup(value);
140 } else if (!strcmp(name, "h")) { 144 } else if (!strcmp(name, "h")) {
141 ctx.qry.head = xstrdup(value); 145 ctx.qry.head = xstrdup(value);
142 ctx.qry.has_symref = 1; 146 ctx.qry.has_symref = 1;
143 } else if (!strcmp(name, "id")) { 147 } else if (!strcmp(name, "id")) {
144 ctx.qry.sha1 = xstrdup(value); 148 ctx.qry.sha1 = xstrdup(value);
145 ctx.qry.has_sha1 = 1; 149 ctx.qry.has_sha1 = 1;
146 } else if (!strcmp(name, "id2")) { 150 } else if (!strcmp(name, "id2")) {
147 ctx.qry.sha2 = xstrdup(value); 151 ctx.qry.sha2 = xstrdup(value);
148 ctx.qry.has_sha1 = 1; 152 ctx.qry.has_sha1 = 1;
149 } else if (!strcmp(name, "ofs")) { 153 } else if (!strcmp(name, "ofs")) {
150 ctx.qry.ofs = atoi(value); 154 ctx.qry.ofs = atoi(value);
151 } else if (!strcmp(name, "path")) { 155 } else if (!strcmp(name, "path")) {
152 ctx.qry.path = trim_end(value, '/'); 156 ctx.qry.path = trim_end(value, '/');
153 } else if (!strcmp(name, "name")) { 157 } else if (!strcmp(name, "name")) {
154 ctx.qry.name = xstrdup(value); 158 ctx.qry.name = xstrdup(value);
155 } else if (!strcmp(name, "mimetype")) { 159 } else if (!strcmp(name, "mimetype")) {
156 ctx.qry.mimetype = xstrdup(value); 160 ctx.qry.mimetype = xstrdup(value);
161 } else if (!strcmp(name, "period")) {
162 ctx.qry.period = xstrdup(value);
157 } 163 }
158} 164}
159 165
160static void prepare_context(struct cgit_context *ctx) 166static void prepare_context(struct cgit_context *ctx)
161{ 167{
162 memset(ctx, 0, sizeof(ctx)); 168 memset(ctx, 0, sizeof(ctx));
163 ctx->cfg.agefile = "info/web/last-modified"; 169 ctx->cfg.agefile = "info/web/last-modified";
164 ctx->cfg.nocache = 0; 170 ctx->cfg.nocache = 0;
165 ctx->cfg.cache_size = 0; 171 ctx->cfg.cache_size = 0;
166 ctx->cfg.cache_dynamic_ttl = 5; 172 ctx->cfg.cache_dynamic_ttl = 5;
167 ctx->cfg.cache_max_create_time = 5; 173 ctx->cfg.cache_max_create_time = 5;
168 ctx->cfg.cache_repo_ttl = 5; 174 ctx->cfg.cache_repo_ttl = 5;
169 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 175 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
170 ctx->cfg.cache_root_ttl = 5; 176 ctx->cfg.cache_root_ttl = 5;
171 ctx->cfg.cache_static_ttl = -1; 177 ctx->cfg.cache_static_ttl = -1;
172 ctx->cfg.css = "/cgit.css"; 178 ctx->cfg.css = "/cgit.css";
173 ctx->cfg.logo = "/git-logo.png"; 179 ctx->cfg.logo = "/git-logo.png";
174 ctx->cfg.local_time = 0; 180 ctx->cfg.local_time = 0;
175 ctx->cfg.max_repo_count = 50; 181 ctx->cfg.max_repo_count = 50;
176 ctx->cfg.max_commit_count = 50; 182 ctx->cfg.max_commit_count = 50;
177 ctx->cfg.max_lock_attempts = 5; 183 ctx->cfg.max_lock_attempts = 5;
178 ctx->cfg.max_msg_len = 80; 184 ctx->cfg.max_msg_len = 80;
179 ctx->cfg.max_repodesc_len = 80; 185 ctx->cfg.max_repodesc_len = 80;
180 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 186 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
181 ctx->cfg.renamelimit = -1; 187 ctx->cfg.renamelimit = -1;
182 ctx->cfg.robots = "index, nofollow"; 188 ctx->cfg.robots = "index, nofollow";
183 ctx->cfg.root_title = "Git repository browser"; 189 ctx->cfg.root_title = "Git repository browser";
184 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 190 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
185 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 191 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
186 ctx->cfg.summary_branches = 10; 192 ctx->cfg.summary_branches = 10;
187 ctx->cfg.summary_log = 10; 193 ctx->cfg.summary_log = 10;
188 ctx->cfg.summary_tags = 10; 194 ctx->cfg.summary_tags = 10;
189 ctx->page.mimetype = "text/html"; 195 ctx->page.mimetype = "text/html";
190 ctx->page.charset = PAGE_ENCODING; 196 ctx->page.charset = PAGE_ENCODING;
191 ctx->page.filename = NULL; 197 ctx->page.filename = NULL;
192 ctx->page.size = 0; 198 ctx->page.size = 0;
193 ctx->page.modified = time(NULL); 199 ctx->page.modified = time(NULL);
194 ctx->page.expires = ctx->page.modified; 200 ctx->page.expires = ctx->page.modified;
195} 201}
196 202
197struct refmatch { 203struct refmatch {
198 char *req_ref; 204 char *req_ref;
199 char *first_ref; 205 char *first_ref;
200 int match; 206 int match;
201}; 207};
202 208
203int find_current_ref(const char *refname, const unsigned char *sha1, 209int find_current_ref(const char *refname, const unsigned char *sha1,
204 int flags, void *cb_data) 210 int flags, void *cb_data)
205{ 211{
206 struct refmatch *info; 212 struct refmatch *info;
207 213
208 info = (struct refmatch *)cb_data; 214 info = (struct refmatch *)cb_data;
209 if (!strcmp(refname, info->req_ref)) 215 if (!strcmp(refname, info->req_ref))
210 info->match = 1; 216 info->match = 1;
211 if (!info->first_ref) 217 if (!info->first_ref)
212 info->first_ref = xstrdup(refname); 218 info->first_ref = xstrdup(refname);
213 return info->match; 219 return info->match;
214} 220}
215 221
216char *find_default_branch(struct cgit_repo *repo) 222char *find_default_branch(struct cgit_repo *repo)
217{ 223{
218 struct refmatch info; 224 struct refmatch info;
219 char *ref; 225 char *ref;
220 226
221 info.req_ref = repo->defbranch; 227 info.req_ref = repo->defbranch;
222 info.first_ref = NULL; 228 info.first_ref = NULL;
223 info.match = 0; 229 info.match = 0;
224 for_each_branch_ref(find_current_ref, &info); 230 for_each_branch_ref(find_current_ref, &info);
225 if (info.match) 231 if (info.match)
226 ref = info.req_ref; 232 ref = info.req_ref;
227 else 233 else
228 ref = info.first_ref; 234 ref = info.first_ref;
229 if (ref) 235 if (ref)
230 ref = xstrdup(ref); 236 ref = xstrdup(ref);
231 return ref; 237 return ref;
232} 238}
233 239
234static int prepare_repo_cmd(struct cgit_context *ctx) 240static int prepare_repo_cmd(struct cgit_context *ctx)
235{ 241{
236 char *tmp; 242 char *tmp;
237 unsigned char sha1[20]; 243 unsigned char sha1[20];
238 int nongit = 0; 244 int nongit = 0;
239 245
240 setenv("GIT_DIR", ctx->repo->path, 1); 246 setenv("GIT_DIR", ctx->repo->path, 1);
241 setup_git_directory_gently(&nongit); 247 setup_git_directory_gently(&nongit);
242 if (nongit) { 248 if (nongit) {
243 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 249 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
244 "config error"); 250 "config error");
245 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 251 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
246 ctx->repo = NULL; 252 ctx->repo = NULL;
247 cgit_print_http_headers(ctx); 253 cgit_print_http_headers(ctx);
248 cgit_print_docstart(ctx); 254 cgit_print_docstart(ctx);
249 cgit_print_pageheader(ctx); 255 cgit_print_pageheader(ctx);
250 cgit_print_error(tmp); 256 cgit_print_error(tmp);
251 cgit_print_docend(); 257 cgit_print_docend();
252 return 1; 258 return 1;
253 } 259 }
254 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 260 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
255 261
256 if (!ctx->qry.head) { 262 if (!ctx->qry.head) {
257 ctx->qry.nohead = 1; 263 ctx->qry.nohead = 1;
258 ctx->qry.head = find_default_branch(ctx->repo); 264 ctx->qry.head = find_default_branch(ctx->repo);
259 ctx->repo->defbranch = ctx->qry.head; 265 ctx->repo->defbranch = ctx->qry.head;
260 } 266 }
261 267
262 if (!ctx->qry.head) { 268 if (!ctx->qry.head) {
263 cgit_print_http_headers(ctx); 269 cgit_print_http_headers(ctx);
264 cgit_print_docstart(ctx); 270 cgit_print_docstart(ctx);
265 cgit_print_pageheader(ctx); 271 cgit_print_pageheader(ctx);
266 cgit_print_error("Repository seems to be empty"); 272 cgit_print_error("Repository seems to be empty");
267 cgit_print_docend(); 273 cgit_print_docend();
268 return 1; 274 return 1;
269 } 275 }
270 276
271 if (get_sha1(ctx->qry.head, sha1)) { 277 if (get_sha1(ctx->qry.head, sha1)) {
272 tmp = xstrdup(ctx->qry.head); 278 tmp = xstrdup(ctx->qry.head);
273 ctx->qry.head = ctx->repo->defbranch; 279 ctx->qry.head = ctx->repo->defbranch;
274 cgit_print_http_headers(ctx); 280 cgit_print_http_headers(ctx);
275 cgit_print_docstart(ctx); 281 cgit_print_docstart(ctx);
276 cgit_print_pageheader(ctx); 282 cgit_print_pageheader(ctx);
277 cgit_print_error(fmt("Invalid branch: %s", tmp)); 283 cgit_print_error(fmt("Invalid branch: %s", tmp));
278 cgit_print_docend(); 284 cgit_print_docend();
279 return 1; 285 return 1;
280 } 286 }
281 return 0; 287 return 0;
282} 288}
283 289
284static void process_request(void *cbdata) 290static void process_request(void *cbdata)
285{ 291{
286 struct cgit_context *ctx = cbdata; 292 struct cgit_context *ctx = cbdata;
287 struct cgit_cmd *cmd; 293 struct cgit_cmd *cmd;
288 294
289 cmd = cgit_get_cmd(ctx); 295 cmd = cgit_get_cmd(ctx);
290 if (!cmd) { 296 if (!cmd) {
291 ctx->page.title = "cgit error"; 297 ctx->page.title = "cgit error";
292 ctx->repo = NULL; 298 ctx->repo = NULL;
293 cgit_print_http_headers(ctx); 299 cgit_print_http_headers(ctx);
294 cgit_print_docstart(ctx); 300 cgit_print_docstart(ctx);
295 cgit_print_pageheader(ctx); 301 cgit_print_pageheader(ctx);
296 cgit_print_error("Invalid request"); 302 cgit_print_error("Invalid request");
297 cgit_print_docend(); 303 cgit_print_docend();
298 return; 304 return;
299 } 305 }
300 306
301 if (cmd->want_repo && !ctx->repo) { 307 if (cmd->want_repo && !ctx->repo) {
302 cgit_print_http_headers(ctx); 308 cgit_print_http_headers(ctx);
303 cgit_print_docstart(ctx); 309 cgit_print_docstart(ctx);
304 cgit_print_pageheader(ctx); 310 cgit_print_pageheader(ctx);
305 cgit_print_error(fmt("No repository selected")); 311 cgit_print_error(fmt("No repository selected"));
306 cgit_print_docend(); 312 cgit_print_docend();
307 return; 313 return;
308 } 314 }
309 315
310 if (ctx->repo && prepare_repo_cmd(ctx)) 316 if (ctx->repo && prepare_repo_cmd(ctx))
311 return; 317 return;
312 318
313 if (cmd->want_layout) { 319 if (cmd->want_layout) {
314 cgit_print_http_headers(ctx); 320 cgit_print_http_headers(ctx);
315 cgit_print_docstart(ctx); 321 cgit_print_docstart(ctx);
316 cgit_print_pageheader(ctx); 322 cgit_print_pageheader(ctx);
317 } 323 }
318 324
319 cmd->fn(ctx); 325 cmd->fn(ctx);
320 326
321 if (cmd->want_layout) 327 if (cmd->want_layout)
322 cgit_print_docend(); 328 cgit_print_docend();
323} 329}
324 330
325int cmp_repos(const void *a, const void *b) 331int cmp_repos(const void *a, const void *b)
326{ 332{
327 const struct cgit_repo *ra = a, *rb = b; 333 const struct cgit_repo *ra = a, *rb = b;
328 return strcmp(ra->url, rb->url); 334 return strcmp(ra->url, rb->url);
329} 335}
330 336
331void print_repo(struct cgit_repo *repo) 337void print_repo(struct cgit_repo *repo)
332{ 338{
333 printf("repo.url=%s\n", repo->url); 339 printf("repo.url=%s\n", repo->url);
334 printf("repo.name=%s\n", repo->name); 340 printf("repo.name=%s\n", repo->name);
335 printf("repo.path=%s\n", repo->path); 341 printf("repo.path=%s\n", repo->path);
336 if (repo->owner) 342 if (repo->owner)
337 printf("repo.owner=%s\n", repo->owner); 343 printf("repo.owner=%s\n", repo->owner);
338 if (repo->desc) 344 if (repo->desc)
339 printf("repo.desc=%s\n", repo->desc); 345 printf("repo.desc=%s\n", repo->desc);
340 if (repo->readme) 346 if (repo->readme)
341 printf("repo.readme=%s\n", repo->readme); 347 printf("repo.readme=%s\n", repo->readme);
342 printf("\n"); 348 printf("\n");
343} 349}
344 350
345void print_repolist(struct cgit_repolist *list) 351void print_repolist(struct cgit_repolist *list)
346{ 352{
347 int i; 353 int i;
348 354
349 for(i = 0; i < list->count; i++) 355 for(i = 0; i < list->count; i++)
350 print_repo(&list->repos[i]); 356 print_repo(&list->repos[i]);
351} 357}
352 358
353 359
354static void cgit_parse_args(int argc, const char **argv) 360static void cgit_parse_args(int argc, const char **argv)
355{ 361{
356 int i; 362 int i;
357 int scan = 0; 363 int scan = 0;
358 364
359 for (i = 1; i < argc; i++) { 365 for (i = 1; i < argc; i++) {
360 if (!strncmp(argv[i], "--cache=", 8)) { 366 if (!strncmp(argv[i], "--cache=", 8)) {
361 ctx.cfg.cache_root = xstrdup(argv[i]+8); 367 ctx.cfg.cache_root = xstrdup(argv[i]+8);
362 } 368 }
363 if (!strcmp(argv[i], "--nocache")) { 369 if (!strcmp(argv[i], "--nocache")) {
364 ctx.cfg.nocache = 1; 370 ctx.cfg.nocache = 1;
365 } 371 }
366 if (!strncmp(argv[i], "--query=", 8)) { 372 if (!strncmp(argv[i], "--query=", 8)) {
367 ctx.qry.raw = xstrdup(argv[i]+8); 373 ctx.qry.raw = xstrdup(argv[i]+8);
368 } 374 }
369 if (!strncmp(argv[i], "--repo=", 7)) { 375 if (!strncmp(argv[i], "--repo=", 7)) {
370 ctx.qry.repo = xstrdup(argv[i]+7); 376 ctx.qry.repo = xstrdup(argv[i]+7);
371 } 377 }
372 if (!strncmp(argv[i], "--page=", 7)) { 378 if (!strncmp(argv[i], "--page=", 7)) {
373 ctx.qry.page = xstrdup(argv[i]+7); 379 ctx.qry.page = xstrdup(argv[i]+7);
374 } 380 }
375 if (!strncmp(argv[i], "--head=", 7)) { 381 if (!strncmp(argv[i], "--head=", 7)) {
376 ctx.qry.head = xstrdup(argv[i]+7); 382 ctx.qry.head = xstrdup(argv[i]+7);
377 ctx.qry.has_symref = 1; 383 ctx.qry.has_symref = 1;
378 } 384 }
379 if (!strncmp(argv[i], "--sha1=", 7)) { 385 if (!strncmp(argv[i], "--sha1=", 7)) {
380 ctx.qry.sha1 = xstrdup(argv[i]+7); 386 ctx.qry.sha1 = xstrdup(argv[i]+7);
381 ctx.qry.has_sha1 = 1; 387 ctx.qry.has_sha1 = 1;
382 } 388 }
383 if (!strncmp(argv[i], "--ofs=", 6)) { 389 if (!strncmp(argv[i], "--ofs=", 6)) {
384 ctx.qry.ofs = atoi(argv[i]+6); 390 ctx.qry.ofs = atoi(argv[i]+6);
385 } 391 }
386 if (!strncmp(argv[i], "--scan-tree=", 12)) { 392 if (!strncmp(argv[i], "--scan-tree=", 12)) {
387 scan++; 393 scan++;
388 scan_tree(argv[i] + 12); 394 scan_tree(argv[i] + 12);
389 } 395 }
390 } 396 }
391 if (scan) { 397 if (scan) {
392 qsort(cgit_repolist.repos, cgit_repolist.count, 398 qsort(cgit_repolist.repos, cgit_repolist.count,
393 sizeof(struct cgit_repo), cmp_repos); 399 sizeof(struct cgit_repo), cmp_repos);
394 print_repolist(&cgit_repolist); 400 print_repolist(&cgit_repolist);
395 exit(0); 401 exit(0);
396 } 402 }
397} 403}
398 404
399static int calc_ttl() 405static int calc_ttl()
400{ 406{
401 if (!ctx.repo) 407 if (!ctx.repo)
402 return ctx.cfg.cache_root_ttl; 408 return ctx.cfg.cache_root_ttl;
403 409
404 if (!ctx.qry.page) 410 if (!ctx.qry.page)
405 return ctx.cfg.cache_repo_ttl; 411 return ctx.cfg.cache_repo_ttl;
406 412
407 if (ctx.qry.has_symref) 413 if (ctx.qry.has_symref)
408 return ctx.cfg.cache_dynamic_ttl; 414 return ctx.cfg.cache_dynamic_ttl;
409 415
410 if (ctx.qry.has_sha1) 416 if (ctx.qry.has_sha1)
411 return ctx.cfg.cache_static_ttl; 417 return ctx.cfg.cache_static_ttl;
412 418
413 return ctx.cfg.cache_repo_ttl; 419 return ctx.cfg.cache_repo_ttl;
414} 420}
415 421
416int main(int argc, const char **argv) 422int main(int argc, const char **argv)
417{ 423{
418 const char *cgit_config_env = getenv("CGIT_CONFIG"); 424 const char *cgit_config_env = getenv("CGIT_CONFIG");
419 const char *path; 425 const char *path;
420 char *qry; 426 char *qry;
421 int err, ttl; 427 int err, ttl;
422 428
423 prepare_context(&ctx); 429 prepare_context(&ctx);
424 cgit_repolist.length = 0; 430 cgit_repolist.length = 0;
425 cgit_repolist.count = 0; 431 cgit_repolist.count = 0;
426 cgit_repolist.repos = NULL; 432 cgit_repolist.repos = NULL;
427 433
428 if (getenv("SCRIPT_NAME")) 434 if (getenv("SCRIPT_NAME"))
429 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); 435 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
430 if (getenv("QUERY_STRING")) 436 if (getenv("QUERY_STRING"))
431 ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); 437 ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
432 cgit_parse_args(argc, argv); 438 cgit_parse_args(argc, argv);
433 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 439 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
434 config_cb); 440 config_cb);
435 ctx.repo = NULL; 441 ctx.repo = NULL;
436 http_parse_querystring(ctx.qry.raw, querystring_cb); 442 http_parse_querystring(ctx.qry.raw, querystring_cb);
437 443
438 /* If virtual-root isn't specified in cgitrc and no url 444 /* If virtual-root isn't specified in cgitrc and no url
439 * parameter is specified on the querystring, lets pretend 445 * parameter is specified on the querystring, lets pretend
440 * that virtualroot equals SCRIPT_NAME and use PATH_INFO as 446 * that virtualroot equals SCRIPT_NAME and use PATH_INFO as
441 * url. This allows cgit to work with virtual urls without 447 * url. This allows cgit to work with virtual urls without
442 * the need for rewriterules in the webserver (as long as 448 * the need for rewriterules in the webserver (as long as
443 * PATH_INFO is included in the cache lookup key). 449 * PATH_INFO is included in the cache lookup key).
444 */ 450 */
445 if (!ctx.cfg.virtual_root && !ctx.qry.url) { 451 if (!ctx.cfg.virtual_root && !ctx.qry.url) {
446 ctx.cfg.virtual_root = ctx.cfg.script_name; 452 ctx.cfg.virtual_root = ctx.cfg.script_name;
447 path = getenv("PATH_INFO"); 453 path = getenv("PATH_INFO");
448 if (path) { 454 if (path) {
449 if (path[0] == '/') 455 if (path[0] == '/')
450 path++; 456 path++;
451 ctx.qry.url = xstrdup(path); 457 ctx.qry.url = xstrdup(path);
452 if (ctx.qry.raw) { 458 if (ctx.qry.raw) {
453 qry = ctx.qry.raw; 459 qry = ctx.qry.raw;
454 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 460 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
455 free(qry); 461 free(qry);
456 } else 462 } else
457 ctx.qry.raw = ctx.qry.url; 463 ctx.qry.raw = ctx.qry.url;
458 cgit_parse_url(ctx.qry.url); 464 cgit_parse_url(ctx.qry.url);
459 } 465 }
460 } 466 }
461 467
462 ttl = calc_ttl(); 468 ttl = calc_ttl();
463 ctx.page.expires += ttl*60; 469 ctx.page.expires += ttl*60;
464 if (ctx.cfg.nocache) 470 if (ctx.cfg.nocache)
465 ctx.cfg.cache_size = 0; 471 ctx.cfg.cache_size = 0;
466 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 472 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
467 ctx.qry.raw, ttl, process_request, &ctx); 473 ctx.qry.raw, ttl, process_request, &ctx);
468 if (err) 474 if (err)
469 cgit_print_error(fmt("Error processing page: %s (%d)", 475 cgit_print_error(fmt("Error processing page: %s (%d)",
470 strerror(err), err)); 476 strerror(err), err));
471 return err; 477 return err;
472} 478}