summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c10
-rw-r--r--cgitrc.5.txt10
2 files changed, 10 insertions, 10 deletions
diff --git a/cgit.c b/cgit.c
index 594b950..6ece411 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,531 +1,531 @@
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"))
122 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
123 process_cached_repolist(value);
124 else
125 scan_tree(value);
121 else if (!strcmp(name, "source-filter")) 126 else if (!strcmp(name, "source-filter"))
122 ctx.cfg.source_filter = new_filter(value, 1); 127 ctx.cfg.source_filter = new_filter(value, 1);
123 else if (!strcmp(name, "summary-log")) 128 else if (!strcmp(name, "summary-log"))
124 ctx.cfg.summary_log = atoi(value); 129 ctx.cfg.summary_log = atoi(value);
125 else if (!strcmp(name, "summary-branches")) 130 else if (!strcmp(name, "summary-branches"))
126 ctx.cfg.summary_branches = atoi(value); 131 ctx.cfg.summary_branches = atoi(value);
127 else if (!strcmp(name, "summary-tags")) 132 else if (!strcmp(name, "summary-tags"))
128 ctx.cfg.summary_tags = atoi(value); 133 ctx.cfg.summary_tags = atoi(value);
129 else if (!strcmp(name, "agefile")) 134 else if (!strcmp(name, "agefile"))
130 ctx.cfg.agefile = xstrdup(value); 135 ctx.cfg.agefile = xstrdup(value);
131 else if (!strcmp(name, "renamelimit")) 136 else if (!strcmp(name, "renamelimit"))
132 ctx.cfg.renamelimit = atoi(value); 137 ctx.cfg.renamelimit = atoi(value);
133 else if (!strcmp(name, "robots")) 138 else if (!strcmp(name, "robots"))
134 ctx.cfg.robots = xstrdup(value); 139 ctx.cfg.robots = xstrdup(value);
135 else if (!strcmp(name, "clone-prefix")) 140 else if (!strcmp(name, "clone-prefix"))
136 ctx.cfg.clone_prefix = xstrdup(value); 141 ctx.cfg.clone_prefix = xstrdup(value);
137 else if (!strcmp(name, "local-time")) 142 else if (!strcmp(name, "local-time"))
138 ctx.cfg.local_time = atoi(value); 143 ctx.cfg.local_time = atoi(value);
139 else if (!prefixcmp(name, "mimetype.")) 144 else if (!prefixcmp(name, "mimetype."))
140 add_mimetype(name + 9, value); 145 add_mimetype(name + 9, value);
141 else if (!strcmp(name, "repo.group")) 146 else if (!strcmp(name, "repo.group"))
142 ctx.cfg.repo_group = xstrdup(value); 147 ctx.cfg.repo_group = xstrdup(value);
143 else if (!strcmp(name, "repo.scan"))
144 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
145 process_cached_repolist(value);
146 else
147 scan_tree(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.about-filter")) 172 else if (ctx.repo && !strcmp(name, "repo.about-filter"))
173 ctx.repo->about_filter = new_filter(value, 0); 173 ctx.repo->about_filter = new_filter(value, 0);
174 else if (ctx.repo && !strcmp(name, "repo.commit-filter")) 174 else if (ctx.repo && !strcmp(name, "repo.commit-filter"))
175 ctx.repo->commit_filter = new_filter(value, 0); 175 ctx.repo->commit_filter = new_filter(value, 0);
176 else if (ctx.repo && !strcmp(name, "repo.source-filter")) 176 else if (ctx.repo && !strcmp(name, "repo.source-filter"))
177 ctx.repo->source_filter = new_filter(value, 1); 177 ctx.repo->source_filter = new_filter(value, 1);
178 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 178 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
179 if (*value == '/') 179 if (*value == '/')
180 ctx.repo->readme = xstrdup(value); 180 ctx.repo->readme = xstrdup(value);
181 else 181 else
182 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 182 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
183 } else if (!strcmp(name, "include")) 183 } else if (!strcmp(name, "include"))
184 parse_configfile(value, config_cb); 184 parse_configfile(value, config_cb);
185} 185}
186 186
187static void querystring_cb(const char *name, const char *value) 187static void querystring_cb(const char *name, const char *value)
188{ 188{
189 if (!value) 189 if (!value)
190 value = ""; 190 value = "";
191 191
192 if (!strcmp(name,"r")) { 192 if (!strcmp(name,"r")) {
193 ctx.qry.repo = xstrdup(value); 193 ctx.qry.repo = xstrdup(value);
194 ctx.repo = cgit_get_repoinfo(value); 194 ctx.repo = cgit_get_repoinfo(value);
195 } else if (!strcmp(name, "p")) { 195 } else if (!strcmp(name, "p")) {
196 ctx.qry.page = xstrdup(value); 196 ctx.qry.page = xstrdup(value);
197 } else if (!strcmp(name, "url")) { 197 } else if (!strcmp(name, "url")) {
198 ctx.qry.url = xstrdup(value); 198 ctx.qry.url = xstrdup(value);
199 cgit_parse_url(value); 199 cgit_parse_url(value);
200 } else if (!strcmp(name, "qt")) { 200 } else if (!strcmp(name, "qt")) {
201 ctx.qry.grep = xstrdup(value); 201 ctx.qry.grep = xstrdup(value);
202 } else if (!strcmp(name, "q")) { 202 } else if (!strcmp(name, "q")) {
203 ctx.qry.search = xstrdup(value); 203 ctx.qry.search = xstrdup(value);
204 } else if (!strcmp(name, "h")) { 204 } else if (!strcmp(name, "h")) {
205 ctx.qry.head = xstrdup(value); 205 ctx.qry.head = xstrdup(value);
206 ctx.qry.has_symref = 1; 206 ctx.qry.has_symref = 1;
207 } else if (!strcmp(name, "id")) { 207 } else if (!strcmp(name, "id")) {
208 ctx.qry.sha1 = xstrdup(value); 208 ctx.qry.sha1 = xstrdup(value);
209 ctx.qry.has_sha1 = 1; 209 ctx.qry.has_sha1 = 1;
210 } else if (!strcmp(name, "id2")) { 210 } else if (!strcmp(name, "id2")) {
211 ctx.qry.sha2 = xstrdup(value); 211 ctx.qry.sha2 = xstrdup(value);
212 ctx.qry.has_sha1 = 1; 212 ctx.qry.has_sha1 = 1;
213 } else if (!strcmp(name, "ofs")) { 213 } else if (!strcmp(name, "ofs")) {
214 ctx.qry.ofs = atoi(value); 214 ctx.qry.ofs = atoi(value);
215 } else if (!strcmp(name, "path")) { 215 } else if (!strcmp(name, "path")) {
216 ctx.qry.path = trim_end(value, '/'); 216 ctx.qry.path = trim_end(value, '/');
217 } else if (!strcmp(name, "name")) { 217 } else if (!strcmp(name, "name")) {
218 ctx.qry.name = xstrdup(value); 218 ctx.qry.name = xstrdup(value);
219 } else if (!strcmp(name, "mimetype")) { 219 } else if (!strcmp(name, "mimetype")) {
220 ctx.qry.mimetype = xstrdup(value); 220 ctx.qry.mimetype = xstrdup(value);
221 } else if (!strcmp(name, "s")){ 221 } else if (!strcmp(name, "s")){
222 ctx.qry.sort = xstrdup(value); 222 ctx.qry.sort = xstrdup(value);
223 } else if (!strcmp(name, "showmsg")) { 223 } else if (!strcmp(name, "showmsg")) {
224 ctx.qry.showmsg = atoi(value); 224 ctx.qry.showmsg = atoi(value);
225 } else if (!strcmp(name, "period")) { 225 } else if (!strcmp(name, "period")) {
226 ctx.qry.period = xstrdup(value); 226 ctx.qry.period = xstrdup(value);
227 } 227 }
228} 228}
229 229
230char *xstrdupn(const char *str) 230char *xstrdupn(const char *str)
231{ 231{
232 return (str ? xstrdup(str) : NULL); 232 return (str ? xstrdup(str) : NULL);
233} 233}
234 234
235static void prepare_context(struct cgit_context *ctx) 235static void prepare_context(struct cgit_context *ctx)
236{ 236{
237 memset(ctx, 0, sizeof(ctx)); 237 memset(ctx, 0, sizeof(ctx));
238 ctx->cfg.agefile = "info/web/last-modified"; 238 ctx->cfg.agefile = "info/web/last-modified";
239 ctx->cfg.nocache = 0; 239 ctx->cfg.nocache = 0;
240 ctx->cfg.cache_size = 0; 240 ctx->cfg.cache_size = 0;
241 ctx->cfg.cache_dynamic_ttl = 5; 241 ctx->cfg.cache_dynamic_ttl = 5;
242 ctx->cfg.cache_max_create_time = 5; 242 ctx->cfg.cache_max_create_time = 5;
243 ctx->cfg.cache_repo_ttl = 5; 243 ctx->cfg.cache_repo_ttl = 5;
244 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 244 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
245 ctx->cfg.cache_root_ttl = 5; 245 ctx->cfg.cache_root_ttl = 5;
246 ctx->cfg.cache_scanrc_ttl = 15; 246 ctx->cfg.cache_scanrc_ttl = 15;
247 ctx->cfg.cache_static_ttl = -1; 247 ctx->cfg.cache_static_ttl = -1;
248 ctx->cfg.css = "/cgit.css"; 248 ctx->cfg.css = "/cgit.css";
249 ctx->cfg.logo = "/cgit.png"; 249 ctx->cfg.logo = "/cgit.png";
250 ctx->cfg.local_time = 0; 250 ctx->cfg.local_time = 0;
251 ctx->cfg.max_repo_count = 50; 251 ctx->cfg.max_repo_count = 50;
252 ctx->cfg.max_commit_count = 50; 252 ctx->cfg.max_commit_count = 50;
253 ctx->cfg.max_lock_attempts = 5; 253 ctx->cfg.max_lock_attempts = 5;
254 ctx->cfg.max_msg_len = 80; 254 ctx->cfg.max_msg_len = 80;
255 ctx->cfg.max_repodesc_len = 80; 255 ctx->cfg.max_repodesc_len = 80;
256 ctx->cfg.max_stats = 0; 256 ctx->cfg.max_stats = 0;
257 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 257 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
258 ctx->cfg.renamelimit = -1; 258 ctx->cfg.renamelimit = -1;
259 ctx->cfg.robots = "index, nofollow"; 259 ctx->cfg.robots = "index, nofollow";
260 ctx->cfg.root_title = "Git repository browser"; 260 ctx->cfg.root_title = "Git repository browser";
261 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 261 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
262 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 262 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
263 ctx->cfg.summary_branches = 10; 263 ctx->cfg.summary_branches = 10;
264 ctx->cfg.summary_log = 10; 264 ctx->cfg.summary_log = 10;
265 ctx->cfg.summary_tags = 10; 265 ctx->cfg.summary_tags = 10;
266 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 266 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
267 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 267 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
268 ctx->env.https = xstrdupn(getenv("HTTPS")); 268 ctx->env.https = xstrdupn(getenv("HTTPS"));
269 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 269 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
270 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 270 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
271 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 271 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
272 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 272 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
273 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 273 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
274 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 274 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
275 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 275 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
276 ctx->page.mimetype = "text/html"; 276 ctx->page.mimetype = "text/html";
277 ctx->page.charset = PAGE_ENCODING; 277 ctx->page.charset = PAGE_ENCODING;
278 ctx->page.filename = NULL; 278 ctx->page.filename = NULL;
279 ctx->page.size = 0; 279 ctx->page.size = 0;
280 ctx->page.modified = time(NULL); 280 ctx->page.modified = time(NULL);
281 ctx->page.expires = ctx->page.modified; 281 ctx->page.expires = ctx->page.modified;
282 ctx->page.etag = NULL; 282 ctx->page.etag = NULL;
283 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 283 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
284 if (ctx->env.script_name) 284 if (ctx->env.script_name)
285 ctx->cfg.script_name = ctx->env.script_name; 285 ctx->cfg.script_name = ctx->env.script_name;
286 if (ctx->env.query_string) 286 if (ctx->env.query_string)
287 ctx->qry.raw = ctx->env.query_string; 287 ctx->qry.raw = ctx->env.query_string;
288 if (!ctx->env.cgit_config) 288 if (!ctx->env.cgit_config)
289 ctx->env.cgit_config = CGIT_CONFIG; 289 ctx->env.cgit_config = CGIT_CONFIG;
290} 290}
291 291
292struct refmatch { 292struct refmatch {
293 char *req_ref; 293 char *req_ref;
294 char *first_ref; 294 char *first_ref;
295 int match; 295 int match;
296}; 296};
297 297
298int find_current_ref(const char *refname, const unsigned char *sha1, 298int find_current_ref(const char *refname, const unsigned char *sha1,
299 int flags, void *cb_data) 299 int flags, void *cb_data)
300{ 300{
301 struct refmatch *info; 301 struct refmatch *info;
302 302
303 info = (struct refmatch *)cb_data; 303 info = (struct refmatch *)cb_data;
304 if (!strcmp(refname, info->req_ref)) 304 if (!strcmp(refname, info->req_ref))
305 info->match = 1; 305 info->match = 1;
306 if (!info->first_ref) 306 if (!info->first_ref)
307 info->first_ref = xstrdup(refname); 307 info->first_ref = xstrdup(refname);
308 return info->match; 308 return info->match;
309} 309}
310 310
311char *find_default_branch(struct cgit_repo *repo) 311char *find_default_branch(struct cgit_repo *repo)
312{ 312{
313 struct refmatch info; 313 struct refmatch info;
314 char *ref; 314 char *ref;
315 315
316 info.req_ref = repo->defbranch; 316 info.req_ref = repo->defbranch;
317 info.first_ref = NULL; 317 info.first_ref = NULL;
318 info.match = 0; 318 info.match = 0;
319 for_each_branch_ref(find_current_ref, &info); 319 for_each_branch_ref(find_current_ref, &info);
320 if (info.match) 320 if (info.match)
321 ref = info.req_ref; 321 ref = info.req_ref;
322 else 322 else
323 ref = info.first_ref; 323 ref = info.first_ref;
324 if (ref) 324 if (ref)
325 ref = xstrdup(ref); 325 ref = xstrdup(ref);
326 return ref; 326 return ref;
327} 327}
328 328
329static int prepare_repo_cmd(struct cgit_context *ctx) 329static int prepare_repo_cmd(struct cgit_context *ctx)
330{ 330{
331 char *tmp; 331 char *tmp;
332 unsigned char sha1[20]; 332 unsigned char sha1[20];
333 int nongit = 0; 333 int nongit = 0;
334 334
335 setenv("GIT_DIR", ctx->repo->path, 1); 335 setenv("GIT_DIR", ctx->repo->path, 1);
336 setup_git_directory_gently(&nongit); 336 setup_git_directory_gently(&nongit);
337 if (nongit) { 337 if (nongit) {
338 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 338 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
339 "config error"); 339 "config error");
340 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 340 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
341 ctx->repo = NULL; 341 ctx->repo = NULL;
342 cgit_print_http_headers(ctx); 342 cgit_print_http_headers(ctx);
343 cgit_print_docstart(ctx); 343 cgit_print_docstart(ctx);
344 cgit_print_pageheader(ctx); 344 cgit_print_pageheader(ctx);
345 cgit_print_error(tmp); 345 cgit_print_error(tmp);
346 cgit_print_docend(); 346 cgit_print_docend();
347 return 1; 347 return 1;
348 } 348 }
349 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 349 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
350 350
351 if (!ctx->qry.head) { 351 if (!ctx->qry.head) {
352 ctx->qry.nohead = 1; 352 ctx->qry.nohead = 1;
353 ctx->qry.head = find_default_branch(ctx->repo); 353 ctx->qry.head = find_default_branch(ctx->repo);
354 ctx->repo->defbranch = ctx->qry.head; 354 ctx->repo->defbranch = ctx->qry.head;
355 } 355 }
356 356
357 if (!ctx->qry.head) { 357 if (!ctx->qry.head) {
358 cgit_print_http_headers(ctx); 358 cgit_print_http_headers(ctx);
359 cgit_print_docstart(ctx); 359 cgit_print_docstart(ctx);
360 cgit_print_pageheader(ctx); 360 cgit_print_pageheader(ctx);
361 cgit_print_error("Repository seems to be empty"); 361 cgit_print_error("Repository seems to be empty");
362 cgit_print_docend(); 362 cgit_print_docend();
363 return 1; 363 return 1;
364 } 364 }
365 365
366 if (get_sha1(ctx->qry.head, sha1)) { 366 if (get_sha1(ctx->qry.head, sha1)) {
367 tmp = xstrdup(ctx->qry.head); 367 tmp = xstrdup(ctx->qry.head);
368 ctx->qry.head = ctx->repo->defbranch; 368 ctx->qry.head = ctx->repo->defbranch;
369 ctx->page.status = 404; 369 ctx->page.status = 404;
370 ctx->page.statusmsg = "not found"; 370 ctx->page.statusmsg = "not found";
371 cgit_print_http_headers(ctx); 371 cgit_print_http_headers(ctx);
372 cgit_print_docstart(ctx); 372 cgit_print_docstart(ctx);
373 cgit_print_pageheader(ctx); 373 cgit_print_pageheader(ctx);
374 cgit_print_error(fmt("Invalid branch: %s", tmp)); 374 cgit_print_error(fmt("Invalid branch: %s", tmp));
375 cgit_print_docend(); 375 cgit_print_docend();
376 return 1; 376 return 1;
377 } 377 }
378 return 0; 378 return 0;
379} 379}
380 380
381static void process_request(void *cbdata) 381static void process_request(void *cbdata)
382{ 382{
383 struct cgit_context *ctx = cbdata; 383 struct cgit_context *ctx = cbdata;
384 struct cgit_cmd *cmd; 384 struct cgit_cmd *cmd;
385 385
386 cmd = cgit_get_cmd(ctx); 386 cmd = cgit_get_cmd(ctx);
387 if (!cmd) { 387 if (!cmd) {
388 ctx->page.title = "cgit error"; 388 ctx->page.title = "cgit error";
389 cgit_print_http_headers(ctx); 389 cgit_print_http_headers(ctx);
390 cgit_print_docstart(ctx); 390 cgit_print_docstart(ctx);
391 cgit_print_pageheader(ctx); 391 cgit_print_pageheader(ctx);
392 cgit_print_error("Invalid request"); 392 cgit_print_error("Invalid request");
393 cgit_print_docend(); 393 cgit_print_docend();
394 return; 394 return;
395 } 395 }
396 396
397 if (cmd->want_repo && !ctx->repo) { 397 if (cmd->want_repo && !ctx->repo) {
398 cgit_print_http_headers(ctx); 398 cgit_print_http_headers(ctx);
399 cgit_print_docstart(ctx); 399 cgit_print_docstart(ctx);
400 cgit_print_pageheader(ctx); 400 cgit_print_pageheader(ctx);
401 cgit_print_error(fmt("No repository selected")); 401 cgit_print_error(fmt("No repository selected"));
402 cgit_print_docend(); 402 cgit_print_docend();
403 return; 403 return;
404 } 404 }
405 405
406 if (ctx->repo && prepare_repo_cmd(ctx)) 406 if (ctx->repo && prepare_repo_cmd(ctx))
407 return; 407 return;
408 408
409 if (cmd->want_layout) { 409 if (cmd->want_layout) {
410 cgit_print_http_headers(ctx); 410 cgit_print_http_headers(ctx);
411 cgit_print_docstart(ctx); 411 cgit_print_docstart(ctx);
412 cgit_print_pageheader(ctx); 412 cgit_print_pageheader(ctx);
413 } 413 }
414 414
415 cmd->fn(ctx); 415 cmd->fn(ctx);
416 416
417 if (cmd->want_layout) 417 if (cmd->want_layout)
418 cgit_print_docend(); 418 cgit_print_docend();
419} 419}
420 420
421int cmp_repos(const void *a, const void *b) 421int cmp_repos(const void *a, const void *b)
422{ 422{
423 const struct cgit_repo *ra = a, *rb = b; 423 const struct cgit_repo *ra = a, *rb = b;
424 return strcmp(ra->url, rb->url); 424 return strcmp(ra->url, rb->url);
425} 425}
426 426
427void print_repo(FILE *f, struct cgit_repo *repo) 427void print_repo(FILE *f, struct cgit_repo *repo)
428{ 428{
429 fprintf(f, "repo.url=%s\n", repo->url); 429 fprintf(f, "repo.url=%s\n", repo->url);
430 fprintf(f, "repo.name=%s\n", repo->name); 430 fprintf(f, "repo.name=%s\n", repo->name);
431 fprintf(f, "repo.path=%s\n", repo->path); 431 fprintf(f, "repo.path=%s\n", repo->path);
432 if (repo->owner) 432 if (repo->owner)
433 fprintf(f, "repo.owner=%s\n", repo->owner); 433 fprintf(f, "repo.owner=%s\n", repo->owner);
434 if (repo->desc) 434 if (repo->desc)
435 fprintf(f, "repo.desc=%s\n", repo->desc); 435 fprintf(f, "repo.desc=%s\n", repo->desc);
436 if (repo->readme) 436 if (repo->readme)
437 fprintf(f, "repo.readme=%s\n", repo->readme); 437 fprintf(f, "repo.readme=%s\n", repo->readme);
438 fprintf(f, "\n"); 438 fprintf(f, "\n");
439} 439}
440 440
441void print_repolist(FILE *f, struct cgit_repolist *list, int start) 441void print_repolist(FILE *f, struct cgit_repolist *list, int start)
442{ 442{
443 int i; 443 int i;
444 444
445 for(i = start; i < list->count; i++) 445 for(i = start; i < list->count; i++)
446 print_repo(f, &list->repos[i]); 446 print_repo(f, &list->repos[i]);
447} 447}
448 448
449/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 449/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
450 * and return 0 on success. 450 * and return 0 on success.
451 */ 451 */
452static int generate_cached_repolist(const char *path, const char *cached_rc) 452static int generate_cached_repolist(const char *path, const char *cached_rc)
453{ 453{
454 char *locked_rc; 454 char *locked_rc;
455 int idx; 455 int idx;
456 FILE *f; 456 FILE *f;
457 457
458 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 458 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
459 f = fopen(locked_rc, "wx"); 459 f = fopen(locked_rc, "wx");
460 if (!f) { 460 if (!f) {
461 /* Inform about the error unless the lockfile already existed, 461 /* Inform about the error unless the lockfile already existed,
462 * since that only means we've got concurrent requests. 462 * since that only means we've got concurrent requests.
463 */ 463 */
464 if (errno != EEXIST) 464 if (errno != EEXIST)
465 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 465 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
466 locked_rc, strerror(errno), errno); 466 locked_rc, strerror(errno), errno);
467 return errno; 467 return errno;
468 } 468 }
469 idx = cgit_repolist.count; 469 idx = cgit_repolist.count;
470 scan_tree(path); 470 scan_tree(path);
471 print_repolist(f, &cgit_repolist, idx); 471 print_repolist(f, &cgit_repolist, idx);
472 if (rename(locked_rc, cached_rc)) 472 if (rename(locked_rc, cached_rc))
473 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 473 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
474 locked_rc, cached_rc, strerror(errno), errno); 474 locked_rc, cached_rc, strerror(errno), errno);
475 fclose(f); 475 fclose(f);
476 return 0; 476 return 0;
477} 477}
478 478
479static void process_cached_repolist(const char *path) 479static void process_cached_repolist(const char *path)
480{ 480{
481 struct stat st; 481 struct stat st;
482 char *cached_rc; 482 char *cached_rc;
483 time_t age; 483 time_t age;
484 484
485 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 485 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root,
486 hash_str(path))); 486 hash_str(path)));
487 487
488 if (stat(cached_rc, &st)) { 488 if (stat(cached_rc, &st)) {
489 /* Nothing is cached, we need to scan without forking. And 489 /* Nothing is cached, we need to scan without forking. And
490 * if we fail to generate a cached repolist, we need to 490 * if we fail to generate a cached repolist, we need to
491 * invoke scan_tree manually. 491 * invoke scan_tree manually.
492 */ 492 */
493 if (generate_cached_repolist(path, cached_rc)) 493 if (generate_cached_repolist(path, cached_rc))
494 scan_tree(path); 494 scan_tree(path);
495 return; 495 return;
496 } 496 }
497 497
498 parse_configfile(cached_rc, config_cb); 498 parse_configfile(cached_rc, config_cb);
499 499
500 /* If the cached configfile hasn't expired, lets exit now */ 500 /* If the cached configfile hasn't expired, lets exit now */
501 age = time(NULL) - st.st_mtime; 501 age = time(NULL) - st.st_mtime;
502 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 502 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
503 return; 503 return;
504 504
505 /* The cached repolist has been parsed, but it was old. So lets 505 /* The cached repolist has been parsed, but it was old. So lets
506 * rescan the specified path and generate a new cached repolist 506 * rescan the specified path and generate a new cached repolist
507 * in a child-process to avoid latency for the current request. 507 * in a child-process to avoid latency for the current request.
508 */ 508 */
509 if (fork()) 509 if (fork())
510 return; 510 return;
511 511
512 exit(generate_cached_repolist(path, cached_rc)); 512 exit(generate_cached_repolist(path, cached_rc));
513} 513}
514 514
515static void cgit_parse_args(int argc, const char **argv) 515static void cgit_parse_args(int argc, const char **argv)
516{ 516{
517 int i; 517 int i;
518 int scan = 0; 518 int scan = 0;
519 519
520 for (i = 1; i < argc; i++) { 520 for (i = 1; i < argc; i++) {
521 if (!strncmp(argv[i], "--cache=", 8)) { 521 if (!strncmp(argv[i], "--cache=", 8)) {
522 ctx.cfg.cache_root = xstrdup(argv[i]+8); 522 ctx.cfg.cache_root = xstrdup(argv[i]+8);
523 } 523 }
524 if (!strcmp(argv[i], "--nocache")) { 524 if (!strcmp(argv[i], "--nocache")) {
525 ctx.cfg.nocache = 1; 525 ctx.cfg.nocache = 1;
526 } 526 }
527 if (!strcmp(argv[i], "--nohttp")) { 527 if (!strcmp(argv[i], "--nohttp")) {
528 ctx.env.no_http = "1"; 528 ctx.env.no_http = "1";
529 } 529 }
530 if (!strncmp(argv[i], "--query=", 8)) { 530 if (!strncmp(argv[i], "--query=", 8)) {
531 ctx.qry.raw = xstrdup(argv[i]+8); 531 ctx.qry.raw = xstrdup(argv[i]+8);
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 68ae2ed..2abbd41 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,465 +1,465 @@
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 A value for the current repository group, which all repositories 203 A value for the current repository group, which all repositories
204 specified after this setting will inherit. Default value: none. 204 specified after this setting will inherit. Default value: none.
205 205
206repo.scan::
207 A path which will be scanned for repositories. If caching is enabled,
208 the result will be cached as a cgitrc include-file in the cache
209 directory. Default value: none. See also: cache-scanrc-ttl.
210
211robots:: 206robots::
212 Text used as content for the "robots" meta-tag. Default value: 207 Text used as content for the "robots" meta-tag. Default value:
213 "index, nofollow". 208 "index, nofollow".
214 209
215root-desc:: 210root-desc::
216 Text printed below the heading on the repository index page. Default 211 Text printed below the heading on the repository index page. Default
217 value: "a fast webinterface for the git dscm". 212 value: "a fast webinterface for the git dscm".
218 213
219root-readme:: 214root-readme::
220 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
221 verbatim below the "about" link on the repository index page. Default 216 verbatim below the "about" link on the repository index page. Default
222 value: none. 217 value: none.
223 218
224root-title:: 219root-title::
225 Text printed as heading on the repository index page. Default value: 220 Text printed as heading on the repository index page. Default value:
226 "Git Repository Browser". 221 "Git Repository Browser".
227 222
223scan-path::
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
226 directory. Default value: none. See also: cache-scanrc-ttl.
227
228snapshots:: 228snapshots::
229 Text which specifies the default set of snapshot formats generated by 229 Text which specifies the default set of snapshot formats generated by
230 cgit. The value is a space-separated list of zero or more of the 230 cgit. The value is a space-separated list of zero or more of the
231 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 231 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
232 232
233source-filter:: 233source-filter::
234 Specifies a command which will be invoked to format plaintext blobs 234 Specifies a command which will be invoked to format plaintext blobs
235 in the tree view. The command will get the blob content on its STDIN 235 in the tree view. The command will get the blob content on its STDIN
236 and the name of the blob as its only command line argument. The STDOUT 236 and the name of the blob as its only command line argument. The STDOUT
237 from the command will be included verbatim as the blob contents, i.e. 237 from the command will be included verbatim as the blob contents, i.e.
238 this can be used to implement e.g. syntax highlighting. Default value: 238 this can be used to implement e.g. syntax highlighting. Default value:
239 none. 239 none.
240 240
241summary-branches:: 241summary-branches::
242 Specifies the number of branches to display in the repository "summary" 242 Specifies the number of branches to display in the repository "summary"
243 view. Default value: "10". 243 view. Default value: "10".
244 244
245summary-log:: 245summary-log::
246 Specifies the number of log entries to display in the repository 246 Specifies the number of log entries to display in the repository
247 "summary" view. Default value: "10". 247 "summary" view. Default value: "10".
248 248
249summary-tags:: 249summary-tags::
250 Specifies the number of tags to display in the repository "summary" 250 Specifies the number of tags to display in the repository "summary"
251 view. Default value: "10". 251 view. Default value: "10".
252 252
253virtual-root:: 253virtual-root::
254 Url which, if specified, will be used as root for all cgit links. It 254 Url which, if specified, will be used as root for all cgit links. It
255 will also cause cgit to generate 'virtual urls', i.e. urls like 255 will also cause cgit to generate 'virtual urls', i.e. urls like
256 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 256 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
257 value: none. 257 value: none.
258 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 258 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
259 same kind of virtual urls, so this option will probably be deprecated. 259 same kind of virtual urls, so this option will probably be deprecated.
260 260
261REPOSITORY SETTINGS 261REPOSITORY SETTINGS
262------------------- 262-------------------
263repo.about-filter:: 263repo.about-filter::
264 Override the default about-filter. Default value: <about-filter>. 264 Override the default about-filter. Default value: <about-filter>.
265 265
266repo.clone-url:: 266repo.clone-url::
267 A list of space-separated urls which can be used to clone this repo. 267 A list of space-separated urls which can be used to clone this repo.
268 Default value: none. 268 Default value: none.
269 269
270repo.commit-filter:: 270repo.commit-filter::
271 Override the default commit-filter. Default value: <commit-filter>. 271 Override the default commit-filter. Default value: <commit-filter>.
272 272
273repo.defbranch:: 273repo.defbranch::
274 The name of the default branch for this repository. If no such branch 274 The name of the default branch for this repository. If no such branch
275 exists in the repository, the first branch name (when sorted) is used 275 exists in the repository, the first branch name (when sorted) is used
276 as default instead. Default value: "master". 276 as default instead. Default value: "master".
277 277
278repo.desc:: 278repo.desc::
279 The value to show as repository description. Default value: none. 279 The value to show as repository description. Default value: none.
280 280
281repo.enable-log-filecount:: 281repo.enable-log-filecount::
282 A flag which can be used to disable the global setting 282 A flag which can be used to disable the global setting
283 `enable-log-filecount'. Default value: none. 283 `enable-log-filecount'. Default value: none.
284 284
285repo.enable-log-linecount:: 285repo.enable-log-linecount::
286 A flag which can be used to disable the global setting 286 A flag which can be used to disable the global setting
287 `enable-log-linecount'. Default value: none. 287 `enable-log-linecount'. Default value: none.
288 288
289repo.max-stats:: 289repo.max-stats::
290 Override the default maximum statistics period. Valid values are equal 290 Override the default maximum statistics period. Valid values are equal
291 to the values specified for the global "max-stats" setting. Default 291 to the values specified for the global "max-stats" setting. Default
292 value: none. 292 value: none.
293 293
294repo.name:: 294repo.name::
295 The value to show as repository name. Default value: <repo.url>. 295 The value to show as repository name. Default value: <repo.url>.
296 296
297repo.owner:: 297repo.owner::
298 A value used to identify the owner of the repository. Default value: 298 A value used to identify the owner of the repository. Default value:
299 none. 299 none.
300 300
301repo.path:: 301repo.path::
302 An absolute path to the repository directory. For non-bare repositories 302 An absolute path to the repository directory. For non-bare repositories
303 this is the .git-directory. Default value: none. 303 this is the .git-directory. Default value: none.
304 304
305repo.readme:: 305repo.readme::
306 A path (relative to <repo.path>) which specifies a file to include 306 A path (relative to <repo.path>) which specifies a file to include
307 verbatim as the "About" page for this repo. Default value: none. 307 verbatim as the "About" page for this repo. Default value: none.
308 308
309repo.snapshots:: 309repo.snapshots::
310 A mask of allowed snapshot-formats for this repo, restricted by the 310 A mask of allowed snapshot-formats for this repo, restricted by the
311 "snapshots" global setting. Default value: <snapshots>. 311 "snapshots" global setting. Default value: <snapshots>.
312 312
313repo.source-filter:: 313repo.source-filter::
314 Override the default source-filter. Default value: <source-filter>. 314 Override the default source-filter. Default value: <source-filter>.
315 315
316repo.url:: 316repo.url::
317 The relative url used to access the repository. This must be the first 317 The relative url used to access the repository. This must be the first
318 setting specified for each repo. Default value: none. 318 setting specified for each repo. Default value: none.
319 319
320 320
321EXAMPLE CGITRC FILE 321EXAMPLE CGITRC FILE
322------------------- 322-------------------
323 323
324.... 324....
325# Enable caching of up to 1000 output entriess 325# Enable caching of up to 1000 output entriess
326cache-size=1000 326cache-size=1000
327 327
328 328
329# Specify some default clone prefixes 329# Specify some default clone prefixes
330clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git 330clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
331 331
332# Specify the css url 332# Specify the css url
333css=/css/cgit.css 333css=/css/cgit.css
334 334
335 335
336# Show extra links for each repository on the index page 336# Show extra links for each repository on the index page
337enable-index-links=1 337enable-index-links=1
338 338
339 339
340# Show number of affected files per commit on the log pages 340# Show number of affected files per commit on the log pages
341enable-log-filecount=1 341enable-log-filecount=1
342 342
343 343
344# Show number of added/removed lines per commit on the log pages 344# Show number of added/removed lines per commit on the log pages
345enable-log-linecount=1 345enable-log-linecount=1
346 346
347 347
348# Add a cgit favicon 348# Add a cgit favicon
349favicon=/favicon.ico 349favicon=/favicon.ico
350 350
351 351
352# Use a custom logo 352# Use a custom logo
353logo=/img/mylogo.png 353logo=/img/mylogo.png
354 354
355 355
356# Enable statistics per week, month and quarter 356# Enable statistics per week, month and quarter
357max-stats=quarter 357max-stats=quarter
358 358
359 359
360# Set the title and heading of the repository index page 360# Set the title and heading of the repository index page
361root-title=foobar.com git repositories 361root-title=foobar.com git repositories
362 362
363 363
364# Set a subheading for the repository index page 364# Set a subheading for the repository index page
365root-desc=tracking the foobar development 365root-desc=tracking the foobar development
366 366
367 367
368# Include some more info about foobar.com on the index page 368# Include some more info about foobar.com on the index page
369root-readme=/var/www/htdocs/about.html 369root-readme=/var/www/htdocs/about.html
370 370
371 371
372# Allow download of tar.gz, tar.bz2 and zip-files 372# Allow download of tar.gz, tar.bz2 and zip-files
373snapshots=tar.gz tar.bz2 zip 373snapshots=tar.gz tar.bz2 zip
374 374
375 375
376## 376##
377## List of common mimetypes 377## List of common mimetypes
378## 378##
379 379
380mimetype.git=image/git 380mimetype.git=image/git
381mimetype.html=text/html 381mimetype.html=text/html
382mimetype.jpg=image/jpeg 382mimetype.jpg=image/jpeg
383mimetype.jpeg=image/jpeg 383mimetype.jpeg=image/jpeg
384mimetype.pdf=application/pdf 384mimetype.pdf=application/pdf
385mimetype.png=image/png 385mimetype.png=image/png
386mimetype.svg=image/svg+xml 386mimetype.svg=image/svg+xml
387 387
388 388
389## 389##
390## List of repositories. 390## List of repositories.
391## PS: Any repositories listed when repo.group is unset will not be 391## PS: Any repositories listed when repo.group is unset will not be
392## displayed under a group heading 392## displayed under a group heading
393## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') 393## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
394## and included like this: 394## and included like this:
395## include=/etc/cgitrepos 395## include=/etc/cgitrepos
396## 396##
397 397
398 398
399repo.url=foo 399repo.url=foo
400repo.path=/pub/git/foo.git 400repo.path=/pub/git/foo.git
401repo.desc=the master foo repository 401repo.desc=the master foo repository
402repo.owner=fooman@foobar.com 402repo.owner=fooman@foobar.com
403repo.readme=info/web/about.html 403repo.readme=info/web/about.html
404 404
405 405
406repo.url=bar 406repo.url=bar
407repo.path=/pub/git/bar.git 407repo.path=/pub/git/bar.git
408repo.desc=the bars for your foo 408repo.desc=the bars for your foo
409repo.owner=barman@foobar.com 409repo.owner=barman@foobar.com
410repo.readme=info/web/about.html 410repo.readme=info/web/about.html
411 411
412 412
413# The next repositories will be displayed under the 'extras' heading 413# The next repositories will be displayed under the 'extras' heading
414repo.group=extras 414repo.group=extras
415 415
416 416
417repo.url=baz 417repo.url=baz
418repo.path=/pub/git/baz.git 418repo.path=/pub/git/baz.git
419repo.desc=a set of extensions for bar users 419repo.desc=a set of extensions for bar users
420 420
421repo.url=wiz 421repo.url=wiz
422repo.path=/pub/git/wiz.git 422repo.path=/pub/git/wiz.git
423repo.desc=the wizard of foo 423repo.desc=the wizard of foo
424 424
425 425
426# Add some mirrored repositories 426# Add some mirrored repositories
427repo.group=mirrors 427repo.group=mirrors
428 428
429 429
430repo.url=git 430repo.url=git
431repo.path=/pub/git/git.git 431repo.path=/pub/git/git.git
432repo.desc=the dscm 432repo.desc=the dscm
433 433
434 434
435repo.url=linux 435repo.url=linux
436repo.path=/pub/git/linux.git 436repo.path=/pub/git/linux.git
437repo.desc=the kernel 437repo.desc=the kernel
438 438
439# Disable adhoc downloads of this repo 439# Disable adhoc downloads of this repo
440repo.snapshots=0 440repo.snapshots=0
441 441
442# Disable line-counts for this repo 442# Disable line-counts for this repo
443repo.enable-log-linecount=0 443repo.enable-log-linecount=0
444 444
445# Restrict the max statistics period for this repo 445# Restrict the max statistics period for this repo
446repo.max-stats=month 446repo.max-stats=month
447.... 447....
448 448
449 449
450BUGS 450BUGS
451---- 451----
452Comments currently cannot appear on the same line as a setting; the comment 452Comments currently cannot appear on the same line as a setting; the comment
453will be included as part of the value. E.g. this line: 453will be included as part of the value. E.g. this line:
454 454
455 robots=index # allow indexing 455 robots=index # allow indexing
456 456
457will generate the following html element: 457will generate the following html element:
458 458
459 <meta name='robots' content='index # allow indexing'/> 459 <meta name='robots' content='index # allow indexing'/>
460 460
461 461
462 462
463AUTHOR 463AUTHOR
464------ 464------
465Lars Hjemli <hjemli@gmail.com> 465Lars Hjemli <hjemli@gmail.com>