summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgitrc.5.txt3
2 files changed, 5 insertions, 0 deletions
diff --git a/cgit.c b/cgit.c
index 013a0fe..7264b8e 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,634 +1,636 @@
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
20void add_mimetype(const char *name, const char *value) 20void add_mimetype(const char *name, const char *value)
21{ 21{
22 struct string_list_item *item; 22 struct string_list_item *item;
23 23
24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes); 24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);
25 item->util = xstrdup(value); 25 item->util = xstrdup(value);
26} 26}
27 27
28struct cgit_filter *new_filter(const char *cmd, int extra_args) 28struct cgit_filter *new_filter(const char *cmd, int extra_args)
29{ 29{
30 struct cgit_filter *f; 30 struct cgit_filter *f;
31 31
32 if (!cmd || !cmd[0]) 32 if (!cmd || !cmd[0])
33 return NULL; 33 return NULL;
34 34
35 f = xmalloc(sizeof(struct cgit_filter)); 35 f = xmalloc(sizeof(struct cgit_filter));
36 f->cmd = xstrdup(cmd); 36 f->cmd = xstrdup(cmd);
37 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 37 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
38 f->argv[0] = f->cmd; 38 f->argv[0] = f->cmd;
39 f->argv[1] = NULL; 39 f->argv[1] = NULL;
40 return f; 40 return f;
41} 41}
42 42
43static void process_cached_repolist(const char *path); 43static void process_cached_repolist(const char *path);
44 44
45void config_cb(const char *name, const char *value) 45void config_cb(const char *name, const char *value)
46{ 46{
47 if (!strcmp(name, "root-title")) 47 if (!strcmp(name, "root-title"))
48 ctx.cfg.root_title = xstrdup(value); 48 ctx.cfg.root_title = xstrdup(value);
49 else if (!strcmp(name, "root-desc")) 49 else if (!strcmp(name, "root-desc"))
50 ctx.cfg.root_desc = xstrdup(value); 50 ctx.cfg.root_desc = xstrdup(value);
51 else if (!strcmp(name, "root-readme")) 51 else if (!strcmp(name, "root-readme"))
52 ctx.cfg.root_readme = xstrdup(value); 52 ctx.cfg.root_readme = xstrdup(value);
53 else if (!strcmp(name, "css")) 53 else if (!strcmp(name, "css"))
54 ctx.cfg.css = xstrdup(value); 54 ctx.cfg.css = xstrdup(value);
55 else if (!strcmp(name, "favicon")) 55 else if (!strcmp(name, "favicon"))
56 ctx.cfg.favicon = xstrdup(value); 56 ctx.cfg.favicon = xstrdup(value);
57 else if (!strcmp(name, "footer")) 57 else if (!strcmp(name, "footer"))
58 ctx.cfg.footer = xstrdup(value); 58 ctx.cfg.footer = xstrdup(value);
59 else if (!strcmp(name, "head-include")) 59 else if (!strcmp(name, "head-include"))
60 ctx.cfg.head_include = xstrdup(value); 60 ctx.cfg.head_include = xstrdup(value);
61 else if (!strcmp(name, "header")) 61 else if (!strcmp(name, "header"))
62 ctx.cfg.header = xstrdup(value); 62 ctx.cfg.header = xstrdup(value);
63 else if (!strcmp(name, "logo")) 63 else if (!strcmp(name, "logo"))
64 ctx.cfg.logo = xstrdup(value); 64 ctx.cfg.logo = xstrdup(value);
65 else if (!strcmp(name, "index-header")) 65 else if (!strcmp(name, "index-header"))
66 ctx.cfg.index_header = xstrdup(value); 66 ctx.cfg.index_header = xstrdup(value);
67 else if (!strcmp(name, "index-info")) 67 else if (!strcmp(name, "index-info"))
68 ctx.cfg.index_info = xstrdup(value); 68 ctx.cfg.index_info = xstrdup(value);
69 else if (!strcmp(name, "logo-link")) 69 else if (!strcmp(name, "logo-link"))
70 ctx.cfg.logo_link = xstrdup(value); 70 ctx.cfg.logo_link = xstrdup(value);
71 else if (!strcmp(name, "module-link")) 71 else if (!strcmp(name, "module-link"))
72 ctx.cfg.module_link = xstrdup(value); 72 ctx.cfg.module_link = xstrdup(value);
73 else if (!strcmp(name, "virtual-root")) { 73 else if (!strcmp(name, "virtual-root")) {
74 ctx.cfg.virtual_root = trim_end(value, '/'); 74 ctx.cfg.virtual_root = trim_end(value, '/');
75 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 75 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
76 ctx.cfg.virtual_root = ""; 76 ctx.cfg.virtual_root = "";
77 } else if (!strcmp(name, "nocache")) 77 } else if (!strcmp(name, "nocache"))
78 ctx.cfg.nocache = atoi(value); 78 ctx.cfg.nocache = atoi(value);
79 else if (!strcmp(name, "noplainemail")) 79 else if (!strcmp(name, "noplainemail"))
80 ctx.cfg.noplainemail = atoi(value); 80 ctx.cfg.noplainemail = atoi(value);
81 else if (!strcmp(name, "noheader")) 81 else if (!strcmp(name, "noheader"))
82 ctx.cfg.noheader = atoi(value); 82 ctx.cfg.noheader = atoi(value);
83 else if (!strcmp(name, "snapshots")) 83 else if (!strcmp(name, "snapshots"))
84 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 84 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
85 else if (!strcmp(name, "enable-index-links")) 85 else if (!strcmp(name, "enable-index-links"))
86 ctx.cfg.enable_index_links = atoi(value); 86 ctx.cfg.enable_index_links = atoi(value);
87 else if (!strcmp(name, "enable-log-filecount")) 87 else if (!strcmp(name, "enable-log-filecount"))
88 ctx.cfg.enable_log_filecount = atoi(value); 88 ctx.cfg.enable_log_filecount = atoi(value);
89 else if (!strcmp(name, "enable-log-linecount")) 89 else if (!strcmp(name, "enable-log-linecount"))
90 ctx.cfg.enable_log_linecount = atoi(value); 90 ctx.cfg.enable_log_linecount = atoi(value);
91 else if (!strcmp(name, "max-stats")) 91 else if (!strcmp(name, "max-stats"))
92 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 92 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
93 else if (!strcmp(name, "cache-size")) 93 else if (!strcmp(name, "cache-size"))
94 ctx.cfg.cache_size = atoi(value); 94 ctx.cfg.cache_size = atoi(value);
95 else if (!strcmp(name, "cache-root")) 95 else if (!strcmp(name, "cache-root"))
96 ctx.cfg.cache_root = xstrdup(value); 96 ctx.cfg.cache_root = xstrdup(value);
97 else if (!strcmp(name, "cache-root-ttl")) 97 else if (!strcmp(name, "cache-root-ttl"))
98 ctx.cfg.cache_root_ttl = atoi(value); 98 ctx.cfg.cache_root_ttl = atoi(value);
99 else if (!strcmp(name, "cache-repo-ttl")) 99 else if (!strcmp(name, "cache-repo-ttl"))
100 ctx.cfg.cache_repo_ttl = atoi(value); 100 ctx.cfg.cache_repo_ttl = atoi(value);
101 else if (!strcmp(name, "cache-scanrc-ttl")) 101 else if (!strcmp(name, "cache-scanrc-ttl"))
102 ctx.cfg.cache_scanrc_ttl = atoi(value); 102 ctx.cfg.cache_scanrc_ttl = atoi(value);
103 else if (!strcmp(name, "cache-static-ttl")) 103 else if (!strcmp(name, "cache-static-ttl"))
104 ctx.cfg.cache_static_ttl = atoi(value); 104 ctx.cfg.cache_static_ttl = atoi(value);
105 else if (!strcmp(name, "cache-dynamic-ttl")) 105 else if (!strcmp(name, "cache-dynamic-ttl"))
106 ctx.cfg.cache_dynamic_ttl = atoi(value); 106 ctx.cfg.cache_dynamic_ttl = atoi(value);
107 else if (!strcmp(name, "about-filter")) 107 else if (!strcmp(name, "about-filter"))
108 ctx.cfg.about_filter = new_filter(value, 0); 108 ctx.cfg.about_filter = new_filter(value, 0);
109 else if (!strcmp(name, "commit-filter")) 109 else if (!strcmp(name, "commit-filter"))
110 ctx.cfg.commit_filter = new_filter(value, 0); 110 ctx.cfg.commit_filter = new_filter(value, 0);
111 else if (!strcmp(name, "embedded")) 111 else if (!strcmp(name, "embedded"))
112 ctx.cfg.embedded = atoi(value); 112 ctx.cfg.embedded = atoi(value);
113 else if (!strcmp(name, "max-message-length")) 113 else if (!strcmp(name, "max-message-length"))
114 ctx.cfg.max_msg_len = atoi(value); 114 ctx.cfg.max_msg_len = atoi(value);
115 else if (!strcmp(name, "max-repodesc-length")) 115 else if (!strcmp(name, "max-repodesc-length"))
116 ctx.cfg.max_repodesc_len = atoi(value); 116 ctx.cfg.max_repodesc_len = atoi(value);
117 else if (!strcmp(name, "max-repo-count")) 117 else if (!strcmp(name, "max-repo-count"))
118 ctx.cfg.max_repo_count = atoi(value); 118 ctx.cfg.max_repo_count = atoi(value);
119 else if (!strcmp(name, "max-commit-count")) 119 else if (!strcmp(name, "max-commit-count"))
120 ctx.cfg.max_commit_count = atoi(value); 120 ctx.cfg.max_commit_count = atoi(value);
121 else if (!strcmp(name, "scan-path")) 121 else if (!strcmp(name, "scan-path"))
122 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 122 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
123 process_cached_repolist(value); 123 process_cached_repolist(value);
124 else 124 else
125 scan_tree(value); 125 scan_tree(value);
126 else if (!strcmp(name, "source-filter")) 126 else if (!strcmp(name, "source-filter"))
127 ctx.cfg.source_filter = new_filter(value, 1); 127 ctx.cfg.source_filter = new_filter(value, 1);
128 else if (!strcmp(name, "summary-log")) 128 else if (!strcmp(name, "summary-log"))
129 ctx.cfg.summary_log = atoi(value); 129 ctx.cfg.summary_log = atoi(value);
130 else if (!strcmp(name, "summary-branches")) 130 else if (!strcmp(name, "summary-branches"))
131 ctx.cfg.summary_branches = atoi(value); 131 ctx.cfg.summary_branches = atoi(value);
132 else if (!strcmp(name, "summary-tags")) 132 else if (!strcmp(name, "summary-tags"))
133 ctx.cfg.summary_tags = atoi(value); 133 ctx.cfg.summary_tags = atoi(value);
134 else if (!strcmp(name, "agefile")) 134 else if (!strcmp(name, "agefile"))
135 ctx.cfg.agefile = xstrdup(value); 135 ctx.cfg.agefile = xstrdup(value);
136 else if (!strcmp(name, "renamelimit")) 136 else if (!strcmp(name, "renamelimit"))
137 ctx.cfg.renamelimit = atoi(value); 137 ctx.cfg.renamelimit = atoi(value);
138 else if (!strcmp(name, "robots")) 138 else if (!strcmp(name, "robots"))
139 ctx.cfg.robots = xstrdup(value); 139 ctx.cfg.robots = xstrdup(value);
140 else if (!strcmp(name, "clone-prefix")) 140 else if (!strcmp(name, "clone-prefix"))
141 ctx.cfg.clone_prefix = xstrdup(value); 141 ctx.cfg.clone_prefix = xstrdup(value);
142 else if (!strcmp(name, "local-time")) 142 else if (!strcmp(name, "local-time"))
143 ctx.cfg.local_time = atoi(value); 143 ctx.cfg.local_time = atoi(value);
144 else if (!prefixcmp(name, "mimetype.")) 144 else if (!prefixcmp(name, "mimetype."))
145 add_mimetype(name + 9, value); 145 add_mimetype(name + 9, value);
146 else if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 146 else if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
147 ctx.cfg.section = xstrdup(value); 147 ctx.cfg.section = xstrdup(value);
148 else if (!strcmp(name, "repo.url")) 148 else if (!strcmp(name, "repo.url"))
149 ctx.repo = cgit_add_repo(value); 149 ctx.repo = cgit_add_repo(value);
150 else if (!strcmp(name, "repo.name")) 150 else if (!strcmp(name, "repo.name"))
151 ctx.repo->name = xstrdup(value); 151 ctx.repo->name = xstrdup(value);
152 else if (ctx.repo && !strcmp(name, "repo.path")) 152 else if (ctx.repo && !strcmp(name, "repo.path"))
153 ctx.repo->path = trim_end(value, '/'); 153 ctx.repo->path = trim_end(value, '/');
154 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 154 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
155 ctx.repo->clone_url = xstrdup(value); 155 ctx.repo->clone_url = xstrdup(value);
156 else if (ctx.repo && !strcmp(name, "repo.desc")) 156 else if (ctx.repo && !strcmp(name, "repo.desc"))
157 ctx.repo->desc = xstrdup(value); 157 ctx.repo->desc = xstrdup(value);
158 else if (ctx.repo && !strcmp(name, "repo.owner")) 158 else if (ctx.repo && !strcmp(name, "repo.owner"))
159 ctx.repo->owner = xstrdup(value); 159 ctx.repo->owner = xstrdup(value);
160 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 160 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
161 ctx.repo->defbranch = xstrdup(value); 161 ctx.repo->defbranch = xstrdup(value);
162 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 162 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
163 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 163 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
164 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 164 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
165 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 165 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
166 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 166 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
167 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 167 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
168 else if (ctx.repo && !strcmp(name, "repo.max-stats")) 168 else if (ctx.repo && !strcmp(name, "repo.max-stats"))
169 ctx.repo->max_stats = cgit_find_stats_period(value, NULL); 169 ctx.repo->max_stats = cgit_find_stats_period(value, NULL);
170 else if (ctx.repo && !strcmp(name, "repo.module-link")) 170 else if (ctx.repo && !strcmp(name, "repo.module-link"))
171 ctx.repo->module_link= xstrdup(value); 171 ctx.repo->module_link= xstrdup(value);
172 else if (ctx.repo && !strcmp(name, "repo.section"))
173 ctx.repo->section = xstrdup(value);
172 else if (ctx.repo && !strcmp(name, "repo.about-filter")) 174 else if (ctx.repo && !strcmp(name, "repo.about-filter"))
173 ctx.repo->about_filter = new_filter(value, 0); 175 ctx.repo->about_filter = new_filter(value, 0);
174 else if (ctx.repo && !strcmp(name, "repo.commit-filter")) 176 else if (ctx.repo && !strcmp(name, "repo.commit-filter"))
175 ctx.repo->commit_filter = new_filter(value, 0); 177 ctx.repo->commit_filter = new_filter(value, 0);
176 else if (ctx.repo && !strcmp(name, "repo.source-filter")) 178 else if (ctx.repo && !strcmp(name, "repo.source-filter"))
177 ctx.repo->source_filter = new_filter(value, 1); 179 ctx.repo->source_filter = new_filter(value, 1);
178 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 180 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
179 if (*value == '/') 181 if (*value == '/')
180 ctx.repo->readme = xstrdup(value); 182 ctx.repo->readme = xstrdup(value);
181 else 183 else
182 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 184 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
183 } else if (!strcmp(name, "include")) 185 } else if (!strcmp(name, "include"))
184 parse_configfile(value, config_cb); 186 parse_configfile(value, config_cb);
185} 187}
186 188
187static void querystring_cb(const char *name, const char *value) 189static void querystring_cb(const char *name, const char *value)
188{ 190{
189 if (!value) 191 if (!value)
190 value = ""; 192 value = "";
191 193
192 if (!strcmp(name,"r")) { 194 if (!strcmp(name,"r")) {
193 ctx.qry.repo = xstrdup(value); 195 ctx.qry.repo = xstrdup(value);
194 ctx.repo = cgit_get_repoinfo(value); 196 ctx.repo = cgit_get_repoinfo(value);
195 } else if (!strcmp(name, "p")) { 197 } else if (!strcmp(name, "p")) {
196 ctx.qry.page = xstrdup(value); 198 ctx.qry.page = xstrdup(value);
197 } else if (!strcmp(name, "url")) { 199 } else if (!strcmp(name, "url")) {
198 ctx.qry.url = xstrdup(value); 200 ctx.qry.url = xstrdup(value);
199 cgit_parse_url(value); 201 cgit_parse_url(value);
200 } else if (!strcmp(name, "qt")) { 202 } else if (!strcmp(name, "qt")) {
201 ctx.qry.grep = xstrdup(value); 203 ctx.qry.grep = xstrdup(value);
202 } else if (!strcmp(name, "q")) { 204 } else if (!strcmp(name, "q")) {
203 ctx.qry.search = xstrdup(value); 205 ctx.qry.search = xstrdup(value);
204 } else if (!strcmp(name, "h")) { 206 } else if (!strcmp(name, "h")) {
205 ctx.qry.head = xstrdup(value); 207 ctx.qry.head = xstrdup(value);
206 ctx.qry.has_symref = 1; 208 ctx.qry.has_symref = 1;
207 } else if (!strcmp(name, "id")) { 209 } else if (!strcmp(name, "id")) {
208 ctx.qry.sha1 = xstrdup(value); 210 ctx.qry.sha1 = xstrdup(value);
209 ctx.qry.has_sha1 = 1; 211 ctx.qry.has_sha1 = 1;
210 } else if (!strcmp(name, "id2")) { 212 } else if (!strcmp(name, "id2")) {
211 ctx.qry.sha2 = xstrdup(value); 213 ctx.qry.sha2 = xstrdup(value);
212 ctx.qry.has_sha1 = 1; 214 ctx.qry.has_sha1 = 1;
213 } else if (!strcmp(name, "ofs")) { 215 } else if (!strcmp(name, "ofs")) {
214 ctx.qry.ofs = atoi(value); 216 ctx.qry.ofs = atoi(value);
215 } else if (!strcmp(name, "path")) { 217 } else if (!strcmp(name, "path")) {
216 ctx.qry.path = trim_end(value, '/'); 218 ctx.qry.path = trim_end(value, '/');
217 } else if (!strcmp(name, "name")) { 219 } else if (!strcmp(name, "name")) {
218 ctx.qry.name = xstrdup(value); 220 ctx.qry.name = xstrdup(value);
219 } else if (!strcmp(name, "mimetype")) { 221 } else if (!strcmp(name, "mimetype")) {
220 ctx.qry.mimetype = xstrdup(value); 222 ctx.qry.mimetype = xstrdup(value);
221 } else if (!strcmp(name, "s")){ 223 } else if (!strcmp(name, "s")){
222 ctx.qry.sort = xstrdup(value); 224 ctx.qry.sort = xstrdup(value);
223 } else if (!strcmp(name, "showmsg")) { 225 } else if (!strcmp(name, "showmsg")) {
224 ctx.qry.showmsg = atoi(value); 226 ctx.qry.showmsg = atoi(value);
225 } else if (!strcmp(name, "period")) { 227 } else if (!strcmp(name, "period")) {
226 ctx.qry.period = xstrdup(value); 228 ctx.qry.period = xstrdup(value);
227 } 229 }
228} 230}
229 231
230char *xstrdupn(const char *str) 232char *xstrdupn(const char *str)
231{ 233{
232 return (str ? xstrdup(str) : NULL); 234 return (str ? xstrdup(str) : NULL);
233} 235}
234 236
235static void prepare_context(struct cgit_context *ctx) 237static void prepare_context(struct cgit_context *ctx)
236{ 238{
237 memset(ctx, 0, sizeof(ctx)); 239 memset(ctx, 0, sizeof(ctx));
238 ctx->cfg.agefile = "info/web/last-modified"; 240 ctx->cfg.agefile = "info/web/last-modified";
239 ctx->cfg.nocache = 0; 241 ctx->cfg.nocache = 0;
240 ctx->cfg.cache_size = 0; 242 ctx->cfg.cache_size = 0;
241 ctx->cfg.cache_dynamic_ttl = 5; 243 ctx->cfg.cache_dynamic_ttl = 5;
242 ctx->cfg.cache_max_create_time = 5; 244 ctx->cfg.cache_max_create_time = 5;
243 ctx->cfg.cache_repo_ttl = 5; 245 ctx->cfg.cache_repo_ttl = 5;
244 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 246 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
245 ctx->cfg.cache_root_ttl = 5; 247 ctx->cfg.cache_root_ttl = 5;
246 ctx->cfg.cache_scanrc_ttl = 15; 248 ctx->cfg.cache_scanrc_ttl = 15;
247 ctx->cfg.cache_static_ttl = -1; 249 ctx->cfg.cache_static_ttl = -1;
248 ctx->cfg.css = "/cgit.css"; 250 ctx->cfg.css = "/cgit.css";
249 ctx->cfg.logo = "/cgit.png"; 251 ctx->cfg.logo = "/cgit.png";
250 ctx->cfg.local_time = 0; 252 ctx->cfg.local_time = 0;
251 ctx->cfg.max_repo_count = 50; 253 ctx->cfg.max_repo_count = 50;
252 ctx->cfg.max_commit_count = 50; 254 ctx->cfg.max_commit_count = 50;
253 ctx->cfg.max_lock_attempts = 5; 255 ctx->cfg.max_lock_attempts = 5;
254 ctx->cfg.max_msg_len = 80; 256 ctx->cfg.max_msg_len = 80;
255 ctx->cfg.max_repodesc_len = 80; 257 ctx->cfg.max_repodesc_len = 80;
256 ctx->cfg.max_stats = 0; 258 ctx->cfg.max_stats = 0;
257 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 259 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
258 ctx->cfg.renamelimit = -1; 260 ctx->cfg.renamelimit = -1;
259 ctx->cfg.robots = "index, nofollow"; 261 ctx->cfg.robots = "index, nofollow";
260 ctx->cfg.root_title = "Git repository browser"; 262 ctx->cfg.root_title = "Git repository browser";
261 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 263 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
262 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 264 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
263 ctx->cfg.summary_branches = 10; 265 ctx->cfg.summary_branches = 10;
264 ctx->cfg.summary_log = 10; 266 ctx->cfg.summary_log = 10;
265 ctx->cfg.summary_tags = 10; 267 ctx->cfg.summary_tags = 10;
266 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 268 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
267 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 269 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
268 ctx->env.https = xstrdupn(getenv("HTTPS")); 270 ctx->env.https = xstrdupn(getenv("HTTPS"));
269 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 271 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
270 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 272 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
271 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 273 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
272 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 274 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
273 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 275 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
274 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 276 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
275 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 277 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
276 ctx->page.mimetype = "text/html"; 278 ctx->page.mimetype = "text/html";
277 ctx->page.charset = PAGE_ENCODING; 279 ctx->page.charset = PAGE_ENCODING;
278 ctx->page.filename = NULL; 280 ctx->page.filename = NULL;
279 ctx->page.size = 0; 281 ctx->page.size = 0;
280 ctx->page.modified = time(NULL); 282 ctx->page.modified = time(NULL);
281 ctx->page.expires = ctx->page.modified; 283 ctx->page.expires = ctx->page.modified;
282 ctx->page.etag = NULL; 284 ctx->page.etag = NULL;
283 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 285 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
284 if (ctx->env.script_name) 286 if (ctx->env.script_name)
285 ctx->cfg.script_name = ctx->env.script_name; 287 ctx->cfg.script_name = ctx->env.script_name;
286 if (ctx->env.query_string) 288 if (ctx->env.query_string)
287 ctx->qry.raw = ctx->env.query_string; 289 ctx->qry.raw = ctx->env.query_string;
288 if (!ctx->env.cgit_config) 290 if (!ctx->env.cgit_config)
289 ctx->env.cgit_config = CGIT_CONFIG; 291 ctx->env.cgit_config = CGIT_CONFIG;
290} 292}
291 293
292struct refmatch { 294struct refmatch {
293 char *req_ref; 295 char *req_ref;
294 char *first_ref; 296 char *first_ref;
295 int match; 297 int match;
296}; 298};
297 299
298int find_current_ref(const char *refname, const unsigned char *sha1, 300int find_current_ref(const char *refname, const unsigned char *sha1,
299 int flags, void *cb_data) 301 int flags, void *cb_data)
300{ 302{
301 struct refmatch *info; 303 struct refmatch *info;
302 304
303 info = (struct refmatch *)cb_data; 305 info = (struct refmatch *)cb_data;
304 if (!strcmp(refname, info->req_ref)) 306 if (!strcmp(refname, info->req_ref))
305 info->match = 1; 307 info->match = 1;
306 if (!info->first_ref) 308 if (!info->first_ref)
307 info->first_ref = xstrdup(refname); 309 info->first_ref = xstrdup(refname);
308 return info->match; 310 return info->match;
309} 311}
310 312
311char *find_default_branch(struct cgit_repo *repo) 313char *find_default_branch(struct cgit_repo *repo)
312{ 314{
313 struct refmatch info; 315 struct refmatch info;
314 char *ref; 316 char *ref;
315 317
316 info.req_ref = repo->defbranch; 318 info.req_ref = repo->defbranch;
317 info.first_ref = NULL; 319 info.first_ref = NULL;
318 info.match = 0; 320 info.match = 0;
319 for_each_branch_ref(find_current_ref, &info); 321 for_each_branch_ref(find_current_ref, &info);
320 if (info.match) 322 if (info.match)
321 ref = info.req_ref; 323 ref = info.req_ref;
322 else 324 else
323 ref = info.first_ref; 325 ref = info.first_ref;
324 if (ref) 326 if (ref)
325 ref = xstrdup(ref); 327 ref = xstrdup(ref);
326 return ref; 328 return ref;
327} 329}
328 330
329static int prepare_repo_cmd(struct cgit_context *ctx) 331static int prepare_repo_cmd(struct cgit_context *ctx)
330{ 332{
331 char *tmp; 333 char *tmp;
332 unsigned char sha1[20]; 334 unsigned char sha1[20];
333 int nongit = 0; 335 int nongit = 0;
334 336
335 setenv("GIT_DIR", ctx->repo->path, 1); 337 setenv("GIT_DIR", ctx->repo->path, 1);
336 setup_git_directory_gently(&nongit); 338 setup_git_directory_gently(&nongit);
337 if (nongit) { 339 if (nongit) {
338 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 340 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
339 "config error"); 341 "config error");
340 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 342 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
341 ctx->repo = NULL; 343 ctx->repo = NULL;
342 cgit_print_http_headers(ctx); 344 cgit_print_http_headers(ctx);
343 cgit_print_docstart(ctx); 345 cgit_print_docstart(ctx);
344 cgit_print_pageheader(ctx); 346 cgit_print_pageheader(ctx);
345 cgit_print_error(tmp); 347 cgit_print_error(tmp);
346 cgit_print_docend(); 348 cgit_print_docend();
347 return 1; 349 return 1;
348 } 350 }
349 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 351 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
350 352
351 if (!ctx->qry.head) { 353 if (!ctx->qry.head) {
352 ctx->qry.nohead = 1; 354 ctx->qry.nohead = 1;
353 ctx->qry.head = find_default_branch(ctx->repo); 355 ctx->qry.head = find_default_branch(ctx->repo);
354 ctx->repo->defbranch = ctx->qry.head; 356 ctx->repo->defbranch = ctx->qry.head;
355 } 357 }
356 358
357 if (!ctx->qry.head) { 359 if (!ctx->qry.head) {
358 cgit_print_http_headers(ctx); 360 cgit_print_http_headers(ctx);
359 cgit_print_docstart(ctx); 361 cgit_print_docstart(ctx);
360 cgit_print_pageheader(ctx); 362 cgit_print_pageheader(ctx);
361 cgit_print_error("Repository seems to be empty"); 363 cgit_print_error("Repository seems to be empty");
362 cgit_print_docend(); 364 cgit_print_docend();
363 return 1; 365 return 1;
364 } 366 }
365 367
366 if (get_sha1(ctx->qry.head, sha1)) { 368 if (get_sha1(ctx->qry.head, sha1)) {
367 tmp = xstrdup(ctx->qry.head); 369 tmp = xstrdup(ctx->qry.head);
368 ctx->qry.head = ctx->repo->defbranch; 370 ctx->qry.head = ctx->repo->defbranch;
369 ctx->page.status = 404; 371 ctx->page.status = 404;
370 ctx->page.statusmsg = "not found"; 372 ctx->page.statusmsg = "not found";
371 cgit_print_http_headers(ctx); 373 cgit_print_http_headers(ctx);
372 cgit_print_docstart(ctx); 374 cgit_print_docstart(ctx);
373 cgit_print_pageheader(ctx); 375 cgit_print_pageheader(ctx);
374 cgit_print_error(fmt("Invalid branch: %s", tmp)); 376 cgit_print_error(fmt("Invalid branch: %s", tmp));
375 cgit_print_docend(); 377 cgit_print_docend();
376 return 1; 378 return 1;
377 } 379 }
378 return 0; 380 return 0;
379} 381}
380 382
381static void process_request(void *cbdata) 383static void process_request(void *cbdata)
382{ 384{
383 struct cgit_context *ctx = cbdata; 385 struct cgit_context *ctx = cbdata;
384 struct cgit_cmd *cmd; 386 struct cgit_cmd *cmd;
385 387
386 cmd = cgit_get_cmd(ctx); 388 cmd = cgit_get_cmd(ctx);
387 if (!cmd) { 389 if (!cmd) {
388 ctx->page.title = "cgit error"; 390 ctx->page.title = "cgit error";
389 cgit_print_http_headers(ctx); 391 cgit_print_http_headers(ctx);
390 cgit_print_docstart(ctx); 392 cgit_print_docstart(ctx);
391 cgit_print_pageheader(ctx); 393 cgit_print_pageheader(ctx);
392 cgit_print_error("Invalid request"); 394 cgit_print_error("Invalid request");
393 cgit_print_docend(); 395 cgit_print_docend();
394 return; 396 return;
395 } 397 }
396 398
397 if (cmd->want_repo && !ctx->repo) { 399 if (cmd->want_repo && !ctx->repo) {
398 cgit_print_http_headers(ctx); 400 cgit_print_http_headers(ctx);
399 cgit_print_docstart(ctx); 401 cgit_print_docstart(ctx);
400 cgit_print_pageheader(ctx); 402 cgit_print_pageheader(ctx);
401 cgit_print_error(fmt("No repository selected")); 403 cgit_print_error(fmt("No repository selected"));
402 cgit_print_docend(); 404 cgit_print_docend();
403 return; 405 return;
404 } 406 }
405 407
406 if (ctx->repo && prepare_repo_cmd(ctx)) 408 if (ctx->repo && prepare_repo_cmd(ctx))
407 return; 409 return;
408 410
409 if (cmd->want_layout) { 411 if (cmd->want_layout) {
410 cgit_print_http_headers(ctx); 412 cgit_print_http_headers(ctx);
411 cgit_print_docstart(ctx); 413 cgit_print_docstart(ctx);
412 cgit_print_pageheader(ctx); 414 cgit_print_pageheader(ctx);
413 } 415 }
414 416
415 cmd->fn(ctx); 417 cmd->fn(ctx);
416 418
417 if (cmd->want_layout) 419 if (cmd->want_layout)
418 cgit_print_docend(); 420 cgit_print_docend();
419} 421}
420 422
421int cmp_repos(const void *a, const void *b) 423int cmp_repos(const void *a, const void *b)
422{ 424{
423 const struct cgit_repo *ra = a, *rb = b; 425 const struct cgit_repo *ra = a, *rb = b;
424 return strcmp(ra->url, rb->url); 426 return strcmp(ra->url, rb->url);
425} 427}
426 428
427void print_repo(FILE *f, struct cgit_repo *repo) 429void print_repo(FILE *f, struct cgit_repo *repo)
428{ 430{
429 fprintf(f, "repo.url=%s\n", repo->url); 431 fprintf(f, "repo.url=%s\n", repo->url);
430 fprintf(f, "repo.name=%s\n", repo->name); 432 fprintf(f, "repo.name=%s\n", repo->name);
431 fprintf(f, "repo.path=%s\n", repo->path); 433 fprintf(f, "repo.path=%s\n", repo->path);
432 if (repo->owner) 434 if (repo->owner)
433 fprintf(f, "repo.owner=%s\n", repo->owner); 435 fprintf(f, "repo.owner=%s\n", repo->owner);
434 if (repo->desc) 436 if (repo->desc)
435 fprintf(f, "repo.desc=%s\n", repo->desc); 437 fprintf(f, "repo.desc=%s\n", repo->desc);
436 if (repo->readme) 438 if (repo->readme)
437 fprintf(f, "repo.readme=%s\n", repo->readme); 439 fprintf(f, "repo.readme=%s\n", repo->readme);
438 fprintf(f, "\n"); 440 fprintf(f, "\n");
439} 441}
440 442
441void print_repolist(FILE *f, struct cgit_repolist *list, int start) 443void print_repolist(FILE *f, struct cgit_repolist *list, int start)
442{ 444{
443 int i; 445 int i;
444 446
445 for(i = start; i < list->count; i++) 447 for(i = start; i < list->count; i++)
446 print_repo(f, &list->repos[i]); 448 print_repo(f, &list->repos[i]);
447} 449}
448 450
449/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 451/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
450 * and return 0 on success. 452 * and return 0 on success.
451 */ 453 */
452static int generate_cached_repolist(const char *path, const char *cached_rc) 454static int generate_cached_repolist(const char *path, const char *cached_rc)
453{ 455{
454 char *locked_rc; 456 char *locked_rc;
455 int idx; 457 int idx;
456 FILE *f; 458 FILE *f;
457 459
458 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 460 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
459 f = fopen(locked_rc, "wx"); 461 f = fopen(locked_rc, "wx");
460 if (!f) { 462 if (!f) {
461 /* Inform about the error unless the lockfile already existed, 463 /* Inform about the error unless the lockfile already existed,
462 * since that only means we've got concurrent requests. 464 * since that only means we've got concurrent requests.
463 */ 465 */
464 if (errno != EEXIST) 466 if (errno != EEXIST)
465 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 467 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
466 locked_rc, strerror(errno), errno); 468 locked_rc, strerror(errno), errno);
467 return errno; 469 return errno;
468 } 470 }
469 idx = cgit_repolist.count; 471 idx = cgit_repolist.count;
470 scan_tree(path); 472 scan_tree(path);
471 print_repolist(f, &cgit_repolist, idx); 473 print_repolist(f, &cgit_repolist, idx);
472 if (rename(locked_rc, cached_rc)) 474 if (rename(locked_rc, cached_rc))
473 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 475 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
474 locked_rc, cached_rc, strerror(errno), errno); 476 locked_rc, cached_rc, strerror(errno), errno);
475 fclose(f); 477 fclose(f);
476 return 0; 478 return 0;
477} 479}
478 480
479static void process_cached_repolist(const char *path) 481static void process_cached_repolist(const char *path)
480{ 482{
481 struct stat st; 483 struct stat st;
482 char *cached_rc; 484 char *cached_rc;
483 time_t age; 485 time_t age;
484 486
485 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 487 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root,
486 hash_str(path))); 488 hash_str(path)));
487 489
488 if (stat(cached_rc, &st)) { 490 if (stat(cached_rc, &st)) {
489 /* Nothing is cached, we need to scan without forking. And 491 /* Nothing is cached, we need to scan without forking. And
490 * if we fail to generate a cached repolist, we need to 492 * if we fail to generate a cached repolist, we need to
491 * invoke scan_tree manually. 493 * invoke scan_tree manually.
492 */ 494 */
493 if (generate_cached_repolist(path, cached_rc)) 495 if (generate_cached_repolist(path, cached_rc))
494 scan_tree(path); 496 scan_tree(path);
495 return; 497 return;
496 } 498 }
497 499
498 parse_configfile(cached_rc, config_cb); 500 parse_configfile(cached_rc, config_cb);
499 501
500 /* If the cached configfile hasn't expired, lets exit now */ 502 /* If the cached configfile hasn't expired, lets exit now */
501 age = time(NULL) - st.st_mtime; 503 age = time(NULL) - st.st_mtime;
502 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 504 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
503 return; 505 return;
504 506
505 /* The cached repolist has been parsed, but it was old. So lets 507 /* The cached repolist has been parsed, but it was old. So lets
506 * rescan the specified path and generate a new cached repolist 508 * rescan the specified path and generate a new cached repolist
507 * in a child-process to avoid latency for the current request. 509 * in a child-process to avoid latency for the current request.
508 */ 510 */
509 if (fork()) 511 if (fork())
510 return; 512 return;
511 513
512 exit(generate_cached_repolist(path, cached_rc)); 514 exit(generate_cached_repolist(path, cached_rc));
513} 515}
514 516
515static void cgit_parse_args(int argc, const char **argv) 517static void cgit_parse_args(int argc, const char **argv)
516{ 518{
517 int i; 519 int i;
518 int scan = 0; 520 int scan = 0;
519 521
520 for (i = 1; i < argc; i++) { 522 for (i = 1; i < argc; i++) {
521 if (!strncmp(argv[i], "--cache=", 8)) { 523 if (!strncmp(argv[i], "--cache=", 8)) {
522 ctx.cfg.cache_root = xstrdup(argv[i]+8); 524 ctx.cfg.cache_root = xstrdup(argv[i]+8);
523 } 525 }
524 if (!strcmp(argv[i], "--nocache")) { 526 if (!strcmp(argv[i], "--nocache")) {
525 ctx.cfg.nocache = 1; 527 ctx.cfg.nocache = 1;
526 } 528 }
527 if (!strcmp(argv[i], "--nohttp")) { 529 if (!strcmp(argv[i], "--nohttp")) {
528 ctx.env.no_http = "1"; 530 ctx.env.no_http = "1";
529 } 531 }
530 if (!strncmp(argv[i], "--query=", 8)) { 532 if (!strncmp(argv[i], "--query=", 8)) {
531 ctx.qry.raw = xstrdup(argv[i]+8); 533 ctx.qry.raw = xstrdup(argv[i]+8);
532 } 534 }
533 if (!strncmp(argv[i], "--repo=", 7)) { 535 if (!strncmp(argv[i], "--repo=", 7)) {
534 ctx.qry.repo = xstrdup(argv[i]+7); 536 ctx.qry.repo = xstrdup(argv[i]+7);
535 } 537 }
536 if (!strncmp(argv[i], "--page=", 7)) { 538 if (!strncmp(argv[i], "--page=", 7)) {
537 ctx.qry.page = xstrdup(argv[i]+7); 539 ctx.qry.page = xstrdup(argv[i]+7);
538 } 540 }
539 if (!strncmp(argv[i], "--head=", 7)) { 541 if (!strncmp(argv[i], "--head=", 7)) {
540 ctx.qry.head = xstrdup(argv[i]+7); 542 ctx.qry.head = xstrdup(argv[i]+7);
541 ctx.qry.has_symref = 1; 543 ctx.qry.has_symref = 1;
542 } 544 }
543 if (!strncmp(argv[i], "--sha1=", 7)) { 545 if (!strncmp(argv[i], "--sha1=", 7)) {
544 ctx.qry.sha1 = xstrdup(argv[i]+7); 546 ctx.qry.sha1 = xstrdup(argv[i]+7);
545 ctx.qry.has_sha1 = 1; 547 ctx.qry.has_sha1 = 1;
546 } 548 }
547 if (!strncmp(argv[i], "--ofs=", 6)) { 549 if (!strncmp(argv[i], "--ofs=", 6)) {
548 ctx.qry.ofs = atoi(argv[i]+6); 550 ctx.qry.ofs = atoi(argv[i]+6);
549 } 551 }
550 if (!strncmp(argv[i], "--scan-tree=", 12) || 552 if (!strncmp(argv[i], "--scan-tree=", 12) ||
551 !strncmp(argv[i], "--scan-path=", 12)) { 553 !strncmp(argv[i], "--scan-path=", 12)) {
552 scan++; 554 scan++;
553 scan_tree(argv[i] + 12); 555 scan_tree(argv[i] + 12);
554 } 556 }
555 } 557 }
556 if (scan) { 558 if (scan) {
557 qsort(cgit_repolist.repos, cgit_repolist.count, 559 qsort(cgit_repolist.repos, cgit_repolist.count,
558 sizeof(struct cgit_repo), cmp_repos); 560 sizeof(struct cgit_repo), cmp_repos);
559 print_repolist(stdout, &cgit_repolist, 0); 561 print_repolist(stdout, &cgit_repolist, 0);
560 exit(0); 562 exit(0);
561 } 563 }
562} 564}
563 565
564static int calc_ttl() 566static int calc_ttl()
565{ 567{
566 if (!ctx.repo) 568 if (!ctx.repo)
567 return ctx.cfg.cache_root_ttl; 569 return ctx.cfg.cache_root_ttl;
568 570
569 if (!ctx.qry.page) 571 if (!ctx.qry.page)
570 return ctx.cfg.cache_repo_ttl; 572 return ctx.cfg.cache_repo_ttl;
571 573
572 if (ctx.qry.has_symref) 574 if (ctx.qry.has_symref)
573 return ctx.cfg.cache_dynamic_ttl; 575 return ctx.cfg.cache_dynamic_ttl;
574 576
575 if (ctx.qry.has_sha1) 577 if (ctx.qry.has_sha1)
576 return ctx.cfg.cache_static_ttl; 578 return ctx.cfg.cache_static_ttl;
577 579
578 return ctx.cfg.cache_repo_ttl; 580 return ctx.cfg.cache_repo_ttl;
579} 581}
580 582
581int main(int argc, const char **argv) 583int main(int argc, const char **argv)
582{ 584{
583 const char *path; 585 const char *path;
584 char *qry; 586 char *qry;
585 int err, ttl; 587 int err, ttl;
586 588
587 prepare_context(&ctx); 589 prepare_context(&ctx);
588 cgit_repolist.length = 0; 590 cgit_repolist.length = 0;
589 cgit_repolist.count = 0; 591 cgit_repolist.count = 0;
590 cgit_repolist.repos = NULL; 592 cgit_repolist.repos = NULL;
591 593
592 cgit_parse_args(argc, argv); 594 cgit_parse_args(argc, argv);
593 parse_configfile(ctx.env.cgit_config, config_cb); 595 parse_configfile(ctx.env.cgit_config, config_cb);
594 ctx.repo = NULL; 596 ctx.repo = NULL;
595 http_parse_querystring(ctx.qry.raw, querystring_cb); 597 http_parse_querystring(ctx.qry.raw, querystring_cb);
596 598
597 /* If virtual-root isn't specified in cgitrc, lets pretend 599 /* If virtual-root isn't specified in cgitrc, lets pretend
598 * that virtual-root equals SCRIPT_NAME. 600 * that virtual-root equals SCRIPT_NAME.
599 */ 601 */
600 if (!ctx.cfg.virtual_root) 602 if (!ctx.cfg.virtual_root)
601 ctx.cfg.virtual_root = ctx.cfg.script_name; 603 ctx.cfg.virtual_root = ctx.cfg.script_name;
602 604
603 /* If no url parameter is specified on the querystring, lets 605 /* If no url parameter is specified on the querystring, lets
604 * use PATH_INFO as url. This allows cgit to work with virtual 606 * use PATH_INFO as url. This allows cgit to work with virtual
605 * urls without the need for rewriterules in the webserver (as 607 * urls without the need for rewriterules in the webserver (as
606 * long as PATH_INFO is included in the cache lookup key). 608 * long as PATH_INFO is included in the cache lookup key).
607 */ 609 */
608 path = ctx.env.path_info; 610 path = ctx.env.path_info;
609 if (!ctx.qry.url && path) { 611 if (!ctx.qry.url && path) {
610 if (path[0] == '/') 612 if (path[0] == '/')
611 path++; 613 path++;
612 ctx.qry.url = xstrdup(path); 614 ctx.qry.url = xstrdup(path);
613 if (ctx.qry.raw) { 615 if (ctx.qry.raw) {
614 qry = ctx.qry.raw; 616 qry = ctx.qry.raw;
615 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 617 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
616 free(qry); 618 free(qry);
617 } else 619 } else
618 ctx.qry.raw = xstrdup(ctx.qry.url); 620 ctx.qry.raw = xstrdup(ctx.qry.url);
619 cgit_parse_url(ctx.qry.url); 621 cgit_parse_url(ctx.qry.url);
620 } 622 }
621 623
622 ttl = calc_ttl(); 624 ttl = calc_ttl();
623 ctx.page.expires += ttl*60; 625 ctx.page.expires += ttl*60;
624 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")) 626 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
625 ctx.cfg.nocache = 1; 627 ctx.cfg.nocache = 1;
626 if (ctx.cfg.nocache) 628 if (ctx.cfg.nocache)
627 ctx.cfg.cache_size = 0; 629 ctx.cfg.cache_size = 0;
628 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 630 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
629 ctx.qry.raw, ttl, process_request, &ctx); 631 ctx.qry.raw, ttl, process_request, &ctx);
630 if (err) 632 if (err)
631 cgit_print_error(fmt("Error processing page: %s (%d)", 633 cgit_print_error(fmt("Error processing page: %s (%d)",
632 strerror(err), err)); 634 strerror(err), err));
633 return err; 635 return err;
634} 636}
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 4d009f9..e99c9f7 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,470 +1,473 @@
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
17LOCATION 17LOCATION
18-------- 18--------
19The default location of cgitrc, defined at compile time, is /etc/cgitrc. At 19The default location of cgitrc, defined at compile time, is /etc/cgitrc. At
20runtime, cgit will consult the environment variable CGIT_CONFIG and, if 20runtime, cgit will consult the environment variable CGIT_CONFIG and, if
21defined, use its value instead. 21defined, use its value instead.
22 22
23 23
24GLOBAL SETTINGS 24GLOBAL SETTINGS
25--------------- 25---------------
26about-filter:: 26about-filter::
27 Specifies a command which will be invoked to format the content of 27 Specifies a command which will be invoked to format the content of
28 about pages (both top-level and for each repository). The command will 28 about pages (both top-level and for each repository). The command will
29 get the content of the about-file on its STDIN, and the STDOUT from the 29 get the content of the about-file on its STDIN, and the STDOUT from the
30 command will be included verbatim on the about page. Default value: 30 command will be included verbatim on the about page. Default value:
31 none. 31 none.
32 32
33agefile:: 33agefile::
34 Specifies a path, relative to each repository path, which can be used 34 Specifies a path, relative to each repository path, which can be used
35 to specify the date and time of the youngest commit in the repository. 35 to specify the date and time of the youngest commit in the repository.
36 The first line in the file is used as input to the "parse_date" 36 The first line in the file is used as input to the "parse_date"
37 function in libgit. Recommended timestamp-format is "yyyy-mm-dd 37 function in libgit. Recommended timestamp-format is "yyyy-mm-dd
38 hh:mm:ss". Default value: "info/web/last-modified". 38 hh:mm:ss". Default value: "info/web/last-modified".
39 39
40cache-root:: 40cache-root::
41 Path used to store the cgit cache entries. Default value: 41 Path used to store the cgit cache entries. Default value:
42 "/var/cache/cgit". 42 "/var/cache/cgit".
43 43
44cache-dynamic-ttl:: 44cache-dynamic-ttl::
45 Number which specifies the time-to-live, in minutes, for the cached 45 Number which specifies the time-to-live, in minutes, for the cached
46 version of repository pages accessed without a fixed SHA1. Default 46 version of repository pages accessed without a fixed SHA1. Default
47 value: "5". 47 value: "5".
48 48
49cache-repo-ttl:: 49cache-repo-ttl::
50 Number which specifies the time-to-live, in minutes, for the cached 50 Number which specifies the time-to-live, in minutes, for the cached
51 version of the repository summary page. Default value: "5". 51 version of the repository summary page. Default value: "5".
52 52
53cache-root-ttl:: 53cache-root-ttl::
54 Number which specifies the time-to-live, in minutes, for the cached 54 Number which specifies the time-to-live, in minutes, for the cached
55 version of the repository index page. Default value: "5". 55 version of the repository index page. Default value: "5".
56 56
57cache-scanrc-ttl:: 57cache-scanrc-ttl::
58 Number which specifies the time-to-live, in minutes, for the result 58 Number which specifies the time-to-live, in minutes, for the result
59 of scanning a path for git repositories. Default value: "15". 59 of scanning a path for git repositories. Default value: "15".
60 60
61cache-size:: 61cache-size::
62 The maximum number of entries in the cgit cache. Default value: "0" 62 The maximum number of entries in the cgit cache. Default value: "0"
63 (i.e. caching is disabled). 63 (i.e. caching is disabled).
64 64
65cache-static-ttl:: 65cache-static-ttl::
66 Number which specifies the time-to-live, in minutes, for the cached 66 Number which specifies the time-to-live, in minutes, for the cached
67 version of repository pages accessed with a fixed SHA1. Default value: 67 version of repository pages accessed with a fixed SHA1. Default value:
68 "5". 68 "5".
69 69
70clone-prefix:: 70clone-prefix::
71 Space-separated list of common prefixes which, when combined with a 71 Space-separated list of common prefixes which, when combined with a
72 repository url, generates valid clone urls for the repository. This 72 repository url, generates valid clone urls for the repository. This
73 setting is only used if `repo.clone-url` is unspecified. Default value: 73 setting is only used if `repo.clone-url` is unspecified. Default value:
74 none. 74 none.
75 75
76commit-filter:: 76commit-filter::
77 Specifies a command which will be invoked to format commit messages. 77 Specifies a command which will be invoked to format commit messages.
78 The command will get the message on its STDIN, and the STDOUT from the 78 The command will get the message on its STDIN, and the STDOUT from the
79 command will be included verbatim as the commit message, i.e. this can 79 command will be included verbatim as the commit message, i.e. this can
80 be used to implement bugtracker integration. Default value: none. 80 be used to implement bugtracker integration. Default value: none.
81 81
82css:: 82css::
83 Url which specifies the css document to include in all cgit pages. 83 Url which specifies the css document to include in all cgit pages.
84 Default value: "/cgit.css". 84 Default value: "/cgit.css".
85 85
86embedded:: 86embedded::
87 Flag which, when set to "1", will make cgit generate a html fragment 87 Flag which, when set to "1", will make cgit generate a html fragment
88 suitable for embedding in other html pages. Default value: none. See 88 suitable for embedding in other html pages. Default value: none. See
89 also: "noheader". 89 also: "noheader".
90 90
91enable-index-links:: 91enable-index-links::
92 Flag which, when set to "1", will make cgit generate extra links for 92 Flag which, when set to "1", will make cgit generate extra links for
93 each repo in the repository index (specifically, to the "summary", 93 each repo in the repository index (specifically, to the "summary",
94 "commit" and "tree" pages). Default value: "0". 94 "commit" and "tree" pages). Default value: "0".
95 95
96enable-log-filecount:: 96enable-log-filecount::
97 Flag which, when set to "1", will make cgit print the number of 97 Flag which, when set to "1", will make cgit print the number of
98 modified files for each commit on the repository log page. Default 98 modified files for each commit on the repository log page. Default
99 value: "0". 99 value: "0".
100 100
101enable-log-linecount:: 101enable-log-linecount::
102 Flag which, when set to "1", will make cgit print the number of added 102 Flag which, when set to "1", will make cgit print the number of added
103 and removed lines for each commit on the repository log page. Default 103 and removed lines for each commit on the repository log page. Default
104 value: "0". 104 value: "0".
105 105
106favicon:: 106favicon::
107 Url used as link to a shortcut icon for cgit. If specified, it is 107 Url used as link to a shortcut icon for cgit. If specified, it is
108 suggested to use the value "/favicon.ico" since certain browsers will 108 suggested to use the value "/favicon.ico" since certain browsers will
109 ignore other values. Default value: none. 109 ignore other values. Default value: none.
110 110
111footer:: 111footer::
112 The content of the file specified with this option will be included 112 The content of the file specified with this option will be included
113 verbatim at the bottom of all pages (i.e. it replaces the standard 113 verbatim at the bottom of all pages (i.e. it replaces the standard
114 "generated by..." message. Default value: none. 114 "generated by..." message. Default value: none.
115 115
116head-include:: 116head-include::
117 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
118 verbatim in the html HEAD section on all pages. Default value: none. 118 verbatim in the html HEAD section on all pages. Default value: none.
119 119
120header:: 120header::
121 The content of the file specified with this option will be included 121 The content of the file specified with this option will be included
122 verbatim at the top of all pages. Default value: none. 122 verbatim at the top of all pages. Default value: none.
123 123
124include:: 124include::
125 Name of a configfile to include before the rest of the current config- 125 Name of a configfile to include before the rest of the current config-
126 file is parsed. Default value: none. 126 file is parsed. Default value: none.
127 127
128index-header:: 128index-header::
129 The content of the file specified with this option will be included 129 The content of the file specified with this option will be included
130 verbatim above the repository index. This setting is deprecated, and 130 verbatim above the repository index. This setting is deprecated, and
131 will not be supported by cgit-1.0 (use root-readme instead). Default 131 will not be supported by cgit-1.0 (use root-readme instead). Default
132 value: none. 132 value: none.
133 133
134index-info:: 134index-info::
135 The content of the file specified with this option will be included 135 The content of the file specified with this option will be included
136 verbatim below the heading on the repository index page. This setting 136 verbatim below the heading on the repository index page. This setting
137 is deprecated, and will not be supported by cgit-1.0 (use root-desc 137 is deprecated, and will not be supported by cgit-1.0 (use root-desc
138 instead). Default value: none. 138 instead). Default value: none.
139 139
140local-time:: 140local-time::
141 Flag which, if set to "1", makes cgit print commit and tag times in the 141 Flag which, if set to "1", makes cgit print commit and tag times in the
142 servers timezone. Default value: "0". 142 servers timezone. Default value: "0".
143 143
144logo:: 144logo::
145 Url which specifies the source of an image which will be used as a logo 145 Url which specifies the source of an image which will be used as a logo
146 on all cgit pages. Default value: "/cgit.png". 146 on all cgit pages. Default value: "/cgit.png".
147 147
148logo-link:: 148logo-link::
149 Url loaded when clicking on the cgit logo image. If unspecified the 149 Url loaded when clicking on the cgit logo image. If unspecified the
150 calculated url of the repository index page will be used. Default 150 calculated url of the repository index page will be used. Default
151 value: none. 151 value: none.
152 152
153max-commit-count:: 153max-commit-count::
154 Specifies the number of entries to list per page in "log" view. Default 154 Specifies the number of entries to list per page in "log" view. Default
155 value: "50". 155 value: "50".
156 156
157max-message-length:: 157max-message-length::
158 Specifies the maximum number of commit message characters to display in 158 Specifies the maximum number of commit message characters to display in
159 "log" view. Default value: "80". 159 "log" view. Default value: "80".
160 160
161max-repo-count:: 161max-repo-count::
162 Specifies the number of entries to list per page on therepository 162 Specifies the number of entries to list per page on therepository
163 index page. Default value: "50". 163 index page. Default value: "50".
164 164
165max-repodesc-length:: 165max-repodesc-length::
166 Specifies the maximum number of repo description characters to display 166 Specifies the maximum number of repo description characters to display
167 on the repository index page. Default value: "80". 167 on the repository index page. Default value: "80".
168 168
169max-stats:: 169max-stats::
170 Set the default maximum statistics period. Valid values are "week", 170 Set the default maximum statistics period. Valid values are "week",
171 "month", "quarter" and "year". If unspecified, statistics are 171 "month", "quarter" and "year". If unspecified, statistics are
172 disabled. Default value: none. See also: "repo.max-stats". 172 disabled. Default value: none. See also: "repo.max-stats".
173 173
174mimetype.<ext>:: 174mimetype.<ext>::
175 Set the mimetype for the specified filename extension. This is used 175 Set the mimetype for the specified filename extension. This is used
176 by the `plain` command when returning blob content. 176 by the `plain` command when returning blob content.
177 177
178module-link:: 178module-link::
179 Text which will be used as the formatstring for a hyperlink when a 179 Text which will be used as the formatstring for a hyperlink when a
180 submodule is printed in a directory listing. The arguments for the 180 submodule is printed in a directory listing. The arguments for the
181 formatstring are the path and SHA1 of the submodule commit. Default 181 formatstring are the path and SHA1 of the submodule commit. Default
182 value: "./?repo=%s&page=commit&id=%s" 182 value: "./?repo=%s&page=commit&id=%s"
183 183
184nocache:: 184nocache::
185 If set to the value "1" caching will be disabled. This settings is 185 If set to the value "1" caching will be disabled. This settings is
186 deprecated, and will not be honored starting with cgit-1.0. Default 186 deprecated, and will not be honored starting with cgit-1.0. Default
187 value: "0". 187 value: "0".
188 188
189noplainemail:: 189noplainemail::
190 If set to "1" showing full author email adresses will be disabled. 190 If set to "1" showing full author email adresses will be disabled.
191 Default value: "0". 191 Default value: "0".
192 192
193noheader:: 193noheader::
194 Flag which, when set to "1", will make cgit omit the standard header 194 Flag which, when set to "1", will make cgit omit the standard header
195 on all pages. Default value: none. See also: "embedded". 195 on all pages. Default value: none. See also: "embedded".
196 196
197renamelimit:: 197renamelimit::
198 Maximum number of files to consider when detecting renames. The value 198 Maximum number of files to consider when detecting renames. The value
199 "-1" uses the compiletime value in git (for further info, look at 199 "-1" uses the compiletime value in git (for further info, look at
200 `man git-diff`). Default value: "-1". 200 `man git-diff`). Default value: "-1".
201 201
202repo.group:: 202repo.group::
203 Legacy alias for 'section' which will be deprecated starting with 203 Legacy alias for 'section' which will be deprecated starting with
204 cgit-1.0. 204 cgit-1.0.
205 205
206robots:: 206robots::
207 Text used as content for the "robots" meta-tag. Default value: 207 Text used as content for the "robots" meta-tag. Default value:
208 "index, nofollow". 208 "index, nofollow".
209 209
210root-desc:: 210root-desc::
211 Text printed below the heading on the repository index page. Default 211 Text printed below the heading on the repository index page. Default
212 value: "a fast webinterface for the git dscm". 212 value: "a fast webinterface for the git dscm".
213 213
214root-readme:: 214root-readme::
215 The content of the file specified with this option will be included 215 The content of the file specified with this option will be included
216 verbatim below the "about" link on the repository index page. Default 216 verbatim below the "about" link on the repository index page. Default
217 value: none. 217 value: none.
218 218
219root-title:: 219root-title::
220 Text printed as heading on the repository index page. Default value: 220 Text printed as heading on the repository index page. Default value:
221 "Git Repository Browser". 221 "Git Repository Browser".
222 222
223scan-path:: 223scan-path::
224 A path which will be scanned for repositories. If caching is enabled, 224 A path which will be scanned for repositories. If caching is enabled,
225 the result will be cached as a cgitrc include-file in the cache 225 the result will be cached as a cgitrc include-file in the cache
226 directory. Default value: none. See also: cache-scanrc-ttl. 226 directory. Default value: none. See also: cache-scanrc-ttl.
227 227
228section: 228section:
229 The name of the current repository section - all repositories defined 229 The name of the current repository section - all repositories defined
230 after this option will inherit the current section name. Default value: 230 after this option will inherit the current section name. Default value:
231 none. 231 none.
232 232
233snapshots:: 233snapshots::
234 Text which specifies the default set of snapshot formats generated by 234 Text which specifies the default set of snapshot formats generated by
235 cgit. The value is a space-separated list of zero or more of the 235 cgit. The value is a space-separated list of zero or more of the
236 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 236 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
237 237
238source-filter:: 238source-filter::
239 Specifies a command which will be invoked to format plaintext blobs 239 Specifies a command which will be invoked to format plaintext blobs
240 in the tree view. The command will get the blob content on its STDIN 240 in the tree view. The command will get the blob content on its STDIN
241 and the name of the blob as its only command line argument. The STDOUT 241 and the name of the blob as its only command line argument. The STDOUT
242 from the command will be included verbatim as the blob contents, i.e. 242 from the command will be included verbatim as the blob contents, i.e.
243 this can be used to implement e.g. syntax highlighting. Default value: 243 this can be used to implement e.g. syntax highlighting. Default value:
244 none. 244 none.
245 245
246summary-branches:: 246summary-branches::
247 Specifies the number of branches to display in the repository "summary" 247 Specifies the number of branches to display in the repository "summary"
248 view. Default value: "10". 248 view. Default value: "10".
249 249
250summary-log:: 250summary-log::
251 Specifies the number of log entries to display in the repository 251 Specifies the number of log entries to display in the repository
252 "summary" view. Default value: "10". 252 "summary" view. Default value: "10".
253 253
254summary-tags:: 254summary-tags::
255 Specifies the number of tags to display in the repository "summary" 255 Specifies the number of tags to display in the repository "summary"
256 view. Default value: "10". 256 view. Default value: "10".
257 257
258virtual-root:: 258virtual-root::
259 Url which, if specified, will be used as root for all cgit links. It 259 Url which, if specified, will be used as root for all cgit links. It
260 will also cause cgit to generate 'virtual urls', i.e. urls like 260 will also cause cgit to generate 'virtual urls', i.e. urls like
261 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 261 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
262 value: none. 262 value: none.
263 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 263 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
264 same kind of virtual urls, so this option will probably be deprecated. 264 same kind of virtual urls, so this option will probably be deprecated.
265 265
266REPOSITORY SETTINGS 266REPOSITORY SETTINGS
267------------------- 267-------------------
268repo.about-filter:: 268repo.about-filter::
269 Override the default about-filter. Default value: <about-filter>. 269 Override the default about-filter. Default value: <about-filter>.
270 270
271repo.clone-url:: 271repo.clone-url::
272 A list of space-separated urls which can be used to clone this repo. 272 A list of space-separated urls which can be used to clone this repo.
273 Default value: none. 273 Default value: none.
274 274
275repo.commit-filter:: 275repo.commit-filter::
276 Override the default commit-filter. Default value: <commit-filter>. 276 Override the default commit-filter. Default value: <commit-filter>.
277 277
278repo.defbranch:: 278repo.defbranch::
279 The name of the default branch for this repository. If no such branch 279 The name of the default branch for this repository. If no such branch
280 exists in the repository, the first branch name (when sorted) is used 280 exists in the repository, the first branch name (when sorted) is used
281 as default instead. Default value: "master". 281 as default instead. Default value: "master".
282 282
283repo.desc:: 283repo.desc::
284 The value to show as repository description. Default value: none. 284 The value to show as repository description. Default value: none.
285 285
286repo.enable-log-filecount:: 286repo.enable-log-filecount::
287 A flag which can be used to disable the global setting 287 A flag which can be used to disable the global setting
288 `enable-log-filecount'. Default value: none. 288 `enable-log-filecount'. Default value: none.
289 289
290repo.enable-log-linecount:: 290repo.enable-log-linecount::
291 A flag which can be used to disable the global setting 291 A flag which can be used to disable the global setting
292 `enable-log-linecount'. Default value: none. 292 `enable-log-linecount'. Default value: none.
293 293
294repo.max-stats:: 294repo.max-stats::
295 Override the default maximum statistics period. Valid values are equal 295 Override the default maximum statistics period. Valid values are equal
296 to the values specified for the global "max-stats" setting. Default 296 to the values specified for the global "max-stats" setting. Default
297 value: none. 297 value: none.
298 298
299repo.name:: 299repo.name::
300 The value to show as repository name. Default value: <repo.url>. 300 The value to show as repository name. Default value: <repo.url>.
301 301
302repo.owner:: 302repo.owner::
303 A value used to identify the owner of the repository. Default value: 303 A value used to identify the owner of the repository. Default value:
304 none. 304 none.
305 305
306repo.path:: 306repo.path::
307 An absolute path to the repository directory. For non-bare repositories 307 An absolute path to the repository directory. For non-bare repositories
308 this is the .git-directory. Default value: none. 308 this is the .git-directory. Default value: none.
309 309
310repo.readme:: 310repo.readme::
311 A path (relative to <repo.path>) which specifies a file to include 311 A path (relative to <repo.path>) which specifies a file to include
312 verbatim as the "About" page for this repo. Default value: none. 312 verbatim as the "About" page for this repo. Default value: none.
313 313
314repo.snapshots:: 314repo.snapshots::
315 A mask of allowed snapshot-formats for this repo, restricted by the 315 A mask of allowed snapshot-formats for this repo, restricted by the
316 "snapshots" global setting. Default value: <snapshots>. 316 "snapshots" global setting. Default value: <snapshots>.
317 317
318repo.section::
319 Override the current section for this repository. Default value: none.
320
318repo.source-filter:: 321repo.source-filter::
319 Override the default source-filter. Default value: <source-filter>. 322 Override the default source-filter. Default value: <source-filter>.
320 323
321repo.url:: 324repo.url::
322 The relative url used to access the repository. This must be the first 325 The relative url used to access the repository. This must be the first
323 setting specified for each repo. Default value: none. 326 setting specified for each repo. Default value: none.
324 327
325 328
326EXAMPLE CGITRC FILE 329EXAMPLE CGITRC FILE
327------------------- 330-------------------
328 331
329.... 332....
330# Enable caching of up to 1000 output entriess 333# Enable caching of up to 1000 output entriess
331cache-size=1000 334cache-size=1000
332 335
333 336
334# Specify some default clone prefixes 337# Specify some default clone prefixes
335clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git 338clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
336 339
337# Specify the css url 340# Specify the css url
338css=/css/cgit.css 341css=/css/cgit.css
339 342
340 343
341# Show extra links for each repository on the index page 344# Show extra links for each repository on the index page
342enable-index-links=1 345enable-index-links=1
343 346
344 347
345# Show number of affected files per commit on the log pages 348# Show number of affected files per commit on the log pages
346enable-log-filecount=1 349enable-log-filecount=1
347 350
348 351
349# Show number of added/removed lines per commit on the log pages 352# Show number of added/removed lines per commit on the log pages
350enable-log-linecount=1 353enable-log-linecount=1
351 354
352 355
353# Add a cgit favicon 356# Add a cgit favicon
354favicon=/favicon.ico 357favicon=/favicon.ico
355 358
356 359
357# Use a custom logo 360# Use a custom logo
358logo=/img/mylogo.png 361logo=/img/mylogo.png
359 362
360 363
361# Enable statistics per week, month and quarter 364# Enable statistics per week, month and quarter
362max-stats=quarter 365max-stats=quarter
363 366
364 367
365# Set the title and heading of the repository index page 368# Set the title and heading of the repository index page
366root-title=foobar.com git repositories 369root-title=foobar.com git repositories
367 370
368 371
369# Set a subheading for the repository index page 372# Set a subheading for the repository index page
370root-desc=tracking the foobar development 373root-desc=tracking the foobar development
371 374
372 375
373# Include some more info about foobar.com on the index page 376# Include some more info about foobar.com on the index page
374root-readme=/var/www/htdocs/about.html 377root-readme=/var/www/htdocs/about.html
375 378
376 379
377# Allow download of tar.gz, tar.bz2 and zip-files 380# Allow download of tar.gz, tar.bz2 and zip-files
378snapshots=tar.gz tar.bz2 zip 381snapshots=tar.gz tar.bz2 zip
379 382
380 383
381## 384##
382## List of common mimetypes 385## List of common mimetypes
383## 386##
384 387
385mimetype.git=image/git 388mimetype.git=image/git
386mimetype.html=text/html 389mimetype.html=text/html
387mimetype.jpg=image/jpeg 390mimetype.jpg=image/jpeg
388mimetype.jpeg=image/jpeg 391mimetype.jpeg=image/jpeg
389mimetype.pdf=application/pdf 392mimetype.pdf=application/pdf
390mimetype.png=image/png 393mimetype.png=image/png
391mimetype.svg=image/svg+xml 394mimetype.svg=image/svg+xml
392 395
393 396
394## 397##
395## List of repositories. 398## List of repositories.
396## PS: Any repositories listed when repo.group is unset will not be 399## PS: Any repositories listed when repo.group is unset will not be
397## displayed under a group heading 400## displayed under a group heading
398## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') 401## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
399## and included like this: 402## and included like this:
400## include=/etc/cgitrepos 403## include=/etc/cgitrepos
401## 404##
402 405
403 406
404repo.url=foo 407repo.url=foo
405repo.path=/pub/git/foo.git 408repo.path=/pub/git/foo.git
406repo.desc=the master foo repository 409repo.desc=the master foo repository
407repo.owner=fooman@foobar.com 410repo.owner=fooman@foobar.com
408repo.readme=info/web/about.html 411repo.readme=info/web/about.html
409 412
410 413
411repo.url=bar 414repo.url=bar
412repo.path=/pub/git/bar.git 415repo.path=/pub/git/bar.git
413repo.desc=the bars for your foo 416repo.desc=the bars for your foo
414repo.owner=barman@foobar.com 417repo.owner=barman@foobar.com
415repo.readme=info/web/about.html 418repo.readme=info/web/about.html
416 419
417 420
418# The next repositories will be displayed under the 'extras' heading 421# The next repositories will be displayed under the 'extras' heading
419repo.group=extras 422repo.group=extras
420 423
421 424
422repo.url=baz 425repo.url=baz
423repo.path=/pub/git/baz.git 426repo.path=/pub/git/baz.git
424repo.desc=a set of extensions for bar users 427repo.desc=a set of extensions for bar users
425 428
426repo.url=wiz 429repo.url=wiz
427repo.path=/pub/git/wiz.git 430repo.path=/pub/git/wiz.git
428repo.desc=the wizard of foo 431repo.desc=the wizard of foo
429 432
430 433
431# Add some mirrored repositories 434# Add some mirrored repositories
432repo.group=mirrors 435repo.group=mirrors
433 436
434 437
435repo.url=git 438repo.url=git
436repo.path=/pub/git/git.git 439repo.path=/pub/git/git.git
437repo.desc=the dscm 440repo.desc=the dscm
438 441
439 442
440repo.url=linux 443repo.url=linux
441repo.path=/pub/git/linux.git 444repo.path=/pub/git/linux.git
442repo.desc=the kernel 445repo.desc=the kernel
443 446
444# Disable adhoc downloads of this repo 447# Disable adhoc downloads of this repo
445repo.snapshots=0 448repo.snapshots=0
446 449
447# Disable line-counts for this repo 450# Disable line-counts for this repo
448repo.enable-log-linecount=0 451repo.enable-log-linecount=0
449 452
450# Restrict the max statistics period for this repo 453# Restrict the max statistics period for this repo
451repo.max-stats=month 454repo.max-stats=month
452.... 455....
453 456
454 457
455BUGS 458BUGS
456---- 459----
457Comments currently cannot appear on the same line as a setting; the comment 460Comments currently cannot appear on the same line as a setting; the comment
458will be included as part of the value. E.g. this line: 461will be included as part of the value. E.g. this line:
459 462
460 robots=index # allow indexing 463 robots=index # allow indexing
461 464
462will generate the following html element: 465will generate the following html element:
463 466
464 <meta name='robots' content='index # allow indexing'/> 467 <meta name='robots' content='index # allow indexing'/>
465 468
466 469
467 470
468AUTHOR 471AUTHOR
469------ 472------
470Lars Hjemli <hjemli@gmail.com> 473Lars Hjemli <hjemli@gmail.com>