summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2010-08-04 01:10:29 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2010-08-04 01:10:29 (UTC)
commitaec9c245e7eaf444c7ae1851e3eda3b30748950f (patch) (unidiff)
tree04d5e945781441f2589ae8eb44bee8d5a2b17c2b
parente6c960c7c0f0d2e54b51cc43ef190df3ce52755e (diff)
parent119397b175874bd606952e93b7249ae4ffb9afbe (diff)
downloadcgit-aec9c245e7eaf444c7ae1851e3eda3b30748950f.zip
cgit-aec9c245e7eaf444c7ae1851e3eda3b30748950f.tar.gz
cgit-aec9c245e7eaf444c7ae1851e3eda3b30748950f.tar.bz2
Merge branch 'jd/gitolite'
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c33
-rw-r--r--cgit.h3
-rw-r--r--cgitrc.5.txt21
-rw-r--r--scan-tree.c73
-rw-r--r--scan-tree.h3
5 files changed, 116 insertions, 17 deletions
diff --git a/cgit.c b/cgit.c
index c263872..eff5b7a 100644
--- a/cgit.c
+++ b/cgit.c
@@ -90,257 +90,269 @@ void config_cb(const char *name, const char *value)
90 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 90 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
91 ctx.cfg.section = xstrdup(value); 91 ctx.cfg.section = xstrdup(value);
92 else if (!strcmp(name, "repo.url")) 92 else if (!strcmp(name, "repo.url"))
93 ctx.repo = cgit_add_repo(value); 93 ctx.repo = cgit_add_repo(value);
94 else if (ctx.repo && !strcmp(name, "repo.path")) 94 else if (ctx.repo && !strcmp(name, "repo.path"))
95 ctx.repo->path = trim_end(value, '/'); 95 ctx.repo->path = trim_end(value, '/');
96 else if (ctx.repo && !prefixcmp(name, "repo.")) 96 else if (ctx.repo && !prefixcmp(name, "repo."))
97 repo_config(ctx.repo, name + 5, value); 97 repo_config(ctx.repo, name + 5, value);
98 else if (!strcmp(name, "root-title")) 98 else if (!strcmp(name, "root-title"))
99 ctx.cfg.root_title = xstrdup(value); 99 ctx.cfg.root_title = xstrdup(value);
100 else if (!strcmp(name, "root-desc")) 100 else if (!strcmp(name, "root-desc"))
101 ctx.cfg.root_desc = xstrdup(value); 101 ctx.cfg.root_desc = xstrdup(value);
102 else if (!strcmp(name, "root-readme")) 102 else if (!strcmp(name, "root-readme"))
103 ctx.cfg.root_readme = xstrdup(value); 103 ctx.cfg.root_readme = xstrdup(value);
104 else if (!strcmp(name, "css")) 104 else if (!strcmp(name, "css"))
105 ctx.cfg.css = xstrdup(value); 105 ctx.cfg.css = xstrdup(value);
106 else if (!strcmp(name, "favicon")) 106 else if (!strcmp(name, "favicon"))
107 ctx.cfg.favicon = xstrdup(value); 107 ctx.cfg.favicon = xstrdup(value);
108 else if (!strcmp(name, "footer")) 108 else if (!strcmp(name, "footer"))
109 ctx.cfg.footer = xstrdup(value); 109 ctx.cfg.footer = xstrdup(value);
110 else if (!strcmp(name, "head-include")) 110 else if (!strcmp(name, "head-include"))
111 ctx.cfg.head_include = xstrdup(value); 111 ctx.cfg.head_include = xstrdup(value);
112 else if (!strcmp(name, "header")) 112 else if (!strcmp(name, "header"))
113 ctx.cfg.header = xstrdup(value); 113 ctx.cfg.header = xstrdup(value);
114 else if (!strcmp(name, "logo")) 114 else if (!strcmp(name, "logo"))
115 ctx.cfg.logo = xstrdup(value); 115 ctx.cfg.logo = xstrdup(value);
116 else if (!strcmp(name, "index-header")) 116 else if (!strcmp(name, "index-header"))
117 ctx.cfg.index_header = xstrdup(value); 117 ctx.cfg.index_header = xstrdup(value);
118 else if (!strcmp(name, "index-info")) 118 else if (!strcmp(name, "index-info"))
119 ctx.cfg.index_info = xstrdup(value); 119 ctx.cfg.index_info = xstrdup(value);
120 else if (!strcmp(name, "logo-link")) 120 else if (!strcmp(name, "logo-link"))
121 ctx.cfg.logo_link = xstrdup(value); 121 ctx.cfg.logo_link = xstrdup(value);
122 else if (!strcmp(name, "module-link")) 122 else if (!strcmp(name, "module-link"))
123 ctx.cfg.module_link = xstrdup(value); 123 ctx.cfg.module_link = xstrdup(value);
124 else if (!strcmp(name, "virtual-root")) { 124 else if (!strcmp(name, "virtual-root")) {
125 ctx.cfg.virtual_root = trim_end(value, '/'); 125 ctx.cfg.virtual_root = trim_end(value, '/');
126 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 126 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
127 ctx.cfg.virtual_root = ""; 127 ctx.cfg.virtual_root = "";
128 } else if (!strcmp(name, "nocache")) 128 } else if (!strcmp(name, "nocache"))
129 ctx.cfg.nocache = atoi(value); 129 ctx.cfg.nocache = atoi(value);
130 else if (!strcmp(name, "noplainemail")) 130 else if (!strcmp(name, "noplainemail"))
131 ctx.cfg.noplainemail = atoi(value); 131 ctx.cfg.noplainemail = atoi(value);
132 else if (!strcmp(name, "noheader")) 132 else if (!strcmp(name, "noheader"))
133 ctx.cfg.noheader = atoi(value); 133 ctx.cfg.noheader = atoi(value);
134 else if (!strcmp(name, "snapshots")) 134 else if (!strcmp(name, "snapshots"))
135 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 135 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
136 else if (!strcmp(name, "enable-filter-overrides")) 136 else if (!strcmp(name, "enable-filter-overrides"))
137 ctx.cfg.enable_filter_overrides = atoi(value); 137 ctx.cfg.enable_filter_overrides = atoi(value);
138 else if (!strcmp(name, "enable-gitweb-owner"))
139 ctx.cfg.enable_gitweb_owner = atoi(value);
138 else if (!strcmp(name, "enable-index-links")) 140 else if (!strcmp(name, "enable-index-links"))
139 ctx.cfg.enable_index_links = atoi(value); 141 ctx.cfg.enable_index_links = atoi(value);
140 else if (!strcmp(name, "enable-log-filecount")) 142 else if (!strcmp(name, "enable-log-filecount"))
141 ctx.cfg.enable_log_filecount = atoi(value); 143 ctx.cfg.enable_log_filecount = atoi(value);
142 else if (!strcmp(name, "enable-log-linecount")) 144 else if (!strcmp(name, "enable-log-linecount"))
143 ctx.cfg.enable_log_linecount = atoi(value); 145 ctx.cfg.enable_log_linecount = atoi(value);
144 else if (!strcmp(name, "enable-remote-branches")) 146 else if (!strcmp(name, "enable-remote-branches"))
145 ctx.cfg.enable_remote_branches = atoi(value); 147 ctx.cfg.enable_remote_branches = atoi(value);
146 else if (!strcmp(name, "enable-subject-links")) 148 else if (!strcmp(name, "enable-subject-links"))
147 ctx.cfg.enable_subject_links = atoi(value); 149 ctx.cfg.enable_subject_links = atoi(value);
148 else if (!strcmp(name, "enable-tree-linenumbers")) 150 else if (!strcmp(name, "enable-tree-linenumbers"))
149 ctx.cfg.enable_tree_linenumbers = atoi(value); 151 ctx.cfg.enable_tree_linenumbers = atoi(value);
150 else if (!strcmp(name, "max-stats")) 152 else if (!strcmp(name, "max-stats"))
151 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 153 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
152 else if (!strcmp(name, "cache-size")) 154 else if (!strcmp(name, "cache-size"))
153 ctx.cfg.cache_size = atoi(value); 155 ctx.cfg.cache_size = atoi(value);
154 else if (!strcmp(name, "cache-root")) 156 else if (!strcmp(name, "cache-root"))
155 ctx.cfg.cache_root = xstrdup(expand_macros(value)); 157 ctx.cfg.cache_root = xstrdup(expand_macros(value));
156 else if (!strcmp(name, "cache-root-ttl")) 158 else if (!strcmp(name, "cache-root-ttl"))
157 ctx.cfg.cache_root_ttl = atoi(value); 159 ctx.cfg.cache_root_ttl = atoi(value);
158 else if (!strcmp(name, "cache-repo-ttl")) 160 else if (!strcmp(name, "cache-repo-ttl"))
159 ctx.cfg.cache_repo_ttl = atoi(value); 161 ctx.cfg.cache_repo_ttl = atoi(value);
160 else if (!strcmp(name, "cache-scanrc-ttl")) 162 else if (!strcmp(name, "cache-scanrc-ttl"))
161 ctx.cfg.cache_scanrc_ttl = atoi(value); 163 ctx.cfg.cache_scanrc_ttl = atoi(value);
162 else if (!strcmp(name, "cache-static-ttl")) 164 else if (!strcmp(name, "cache-static-ttl"))
163 ctx.cfg.cache_static_ttl = atoi(value); 165 ctx.cfg.cache_static_ttl = atoi(value);
164 else if (!strcmp(name, "cache-dynamic-ttl")) 166 else if (!strcmp(name, "cache-dynamic-ttl"))
165 ctx.cfg.cache_dynamic_ttl = atoi(value); 167 ctx.cfg.cache_dynamic_ttl = atoi(value);
166 else if (!strcmp(name, "about-filter")) 168 else if (!strcmp(name, "about-filter"))
167 ctx.cfg.about_filter = new_filter(value, 0); 169 ctx.cfg.about_filter = new_filter(value, 0);
168 else if (!strcmp(name, "commit-filter")) 170 else if (!strcmp(name, "commit-filter"))
169 ctx.cfg.commit_filter = new_filter(value, 0); 171 ctx.cfg.commit_filter = new_filter(value, 0);
170 else if (!strcmp(name, "embedded")) 172 else if (!strcmp(name, "embedded"))
171 ctx.cfg.embedded = atoi(value); 173 ctx.cfg.embedded = atoi(value);
172 else if (!strcmp(name, "max-atom-items")) 174 else if (!strcmp(name, "max-atom-items"))
173 ctx.cfg.max_atom_items = atoi(value); 175 ctx.cfg.max_atom_items = atoi(value);
174 else if (!strcmp(name, "max-message-length")) 176 else if (!strcmp(name, "max-message-length"))
175 ctx.cfg.max_msg_len = atoi(value); 177 ctx.cfg.max_msg_len = atoi(value);
176 else if (!strcmp(name, "max-repodesc-length")) 178 else if (!strcmp(name, "max-repodesc-length"))
177 ctx.cfg.max_repodesc_len = atoi(value); 179 ctx.cfg.max_repodesc_len = atoi(value);
178 else if (!strcmp(name, "max-blob-size")) 180 else if (!strcmp(name, "max-blob-size"))
179 ctx.cfg.max_blob_size = atoi(value); 181 ctx.cfg.max_blob_size = atoi(value);
180 else if (!strcmp(name, "max-repo-count")) 182 else if (!strcmp(name, "max-repo-count"))
181 ctx.cfg.max_repo_count = atoi(value); 183 ctx.cfg.max_repo_count = atoi(value);
182 else if (!strcmp(name, "max-commit-count")) 184 else if (!strcmp(name, "max-commit-count"))
183 ctx.cfg.max_commit_count = atoi(value); 185 ctx.cfg.max_commit_count = atoi(value);
186 else if (!strcmp(name, "project-list"))
187 ctx.cfg.project_list = xstrdup(expand_macros(value));
184 else if (!strcmp(name, "scan-path")) 188 else if (!strcmp(name, "scan-path"))
185 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 189 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
186 process_cached_repolist(expand_macros(value)); 190 process_cached_repolist(expand_macros(value));
191 else if (ctx.cfg.project_list)
192 scan_projects(expand_macros(value),
193 ctx.cfg.project_list, repo_config);
187 else 194 else
188 scan_tree(expand_macros(value), repo_config); 195 scan_tree(expand_macros(value), repo_config);
189 else if (!strcmp(name, "source-filter")) 196 else if (!strcmp(name, "source-filter"))
190 ctx.cfg.source_filter = new_filter(value, 1); 197 ctx.cfg.source_filter = new_filter(value, 1);
191 else if (!strcmp(name, "summary-log")) 198 else if (!strcmp(name, "summary-log"))
192 ctx.cfg.summary_log = atoi(value); 199 ctx.cfg.summary_log = atoi(value);
193 else if (!strcmp(name, "summary-branches")) 200 else if (!strcmp(name, "summary-branches"))
194 ctx.cfg.summary_branches = atoi(value); 201 ctx.cfg.summary_branches = atoi(value);
195 else if (!strcmp(name, "summary-tags")) 202 else if (!strcmp(name, "summary-tags"))
196 ctx.cfg.summary_tags = atoi(value); 203 ctx.cfg.summary_tags = atoi(value);
197 else if (!strcmp(name, "side-by-side-diffs")) 204 else if (!strcmp(name, "side-by-side-diffs"))
198 ctx.cfg.ssdiff = atoi(value); 205 ctx.cfg.ssdiff = atoi(value);
199 else if (!strcmp(name, "agefile")) 206 else if (!strcmp(name, "agefile"))
200 ctx.cfg.agefile = xstrdup(value); 207 ctx.cfg.agefile = xstrdup(value);
201 else if (!strcmp(name, "renamelimit")) 208 else if (!strcmp(name, "renamelimit"))
202 ctx.cfg.renamelimit = atoi(value); 209 ctx.cfg.renamelimit = atoi(value);
210 else if (!strcmp(name, "remove-suffix"))
211 ctx.cfg.remove_suffix = atoi(value);
203 else if (!strcmp(name, "robots")) 212 else if (!strcmp(name, "robots"))
204 ctx.cfg.robots = xstrdup(value); 213 ctx.cfg.robots = xstrdup(value);
205 else if (!strcmp(name, "clone-prefix")) 214 else if (!strcmp(name, "clone-prefix"))
206 ctx.cfg.clone_prefix = xstrdup(value); 215 ctx.cfg.clone_prefix = xstrdup(value);
207 else if (!strcmp(name, "local-time")) 216 else if (!strcmp(name, "local-time"))
208 ctx.cfg.local_time = atoi(value); 217 ctx.cfg.local_time = atoi(value);
209 else if (!prefixcmp(name, "mimetype.")) 218 else if (!prefixcmp(name, "mimetype."))
210 add_mimetype(name + 9, value); 219 add_mimetype(name + 9, value);
211 else if (!strcmp(name, "include")) 220 else if (!strcmp(name, "include"))
212 parse_configfile(expand_macros(value), config_cb); 221 parse_configfile(expand_macros(value), config_cb);
213} 222}
214 223
215static void querystring_cb(const char *name, const char *value) 224static void querystring_cb(const char *name, const char *value)
216{ 225{
217 if (!value) 226 if (!value)
218 value = ""; 227 value = "";
219 228
220 if (!strcmp(name,"r")) { 229 if (!strcmp(name,"r")) {
221 ctx.qry.repo = xstrdup(value); 230 ctx.qry.repo = xstrdup(value);
222 ctx.repo = cgit_get_repoinfo(value); 231 ctx.repo = cgit_get_repoinfo(value);
223 } else if (!strcmp(name, "p")) { 232 } else if (!strcmp(name, "p")) {
224 ctx.qry.page = xstrdup(value); 233 ctx.qry.page = xstrdup(value);
225 } else if (!strcmp(name, "url")) { 234 } else if (!strcmp(name, "url")) {
226 if (*value == '/') 235 if (*value == '/')
227 value++; 236 value++;
228 ctx.qry.url = xstrdup(value); 237 ctx.qry.url = xstrdup(value);
229 cgit_parse_url(value); 238 cgit_parse_url(value);
230 } else if (!strcmp(name, "qt")) { 239 } else if (!strcmp(name, "qt")) {
231 ctx.qry.grep = xstrdup(value); 240 ctx.qry.grep = xstrdup(value);
232 } else if (!strcmp(name, "q")) { 241 } else if (!strcmp(name, "q")) {
233 ctx.qry.search = xstrdup(value); 242 ctx.qry.search = xstrdup(value);
234 } else if (!strcmp(name, "h")) { 243 } else if (!strcmp(name, "h")) {
235 ctx.qry.head = xstrdup(value); 244 ctx.qry.head = xstrdup(value);
236 ctx.qry.has_symref = 1; 245 ctx.qry.has_symref = 1;
237 } else if (!strcmp(name, "id")) { 246 } else if (!strcmp(name, "id")) {
238 ctx.qry.sha1 = xstrdup(value); 247 ctx.qry.sha1 = xstrdup(value);
239 ctx.qry.has_sha1 = 1; 248 ctx.qry.has_sha1 = 1;
240 } else if (!strcmp(name, "id2")) { 249 } else if (!strcmp(name, "id2")) {
241 ctx.qry.sha2 = xstrdup(value); 250 ctx.qry.sha2 = xstrdup(value);
242 ctx.qry.has_sha1 = 1; 251 ctx.qry.has_sha1 = 1;
243 } else if (!strcmp(name, "ofs")) { 252 } else if (!strcmp(name, "ofs")) {
244 ctx.qry.ofs = atoi(value); 253 ctx.qry.ofs = atoi(value);
245 } else if (!strcmp(name, "path")) { 254 } else if (!strcmp(name, "path")) {
246 ctx.qry.path = trim_end(value, '/'); 255 ctx.qry.path = trim_end(value, '/');
247 } else if (!strcmp(name, "name")) { 256 } else if (!strcmp(name, "name")) {
248 ctx.qry.name = xstrdup(value); 257 ctx.qry.name = xstrdup(value);
249 } else if (!strcmp(name, "mimetype")) { 258 } else if (!strcmp(name, "mimetype")) {
250 ctx.qry.mimetype = xstrdup(value); 259 ctx.qry.mimetype = xstrdup(value);
251 } else if (!strcmp(name, "s")){ 260 } else if (!strcmp(name, "s")){
252 ctx.qry.sort = xstrdup(value); 261 ctx.qry.sort = xstrdup(value);
253 } else if (!strcmp(name, "showmsg")) { 262 } else if (!strcmp(name, "showmsg")) {
254 ctx.qry.showmsg = atoi(value); 263 ctx.qry.showmsg = atoi(value);
255 } else if (!strcmp(name, "period")) { 264 } else if (!strcmp(name, "period")) {
256 ctx.qry.period = xstrdup(value); 265 ctx.qry.period = xstrdup(value);
257 } else if (!strcmp(name, "ss")) { 266 } else if (!strcmp(name, "ss")) {
258 ctx.qry.ssdiff = atoi(value); 267 ctx.qry.ssdiff = atoi(value);
259 } else if (!strcmp(name, "all")) { 268 } else if (!strcmp(name, "all")) {
260 ctx.qry.show_all = atoi(value); 269 ctx.qry.show_all = atoi(value);
261 } else if (!strcmp(name, "context")) { 270 } else if (!strcmp(name, "context")) {
262 ctx.qry.context = atoi(value); 271 ctx.qry.context = atoi(value);
263 } else if (!strcmp(name, "ignorews")) { 272 } else if (!strcmp(name, "ignorews")) {
264 ctx.qry.ignorews = atoi(value); 273 ctx.qry.ignorews = atoi(value);
265 } 274 }
266} 275}
267 276
268char *xstrdupn(const char *str) 277char *xstrdupn(const char *str)
269{ 278{
270 return (str ? xstrdup(str) : NULL); 279 return (str ? xstrdup(str) : NULL);
271} 280}
272 281
273static void prepare_context(struct cgit_context *ctx) 282static void prepare_context(struct cgit_context *ctx)
274{ 283{
275 memset(ctx, 0, sizeof(*ctx)); 284 memset(ctx, 0, sizeof(*ctx));
276 ctx->cfg.agefile = "info/web/last-modified"; 285 ctx->cfg.agefile = "info/web/last-modified";
277 ctx->cfg.nocache = 0; 286 ctx->cfg.nocache = 0;
278 ctx->cfg.cache_size = 0; 287 ctx->cfg.cache_size = 0;
279 ctx->cfg.cache_dynamic_ttl = 5; 288 ctx->cfg.cache_dynamic_ttl = 5;
280 ctx->cfg.cache_max_create_time = 5; 289 ctx->cfg.cache_max_create_time = 5;
281 ctx->cfg.cache_repo_ttl = 5; 290 ctx->cfg.cache_repo_ttl = 5;
282 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 291 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
283 ctx->cfg.cache_root_ttl = 5; 292 ctx->cfg.cache_root_ttl = 5;
284 ctx->cfg.cache_scanrc_ttl = 15; 293 ctx->cfg.cache_scanrc_ttl = 15;
285 ctx->cfg.cache_static_ttl = -1; 294 ctx->cfg.cache_static_ttl = -1;
286 ctx->cfg.css = "/cgit.css"; 295 ctx->cfg.css = "/cgit.css";
287 ctx->cfg.logo = "/cgit.png"; 296 ctx->cfg.logo = "/cgit.png";
288 ctx->cfg.local_time = 0; 297 ctx->cfg.local_time = 0;
298 ctx->cfg.enable_gitweb_owner = 1;
289 ctx->cfg.enable_tree_linenumbers = 1; 299 ctx->cfg.enable_tree_linenumbers = 1;
290 ctx->cfg.max_repo_count = 50; 300 ctx->cfg.max_repo_count = 50;
291 ctx->cfg.max_commit_count = 50; 301 ctx->cfg.max_commit_count = 50;
292 ctx->cfg.max_lock_attempts = 5; 302 ctx->cfg.max_lock_attempts = 5;
293 ctx->cfg.max_msg_len = 80; 303 ctx->cfg.max_msg_len = 80;
294 ctx->cfg.max_repodesc_len = 80; 304 ctx->cfg.max_repodesc_len = 80;
295 ctx->cfg.max_blob_size = 0; 305 ctx->cfg.max_blob_size = 0;
296 ctx->cfg.max_stats = 0; 306 ctx->cfg.max_stats = 0;
297 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 307 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
308 ctx->cfg.project_list = NULL;
298 ctx->cfg.renamelimit = -1; 309 ctx->cfg.renamelimit = -1;
310 ctx->cfg.remove_suffix = 0;
299 ctx->cfg.robots = "index, nofollow"; 311 ctx->cfg.robots = "index, nofollow";
300 ctx->cfg.root_title = "Git repository browser"; 312 ctx->cfg.root_title = "Git repository browser";
301 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 313 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
302 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 314 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
303 ctx->cfg.section = ""; 315 ctx->cfg.section = "";
304 ctx->cfg.summary_branches = 10; 316 ctx->cfg.summary_branches = 10;
305 ctx->cfg.summary_log = 10; 317 ctx->cfg.summary_log = 10;
306 ctx->cfg.summary_tags = 10; 318 ctx->cfg.summary_tags = 10;
307 ctx->cfg.max_atom_items = 10; 319 ctx->cfg.max_atom_items = 10;
308 ctx->cfg.ssdiff = 0; 320 ctx->cfg.ssdiff = 0;
309 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 321 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
310 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 322 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
311 ctx->env.https = xstrdupn(getenv("HTTPS")); 323 ctx->env.https = xstrdupn(getenv("HTTPS"));
312 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 324 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
313 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 325 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
314 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 326 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
315 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 327 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
316 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 328 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
317 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 329 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
318 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 330 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
319 ctx->page.mimetype = "text/html"; 331 ctx->page.mimetype = "text/html";
320 ctx->page.charset = PAGE_ENCODING; 332 ctx->page.charset = PAGE_ENCODING;
321 ctx->page.filename = NULL; 333 ctx->page.filename = NULL;
322 ctx->page.size = 0; 334 ctx->page.size = 0;
323 ctx->page.modified = time(NULL); 335 ctx->page.modified = time(NULL);
324 ctx->page.expires = ctx->page.modified; 336 ctx->page.expires = ctx->page.modified;
325 ctx->page.etag = NULL; 337 ctx->page.etag = NULL;
326 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 338 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
327 if (ctx->env.script_name) 339 if (ctx->env.script_name)
328 ctx->cfg.script_name = ctx->env.script_name; 340 ctx->cfg.script_name = ctx->env.script_name;
329 if (ctx->env.query_string) 341 if (ctx->env.query_string)
330 ctx->qry.raw = ctx->env.query_string; 342 ctx->qry.raw = ctx->env.query_string;
331 if (!ctx->env.cgit_config) 343 if (!ctx->env.cgit_config)
332 ctx->env.cgit_config = CGIT_CONFIG; 344 ctx->env.cgit_config = CGIT_CONFIG;
333} 345}
334 346
335struct refmatch { 347struct refmatch {
336 char *req_ref; 348 char *req_ref;
337 char *first_ref; 349 char *first_ref;
338 int match; 350 int match;
339}; 351};
340 352
341int find_current_ref(const char *refname, const unsigned char *sha1, 353int find_current_ref(const char *refname, const unsigned char *sha1,
342 int flags, void *cb_data) 354 int flags, void *cb_data)
343{ 355{
344 struct refmatch *info; 356 struct refmatch *info;
345 357
346 info = (struct refmatch *)cb_data; 358 info = (struct refmatch *)cb_data;
@@ -529,121 +541,132 @@ void print_repo(FILE *f, struct cgit_repo *repo)
529 fprintf(f, "repo.enable-log-linecount=%d\n", 541 fprintf(f, "repo.enable-log-linecount=%d\n",
530 repo->enable_log_linecount); 542 repo->enable_log_linecount);
531 if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter) 543 if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter)
532 fprintf(f, "repo.about-filter=%s\n", repo->about_filter->cmd); 544 fprintf(f, "repo.about-filter=%s\n", repo->about_filter->cmd);
533 if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter) 545 if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter)
534 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd); 546 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd);
535 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter) 547 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
536 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); 548 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd);
537 if (repo->snapshots != ctx.cfg.snapshots) { 549 if (repo->snapshots != ctx.cfg.snapshots) {
538 char *tmp = build_snapshot_setting(repo->snapshots); 550 char *tmp = build_snapshot_setting(repo->snapshots);
539 fprintf(f, "repo.snapshots=%s\n", tmp); 551 fprintf(f, "repo.snapshots=%s\n", tmp);
540 free(tmp); 552 free(tmp);
541 } 553 }
542 if (repo->max_stats != ctx.cfg.max_stats) 554 if (repo->max_stats != ctx.cfg.max_stats)
543 fprintf(f, "repo.max-stats=%s\n", 555 fprintf(f, "repo.max-stats=%s\n",
544 cgit_find_stats_periodname(repo->max_stats)); 556 cgit_find_stats_periodname(repo->max_stats));
545 fprintf(f, "\n"); 557 fprintf(f, "\n");
546} 558}
547 559
548void print_repolist(FILE *f, struct cgit_repolist *list, int start) 560void print_repolist(FILE *f, struct cgit_repolist *list, int start)
549{ 561{
550 int i; 562 int i;
551 563
552 for(i = start; i < list->count; i++) 564 for(i = start; i < list->count; i++)
553 print_repo(f, &list->repos[i]); 565 print_repo(f, &list->repos[i]);
554} 566}
555 567
556/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 568/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
557 * and return 0 on success. 569 * and return 0 on success.
558 */ 570 */
559static int generate_cached_repolist(const char *path, const char *cached_rc) 571static int generate_cached_repolist(const char *path, const char *cached_rc)
560{ 572{
561 char *locked_rc; 573 char *locked_rc;
562 int idx; 574 int idx;
563 FILE *f; 575 FILE *f;
564 576
565 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 577 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
566 f = fopen(locked_rc, "wx"); 578 f = fopen(locked_rc, "wx");
567 if (!f) { 579 if (!f) {
568 /* Inform about the error unless the lockfile already existed, 580 /* Inform about the error unless the lockfile already existed,
569 * since that only means we've got concurrent requests. 581 * since that only means we've got concurrent requests.
570 */ 582 */
571 if (errno != EEXIST) 583 if (errno != EEXIST)
572 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 584 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
573 locked_rc, strerror(errno), errno); 585 locked_rc, strerror(errno), errno);
574 return errno; 586 return errno;
575 } 587 }
576 idx = cgit_repolist.count; 588 idx = cgit_repolist.count;
577 scan_tree(path, repo_config); 589 if (ctx.cfg.project_list)
590 scan_projects(path, ctx.cfg.project_list, repo_config);
591 else
592 scan_tree(path, repo_config);
578 print_repolist(f, &cgit_repolist, idx); 593 print_repolist(f, &cgit_repolist, idx);
579 if (rename(locked_rc, cached_rc)) 594 if (rename(locked_rc, cached_rc))
580 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 595 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
581 locked_rc, cached_rc, strerror(errno), errno); 596 locked_rc, cached_rc, strerror(errno), errno);
582 fclose(f); 597 fclose(f);
583 return 0; 598 return 0;
584} 599}
585 600
586static void process_cached_repolist(const char *path) 601static void process_cached_repolist(const char *path)
587{ 602{
588 struct stat st; 603 struct stat st;
589 char *cached_rc; 604 char *cached_rc;
590 time_t age; 605 time_t age;
606 unsigned long hash;
591 607
592 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 608 hash = hash_str(path);
593 hash_str(path))); 609 if (ctx.cfg.project_list)
610 hash += hash_str(ctx.cfg.project_list);
611 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, hash));
594 612
595 if (stat(cached_rc, &st)) { 613 if (stat(cached_rc, &st)) {
596 /* Nothing is cached, we need to scan without forking. And 614 /* Nothing is cached, we need to scan without forking. And
597 * if we fail to generate a cached repolist, we need to 615 * if we fail to generate a cached repolist, we need to
598 * invoke scan_tree manually. 616 * invoke scan_tree manually.
599 */ 617 */
600 if (generate_cached_repolist(path, cached_rc)) 618 if (generate_cached_repolist(path, cached_rc)) {
601 scan_tree(path, repo_config); 619 if (ctx.cfg.project_list)
620 scan_projects(path, ctx.cfg.project_list,
621 repo_config);
622 else
623 scan_tree(path, repo_config);
624 }
602 return; 625 return;
603 } 626 }
604 627
605 parse_configfile(cached_rc, config_cb); 628 parse_configfile(cached_rc, config_cb);
606 629
607 /* If the cached configfile hasn't expired, lets exit now */ 630 /* If the cached configfile hasn't expired, lets exit now */
608 age = time(NULL) - st.st_mtime; 631 age = time(NULL) - st.st_mtime;
609 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 632 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
610 return; 633 return;
611 634
612 /* The cached repolist has been parsed, but it was old. So lets 635 /* The cached repolist has been parsed, but it was old. So lets
613 * rescan the specified path and generate a new cached repolist 636 * rescan the specified path and generate a new cached repolist
614 * in a child-process to avoid latency for the current request. 637 * in a child-process to avoid latency for the current request.
615 */ 638 */
616 if (fork()) 639 if (fork())
617 return; 640 return;
618 641
619 exit(generate_cached_repolist(path, cached_rc)); 642 exit(generate_cached_repolist(path, cached_rc));
620} 643}
621 644
622static void cgit_parse_args(int argc, const char **argv) 645static void cgit_parse_args(int argc, const char **argv)
623{ 646{
624 int i; 647 int i;
625 int scan = 0; 648 int scan = 0;
626 649
627 for (i = 1; i < argc; i++) { 650 for (i = 1; i < argc; i++) {
628 if (!strncmp(argv[i], "--cache=", 8)) { 651 if (!strncmp(argv[i], "--cache=", 8)) {
629 ctx.cfg.cache_root = xstrdup(argv[i]+8); 652 ctx.cfg.cache_root = xstrdup(argv[i]+8);
630 } 653 }
631 if (!strcmp(argv[i], "--nocache")) { 654 if (!strcmp(argv[i], "--nocache")) {
632 ctx.cfg.nocache = 1; 655 ctx.cfg.nocache = 1;
633 } 656 }
634 if (!strcmp(argv[i], "--nohttp")) { 657 if (!strcmp(argv[i], "--nohttp")) {
635 ctx.env.no_http = "1"; 658 ctx.env.no_http = "1";
636 } 659 }
637 if (!strncmp(argv[i], "--query=", 8)) { 660 if (!strncmp(argv[i], "--query=", 8)) {
638 ctx.qry.raw = xstrdup(argv[i]+8); 661 ctx.qry.raw = xstrdup(argv[i]+8);
639 } 662 }
640 if (!strncmp(argv[i], "--repo=", 7)) { 663 if (!strncmp(argv[i], "--repo=", 7)) {
641 ctx.qry.repo = xstrdup(argv[i]+7); 664 ctx.qry.repo = xstrdup(argv[i]+7);
642 } 665 }
643 if (!strncmp(argv[i], "--page=", 7)) { 666 if (!strncmp(argv[i], "--page=", 7)) {
644 ctx.qry.page = xstrdup(argv[i]+7); 667 ctx.qry.page = xstrdup(argv[i]+7);
645 } 668 }
646 if (!strncmp(argv[i], "--head=", 7)) { 669 if (!strncmp(argv[i], "--head=", 7)) {
647 ctx.qry.head = xstrdup(argv[i]+7); 670 ctx.qry.head = xstrdup(argv[i]+7);
648 ctx.qry.has_symref = 1; 671 ctx.qry.has_symref = 1;
649 } 672 }
diff --git a/cgit.h b/cgit.h
index 32d9d2b..4090cd4 100644
--- a/cgit.h
+++ b/cgit.h
@@ -122,131 +122,134 @@ struct refinfo {
122 122
123struct reflist { 123struct reflist {
124 struct refinfo **refs; 124 struct refinfo **refs;
125 int alloc; 125 int alloc;
126 int count; 126 int count;
127}; 127};
128 128
129struct cgit_query { 129struct cgit_query {
130 int has_symref; 130 int has_symref;
131 int has_sha1; 131 int has_sha1;
132 char *raw; 132 char *raw;
133 char *repo; 133 char *repo;
134 char *page; 134 char *page;
135 char *search; 135 char *search;
136 char *grep; 136 char *grep;
137 char *head; 137 char *head;
138 char *sha1; 138 char *sha1;
139 char *sha2; 139 char *sha2;
140 char *path; 140 char *path;
141 char *name; 141 char *name;
142 char *mimetype; 142 char *mimetype;
143 char *url; 143 char *url;
144 char *period; 144 char *period;
145 int ofs; 145 int ofs;
146 int nohead; 146 int nohead;
147 char *sort; 147 char *sort;
148 int showmsg; 148 int showmsg;
149 int ssdiff; 149 int ssdiff;
150 int show_all; 150 int show_all;
151 int context; 151 int context;
152 int ignorews; 152 int ignorews;
153 char *vpath; 153 char *vpath;
154}; 154};
155 155
156struct cgit_config { 156struct cgit_config {
157 char *agefile; 157 char *agefile;
158 char *cache_root; 158 char *cache_root;
159 char *clone_prefix; 159 char *clone_prefix;
160 char *css; 160 char *css;
161 char *favicon; 161 char *favicon;
162 char *footer; 162 char *footer;
163 char *head_include; 163 char *head_include;
164 char *header; 164 char *header;
165 char *index_header; 165 char *index_header;
166 char *index_info; 166 char *index_info;
167 char *logo; 167 char *logo;
168 char *logo_link; 168 char *logo_link;
169 char *module_link; 169 char *module_link;
170 char *project_list;
170 char *robots; 171 char *robots;
171 char *root_title; 172 char *root_title;
172 char *root_desc; 173 char *root_desc;
173 char *root_readme; 174 char *root_readme;
174 char *script_name; 175 char *script_name;
175 char *section; 176 char *section;
176 char *virtual_root; 177 char *virtual_root;
177 int cache_size; 178 int cache_size;
178 int cache_dynamic_ttl; 179 int cache_dynamic_ttl;
179 int cache_max_create_time; 180 int cache_max_create_time;
180 int cache_repo_ttl; 181 int cache_repo_ttl;
181 int cache_root_ttl; 182 int cache_root_ttl;
182 int cache_scanrc_ttl; 183 int cache_scanrc_ttl;
183 int cache_static_ttl; 184 int cache_static_ttl;
184 int embedded; 185 int embedded;
185 int enable_filter_overrides; 186 int enable_filter_overrides;
187 int enable_gitweb_owner;
186 int enable_index_links; 188 int enable_index_links;
187 int enable_log_filecount; 189 int enable_log_filecount;
188 int enable_log_linecount; 190 int enable_log_linecount;
189 int enable_remote_branches; 191 int enable_remote_branches;
190 int enable_subject_links; 192 int enable_subject_links;
191 int enable_tree_linenumbers; 193 int enable_tree_linenumbers;
192 int local_time; 194 int local_time;
193 int max_atom_items; 195 int max_atom_items;
194 int max_repo_count; 196 int max_repo_count;
195 int max_commit_count; 197 int max_commit_count;
196 int max_lock_attempts; 198 int max_lock_attempts;
197 int max_msg_len; 199 int max_msg_len;
198 int max_repodesc_len; 200 int max_repodesc_len;
199 int max_blob_size; 201 int max_blob_size;
200 int max_stats; 202 int max_stats;
201 int nocache; 203 int nocache;
202 int noplainemail; 204 int noplainemail;
203 int noheader; 205 int noheader;
204 int renamelimit; 206 int renamelimit;
207 int remove_suffix;
205 int snapshots; 208 int snapshots;
206 int summary_branches; 209 int summary_branches;
207 int summary_log; 210 int summary_log;
208 int summary_tags; 211 int summary_tags;
209 int ssdiff; 212 int ssdiff;
210 struct string_list mimetypes; 213 struct string_list mimetypes;
211 struct cgit_filter *about_filter; 214 struct cgit_filter *about_filter;
212 struct cgit_filter *commit_filter; 215 struct cgit_filter *commit_filter;
213 struct cgit_filter *source_filter; 216 struct cgit_filter *source_filter;
214}; 217};
215 218
216struct cgit_page { 219struct cgit_page {
217 time_t modified; 220 time_t modified;
218 time_t expires; 221 time_t expires;
219 size_t size; 222 size_t size;
220 char *mimetype; 223 char *mimetype;
221 char *charset; 224 char *charset;
222 char *filename; 225 char *filename;
223 char *etag; 226 char *etag;
224 char *title; 227 char *title;
225 int status; 228 int status;
226 char *statusmsg; 229 char *statusmsg;
227}; 230};
228 231
229struct cgit_environment { 232struct cgit_environment {
230 char *cgit_config; 233 char *cgit_config;
231 char *http_host; 234 char *http_host;
232 char *https; 235 char *https;
233 char *no_http; 236 char *no_http;
234 char *path_info; 237 char *path_info;
235 char *query_string; 238 char *query_string;
236 char *request_method; 239 char *request_method;
237 char *script_name; 240 char *script_name;
238 char *server_name; 241 char *server_name;
239 char *server_port; 242 char *server_port;
240}; 243};
241 244
242struct cgit_context { 245struct cgit_context {
243 struct cgit_environment env; 246 struct cgit_environment env;
244 struct cgit_query qry; 247 struct cgit_query qry;
245 struct cgit_config cfg; 248 struct cgit_config cfg;
246 struct cgit_repo *repo; 249 struct cgit_repo *repo;
247 struct cgit_page page; 250 struct cgit_page page;
248}; 251};
249 252
250struct cgit_snapshot_format { 253struct cgit_snapshot_format {
251 const char *suffix; 254 const char *suffix;
252 const char *mimetype; 255 const char *mimetype;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index a853522..5d77973 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -50,96 +50,101 @@ cache-dynamic-ttl::
50 value: "5". 50 value: "5".
51 51
52cache-repo-ttl:: 52cache-repo-ttl::
53 Number which specifies the time-to-live, in minutes, for the cached 53 Number which specifies the time-to-live, in minutes, for the cached
54 version of the repository summary page. Default value: "5". 54 version of the repository summary page. Default value: "5".
55 55
56cache-root-ttl:: 56cache-root-ttl::
57 Number which specifies the time-to-live, in minutes, for the cached 57 Number which specifies the time-to-live, in minutes, for the cached
58 version of the repository index page. Default value: "5". 58 version of the repository index page. Default value: "5".
59 59
60cache-scanrc-ttl:: 60cache-scanrc-ttl::
61 Number which specifies the time-to-live, in minutes, for the result 61 Number which specifies the time-to-live, in minutes, for the result
62 of scanning a path for git repositories. Default value: "15". 62 of scanning a path for git repositories. Default value: "15".
63 63
64cache-size:: 64cache-size::
65 The maximum number of entries in the cgit cache. Default value: "0" 65 The maximum number of entries in the cgit cache. Default value: "0"
66 (i.e. caching is disabled). 66 (i.e. caching is disabled).
67 67
68cache-static-ttl:: 68cache-static-ttl::
69 Number which specifies the time-to-live, in minutes, for the cached 69 Number which specifies the time-to-live, in minutes, for the cached
70 version of repository pages accessed with a fixed SHA1. Default value: 70 version of repository pages accessed with a fixed SHA1. Default value:
71 "5". 71 "5".
72 72
73clone-prefix:: 73clone-prefix::
74 Space-separated list of common prefixes which, when combined with a 74 Space-separated list of common prefixes which, when combined with a
75 repository url, generates valid clone urls for the repository. This 75 repository url, generates valid clone urls for the repository. This
76 setting is only used if `repo.clone-url` is unspecified. Default value: 76 setting is only used if `repo.clone-url` is unspecified. Default value:
77 none. 77 none.
78 78
79commit-filter:: 79commit-filter::
80 Specifies a command which will be invoked to format commit messages. 80 Specifies a command which will be invoked to format commit messages.
81 The command will get the message on its STDIN, and the STDOUT from the 81 The command will get the message on its STDIN, and the STDOUT from the
82 command will be included verbatim as the commit message, i.e. this can 82 command will be included verbatim as the commit message, i.e. this can
83 be used to implement bugtracker integration. Default value: none. 83 be used to implement bugtracker integration. Default value: none.
84 84
85css:: 85css::
86 Url which specifies the css document to include in all cgit pages. 86 Url which specifies the css document to include in all cgit pages.
87 Default value: "/cgit.css". 87 Default value: "/cgit.css".
88 88
89embedded:: 89embedded::
90 Flag which, when set to "1", will make cgit generate a html fragment 90 Flag which, when set to "1", will make cgit generate a html fragment
91 suitable for embedding in other html pages. Default value: none. See 91 suitable for embedding in other html pages. Default value: none. See
92 also: "noheader". 92 also: "noheader".
93 93
94enable-filter-overrides:: 94enable-filter-overrides::
95 Flag which, when set to "1", allows all filter settings to be 95 Flag which, when set to "1", allows all filter settings to be
96 overridden in repository-specific cgitrc files. Default value: none. 96 overridden in repository-specific cgitrc files. Default value: none.
97 97
98enable-gitweb-owner::
99 If set to "1" and scan-path is enabled, we first check each repository
100 for the git config value "gitweb.owner" to determine the owner.
101 Default value: "1". See also: scan-path.
102
98enable-index-links:: 103enable-index-links::
99 Flag which, when set to "1", will make cgit generate extra links for 104 Flag which, when set to "1", will make cgit generate extra links for
100 each repo in the repository index (specifically, to the "summary", 105 each repo in the repository index (specifically, to the "summary",
101 "commit" and "tree" pages). Default value: "0". 106 "commit" and "tree" pages). Default value: "0".
102 107
103enable-log-filecount:: 108enable-log-filecount::
104 Flag which, when set to "1", will make cgit print the number of 109 Flag which, when set to "1", will make cgit print the number of
105 modified files for each commit on the repository log page. Default 110 modified files for each commit on the repository log page. Default
106 value: "0". 111 value: "0".
107 112
108enable-log-linecount:: 113enable-log-linecount::
109 Flag which, when set to "1", will make cgit print the number of added 114 Flag which, when set to "1", will make cgit print the number of added
110 and removed lines for each commit on the repository log page. Default 115 and removed lines for each commit on the repository log page. Default
111 value: "0". 116 value: "0".
112 117
113enable-remote-branches:: 118enable-remote-branches::
114 Flag which, when set to "1", will make cgit display remote branches 119 Flag which, when set to "1", will make cgit display remote branches
115 in the summary and refs views. Default value: "0". See also: 120 in the summary and refs views. Default value: "0". See also:
116 "repo.enable-remote-branches". 121 "repo.enable-remote-branches".
117 122
118enable-subject-links:: 123enable-subject-links::
119 Flag which, when set to "1", will make cgit use the subject of the 124 Flag which, when set to "1", will make cgit use the subject of the
120 parent commit as link text when generating links to parent commits 125 parent commit as link text when generating links to parent commits
121 in commit view. Default value: "0". See also: 126 in commit view. Default value: "0". See also:
122 "repo.enable-subject-links". 127 "repo.enable-subject-links".
123 128
124enable-tree-linenumbers:: 129enable-tree-linenumbers::
125 Flag which, when set to "1", will make cgit generate linenumber links 130 Flag which, when set to "1", will make cgit generate linenumber links
126 for plaintext blobs printed in the tree view. Default value: "1". 131 for plaintext blobs printed in the tree view. Default value: "1".
127 132
128favicon:: 133favicon::
129 Url used as link to a shortcut icon for cgit. If specified, it is 134 Url used as link to a shortcut icon for cgit. If specified, it is
130 suggested to use the value "/favicon.ico" since certain browsers will 135 suggested to use the value "/favicon.ico" since certain browsers will
131 ignore other values. Default value: none. 136 ignore other values. Default value: none.
132 137
133footer:: 138footer::
134 The content of the file specified with this option will be included 139 The content of the file specified with this option will be included
135 verbatim at the bottom of all pages (i.e. it replaces the standard 140 verbatim at the bottom of all pages (i.e. it replaces the standard
136 "generated by..." message. Default value: none. 141 "generated by..." message. Default value: none.
137 142
138head-include:: 143head-include::
139 The content of the file specified with this option will be included 144 The content of the file specified with this option will be included
140 verbatim in the html HEAD section on all pages. Default value: none. 145 verbatim in the html HEAD section on all pages. Default value: none.
141 146
142header:: 147header::
143 The content of the file specified with this option will be included 148 The content of the file specified with this option will be included
144 verbatim at the top of all pages. Default value: none. 149 verbatim at the top of all pages. Default value: none.
145 150
@@ -179,126 +184,139 @@ max-atom-items::
179max-commit-count:: 184max-commit-count::
180 Specifies the number of entries to list per page in "log" view. Default 185 Specifies the number of entries to list per page in "log" view. Default
181 value: "50". 186 value: "50".
182 187
183max-message-length:: 188max-message-length::
184 Specifies the maximum number of commit message characters to display in 189 Specifies the maximum number of commit message characters to display in
185 "log" view. Default value: "80". 190 "log" view. Default value: "80".
186 191
187max-repo-count:: 192max-repo-count::
188 Specifies the number of entries to list per page on therepository 193 Specifies the number of entries to list per page on therepository
189 index page. Default value: "50". 194 index page. Default value: "50".
190 195
191max-repodesc-length:: 196max-repodesc-length::
192 Specifies the maximum number of repo description characters to display 197 Specifies the maximum number of repo description characters to display
193 on the repository index page. Default value: "80". 198 on the repository index page. Default value: "80".
194 199
195max-blob-size:: 200max-blob-size::
196 Specifies the maximum size of a blob to display HTML for in KBytes. 201 Specifies the maximum size of a blob to display HTML for in KBytes.
197 Default value: "0" (limit disabled). 202 Default value: "0" (limit disabled).
198 203
199max-stats:: 204max-stats::
200 Set the default maximum statistics period. Valid values are "week", 205 Set the default maximum statistics period. Valid values are "week",
201 "month", "quarter" and "year". If unspecified, statistics are 206 "month", "quarter" and "year". If unspecified, statistics are
202 disabled. Default value: none. See also: "repo.max-stats". 207 disabled. Default value: none. See also: "repo.max-stats".
203 208
204mimetype.<ext>:: 209mimetype.<ext>::
205 Set the mimetype for the specified filename extension. This is used 210 Set the mimetype for the specified filename extension. This is used
206 by the `plain` command when returning blob content. 211 by the `plain` command when returning blob content.
207 212
208module-link:: 213module-link::
209 Text which will be used as the formatstring for a hyperlink when a 214 Text which will be used as the formatstring for a hyperlink when a
210 submodule is printed in a directory listing. The arguments for the 215 submodule is printed in a directory listing. The arguments for the
211 formatstring are the path and SHA1 of the submodule commit. Default 216 formatstring are the path and SHA1 of the submodule commit. Default
212 value: "./?repo=%s&page=commit&id=%s" 217 value: "./?repo=%s&page=commit&id=%s"
213 218
214nocache:: 219nocache::
215 If set to the value "1" caching will be disabled. This settings is 220 If set to the value "1" caching will be disabled. This settings is
216 deprecated, and will not be honored starting with cgit-1.0. Default 221 deprecated, and will not be honored starting with cgit-1.0. Default
217 value: "0". 222 value: "0".
218 223
219noplainemail:: 224noplainemail::
220 If set to "1" showing full author email adresses will be disabled. 225 If set to "1" showing full author email adresses will be disabled.
221 Default value: "0". 226 Default value: "0".
222 227
223noheader:: 228noheader::
224 Flag which, when set to "1", will make cgit omit the standard header 229 Flag which, when set to "1", will make cgit omit the standard header
225 on all pages. Default value: none. See also: "embedded". 230 on all pages. Default value: none. See also: "embedded".
226 231
232project-list::
233 A list of subdirectories inside of scan-path, relative to it, that
234 should loaded as git repositories. This must be defined prior to
235 scan-path. Default value: none. See also: scan-path.
236
237remove-suffix::
238 If set to "1" and scan-path is enabled, if any repositories are found
239 with a suffix of ".git", this suffix will be removed for the url and
240 name. Default value: "0". See also: scan-path.
241
227renamelimit:: 242renamelimit::
228 Maximum number of files to consider when detecting renames. The value 243 Maximum number of files to consider when detecting renames. The value
229 "-1" uses the compiletime value in git (for further info, look at 244 "-1" uses the compiletime value in git (for further info, look at
230 `man git-diff`). Default value: "-1". 245 `man git-diff`). Default value: "-1".
231 246
232repo.group:: 247repo.group::
233 Legacy alias for "section". This option is deprecated and will not be 248 Legacy alias for "section". This option is deprecated and will not be
234 supported in cgit-1.0. 249 supported in cgit-1.0.
235 250
236robots:: 251robots::
237 Text used as content for the "robots" meta-tag. Default value: 252 Text used as content for the "robots" meta-tag. Default value:
238 "index, nofollow". 253 "index, nofollow".
239 254
240root-desc:: 255root-desc::
241 Text printed below the heading on the repository index page. Default 256 Text printed below the heading on the repository index page. Default
242 value: "a fast webinterface for the git dscm". 257 value: "a fast webinterface for the git dscm".
243 258
244root-readme:: 259root-readme::
245 The content of the file specified with this option will be included 260 The content of the file specified with this option will be included
246 verbatim below the "about" link on the repository index page. Default 261 verbatim below the "about" link on the repository index page. Default
247 value: none. 262 value: none.
248 263
249root-title:: 264root-title::
250 Text printed as heading on the repository index page. Default value: 265 Text printed as heading on the repository index page. Default value:
251 "Git Repository Browser". 266 "Git Repository Browser".
252 267
253scan-path:: 268scan-path::
254 A path which will be scanned for repositories. If caching is enabled, 269 A path which will be scanned for repositories. If caching is enabled,
255 the result will be cached as a cgitrc include-file in the cache 270 the result will be cached as a cgitrc include-file in the cache
256 directory. Default value: none. See also: cache-scanrc-ttl. 271 directory. If project-list has been defined prior to scan-path,
272 scan-path loads only the directories listed in the file pointed to by
273 project-list. Default value: none. See also: cache-scanrc-ttl,
274 project-list.
257 275
258section:: 276section::
259 The name of the current repository section - all repositories defined 277 The name of the current repository section - all repositories defined
260 after this option will inherit the current section name. Default value: 278 after this option will inherit the current section name. Default value:
261 none. 279 none.
262 280
263side-by-side-diffs:: 281side-by-side-diffs::
264 If set to "1" shows side-by-side diffs instead of unidiffs per 282 If set to "1" shows side-by-side diffs instead of unidiffs per
265 default. Default value: "0". 283 default. Default value: "0".
266 284
267snapshots:: 285snapshots::
268 Text which specifies the default set of snapshot formats generated by 286 Text which specifies the default set of snapshot formats generated by
269 cgit. The value is a space-separated list of zero or more of the 287 cgit. The value is a space-separated list of zero or more of the
270 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 288 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
271 289
272source-filter:: 290source-filter::
273 Specifies a command which will be invoked to format plaintext blobs 291 Specifies a command which will be invoked to format plaintext blobs
274 in the tree view. The command will get the blob content on its STDIN 292 in the tree view. The command will get the blob content on its STDIN
275 and the name of the blob as its only command line argument. The STDOUT 293 and the name of the blob as its only command line argument. The STDOUT
276 from the command will be included verbatim as the blob contents, i.e. 294 from the command will be included verbatim as the blob contents, i.e.
277 this can be used to implement e.g. syntax highlighting. Default value: 295 this can be used to implement e.g. syntax highlighting. Default value:
278 none. 296 none.
279 297
280summary-branches:: 298summary-branches::
281 Specifies the number of branches to display in the repository "summary" 299 Specifies the number of branches to display in the repository "summary"
282 view. Default value: "10". 300 view. Default value: "10".
283 301
284summary-log:: 302summary-log::
285 Specifies the number of log entries to display in the repository 303 Specifies the number of log entries to display in the repository
286 "summary" view. Default value: "10". 304 "summary" view. Default value: "10".
287 305
288summary-tags:: 306summary-tags::
289 Specifies the number of tags to display in the repository "summary" 307 Specifies the number of tags to display in the repository "summary"
290 view. Default value: "10". 308 view. Default value: "10".
291 309
292virtual-root:: 310virtual-root::
293 Url which, if specified, will be used as root for all cgit links. It 311 Url which, if specified, will be used as root for all cgit links. It
294 will also cause cgit to generate 'virtual urls', i.e. urls like 312 will also cause cgit to generate 'virtual urls', i.e. urls like
295 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 313 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
296 value: none. 314 value: none.
297 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 315 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
298 same kind of virtual urls, so this option will probably be deprecated. 316 same kind of virtual urls, so this option will probably be deprecated.
299 317
300REPOSITORY SETTINGS 318REPOSITORY SETTINGS
301------------------- 319-------------------
302repo.about-filter:: 320repo.about-filter::
303 Override the default about-filter. Default value: none. See also: 321 Override the default about-filter. Default value: none. See also:
304 "enable-filter-overrides". 322 "enable-filter-overrides".
@@ -485,48 +503,49 @@ repo.url=baz
485repo.path=/pub/git/baz.git 503repo.path=/pub/git/baz.git
486repo.desc=a set of extensions for bar users 504repo.desc=a set of extensions for bar users
487 505
488repo.url=wiz 506repo.url=wiz
489repo.path=/pub/git/wiz.git 507repo.path=/pub/git/wiz.git
490repo.desc=the wizard of foo 508repo.desc=the wizard of foo
491 509
492 510
493# Add some mirrored repositories 511# Add some mirrored repositories
494section=mirrors 512section=mirrors
495 513
496 514
497repo.url=git 515repo.url=git
498repo.path=/pub/git/git.git 516repo.path=/pub/git/git.git
499repo.desc=the dscm 517repo.desc=the dscm
500 518
501 519
502repo.url=linux 520repo.url=linux
503repo.path=/pub/git/linux.git 521repo.path=/pub/git/linux.git
504repo.desc=the kernel 522repo.desc=the kernel
505 523
506# Disable adhoc downloads of this repo 524# Disable adhoc downloads of this repo
507repo.snapshots=0 525repo.snapshots=0
508 526
509# Disable line-counts for this repo 527# Disable line-counts for this repo
510repo.enable-log-linecount=0 528repo.enable-log-linecount=0
511 529
512# Restrict the max statistics period for this repo 530# Restrict the max statistics period for this repo
513repo.max-stats=month 531repo.max-stats=month
514.... 532....
515 533
516 534
517BUGS 535BUGS
518---- 536----
519Comments currently cannot appear on the same line as a setting; the comment 537Comments currently cannot appear on the same line as a setting; the comment
520will be included as part of the value. E.g. this line: 538will be included as part of the value. E.g. this line:
521 539
522 robots=index # allow indexing 540 robots=index # allow indexing
523 541
524will generate the following html element: 542will generate the following html element:
525 543
526 <meta name='robots' content='index # allow indexing'/> 544 <meta name='robots' content='index # allow indexing'/>
527 545
528 546
529 547
530AUTHOR 548AUTHOR
531------ 549------
532Lars Hjemli <hjemli@gmail.com> 550Lars Hjemli <hjemli@gmail.com>
551Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/scan-tree.c b/scan-tree.c
index 1e18f3c..e987824 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -1,148 +1,203 @@
1/* scan-tree.c
2 *
3 * Copyright (C) 2008-2009 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
5 *
6 * Licensed under GNU General Public License v2
7 * (see COPYING for full license text)
8 */
9
1#include "cgit.h" 10#include "cgit.h"
2#include "configfile.h" 11#include "configfile.h"
3#include "html.h" 12#include "html.h"
4 13
5#define MAX_PATH 4096 14#define MAX_PATH 4096
6 15
7/* return 1 if path contains a objects/ directory and a HEAD file */ 16/* return 1 if path contains a objects/ directory and a HEAD file */
8static int is_git_dir(const char *path) 17static int is_git_dir(const char *path)
9{ 18{
10 struct stat st; 19 struct stat st;
11 static char buf[MAX_PATH]; 20 static char buf[MAX_PATH];
12 21
13 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) { 22 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) {
14 fprintf(stderr, "Insanely long path: %s\n", path); 23 fprintf(stderr, "Insanely long path: %s\n", path);
15 return 0; 24 return 0;
16 } 25 }
17 if (stat(buf, &st)) { 26 if (stat(buf, &st)) {
18 if (errno != ENOENT) 27 if (errno != ENOENT)
19 fprintf(stderr, "Error checking path %s: %s (%d)\n", 28 fprintf(stderr, "Error checking path %s: %s (%d)\n",
20 path, strerror(errno), errno); 29 path, strerror(errno), errno);
21 return 0; 30 return 0;
22 } 31 }
23 if (!S_ISDIR(st.st_mode)) 32 if (!S_ISDIR(st.st_mode))
24 return 0; 33 return 0;
25 34
26 sprintf(buf, "%s/HEAD", path); 35 sprintf(buf, "%s/HEAD", path);
27 if (stat(buf, &st)) { 36 if (stat(buf, &st)) {
28 if (errno != ENOENT) 37 if (errno != ENOENT)
29 fprintf(stderr, "Error checking path %s: %s (%d)\n", 38 fprintf(stderr, "Error checking path %s: %s (%d)\n",
30 path, strerror(errno), errno); 39 path, strerror(errno), errno);
31 return 0; 40 return 0;
32 } 41 }
33 if (!S_ISREG(st.st_mode)) 42 if (!S_ISREG(st.st_mode))
34 return 0; 43 return 0;
35 44
36 return 1; 45 return 1;
37} 46}
38 47
39struct cgit_repo *repo; 48struct cgit_repo *repo;
40repo_config_fn config_fn; 49repo_config_fn config_fn;
50char *owner;
41 51
42static void repo_config(const char *name, const char *value) 52static void repo_config(const char *name, const char *value)
43{ 53{
44 config_fn(repo, name, value); 54 config_fn(repo, name, value);
45} 55}
46 56
57static int git_owner_config(const char *key, const char *value, void *cb)
58{
59 if (!strcmp(key, "gitweb.owner"))
60 owner = xstrdup(value);
61 return 0;
62}
63
47static void add_repo(const char *base, const char *path, repo_config_fn fn) 64static void add_repo(const char *base, const char *path, repo_config_fn fn)
48{ 65{
49 struct stat st; 66 struct stat st;
50 struct passwd *pwd; 67 struct passwd *pwd;
51 char *p; 68 char *p;
52 size_t size; 69 size_t size;
53 70
54 if (stat(path, &st)) { 71 if (stat(path, &st)) {
55 fprintf(stderr, "Error accessing %s: %s (%d)\n", 72 fprintf(stderr, "Error accessing %s: %s (%d)\n",
56 path, strerror(errno), errno); 73 path, strerror(errno), errno);
57 return; 74 return;
58 } 75 }
59 if (!stat(fmt("%s/noweb", path), &st)) 76 if (!stat(fmt("%s/noweb", path), &st))
60 return; 77 return;
61 if ((pwd = getpwuid(st.st_uid)) == NULL) { 78
62 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 79 owner = NULL;
63 path, strerror(errno), errno); 80 if (ctx.cfg.enable_gitweb_owner)
64 return; 81 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
65 }
66 if (base == path) 82 if (base == path)
67 p = fmt("%s", path); 83 p = fmt("%s", path);
68 else 84 else
69 p = fmt("%s", path + strlen(base) + 1); 85 p = fmt("%s", path + strlen(base) + 1);
70 86
71 if (!strcmp(p + strlen(p) - 5, "/.git")) 87 if (!strcmp(p + strlen(p) - 5, "/.git"))
72 p[strlen(p) - 5] = '\0'; 88 p[strlen(p) - 5] = '\0';
73 89
74 repo = cgit_add_repo(xstrdup(p)); 90 repo = cgit_add_repo(xstrdup(p));
91 if (ctx.cfg.remove_suffix)
92 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
93 *p = '\0';
75 repo->name = repo->url; 94 repo->name = repo->url;
76 repo->path = xstrdup(path); 95 repo->path = xstrdup(path);
77 p = (pwd && pwd->pw_gecos) ? strchr(pwd->pw_gecos, ',') : NULL; 96 while (!owner) {
78 if (p) 97 if ((pwd = getpwuid(st.st_uid)) == NULL) {
79 *p = '\0'; 98 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
80 repo->owner = (pwd ? xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name) : ""); 99 path, strerror(errno), errno);
100 break;
101 }
102 if (pwd->pw_gecos)
103 if ((p = strchr(pwd->pw_gecos, ',')))
104 *p = '\0';
105 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
106 }
107 repo->owner = owner;
81 108
82 p = fmt("%s/description", path); 109 p = fmt("%s/description", path);
83 if (!stat(p, &st)) 110 if (!stat(p, &st))
84 readfile(p, &repo->desc, &size); 111 readfile(p, &repo->desc, &size);
85 112
86 p = fmt("%s/README.html", path); 113 p = fmt("%s/README.html", path);
87 if (!stat(p, &st)) 114 if (!stat(p, &st))
88 repo->readme = "README.html"; 115 repo->readme = "README.html";
89 116
90 p = fmt("%s/cgitrc", path); 117 p = fmt("%s/cgitrc", path);
91 if (!stat(p, &st)) { 118 if (!stat(p, &st)) {
92 config_fn = fn; 119 config_fn = fn;
93 parse_configfile(xstrdup(p), &repo_config); 120 parse_configfile(xstrdup(p), &repo_config);
94 } 121 }
95} 122}
96 123
97static void scan_path(const char *base, const char *path, repo_config_fn fn) 124static void scan_path(const char *base, const char *path, repo_config_fn fn)
98{ 125{
99 DIR *dir; 126 DIR *dir;
100 struct dirent *ent; 127 struct dirent *ent;
101 char *buf; 128 char *buf;
102 struct stat st; 129 struct stat st;
103 130
104 if (is_git_dir(path)) { 131 if (is_git_dir(path)) {
105 add_repo(base, path, fn); 132 add_repo(base, path, fn);
106 return; 133 return;
107 } 134 }
108 if (is_git_dir(fmt("%s/.git", path))) { 135 if (is_git_dir(fmt("%s/.git", path))) {
109 add_repo(base, fmt("%s/.git", path), fn); 136 add_repo(base, fmt("%s/.git", path), fn);
110 return; 137 return;
111 } 138 }
112 dir = opendir(path); 139 dir = opendir(path);
113 if (!dir) { 140 if (!dir) {
114 fprintf(stderr, "Error opening directory %s: %s (%d)\n", 141 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
115 path, strerror(errno), errno); 142 path, strerror(errno), errno);
116 return; 143 return;
117 } 144 }
118 while((ent = readdir(dir)) != NULL) { 145 while((ent = readdir(dir)) != NULL) {
119 if (ent->d_name[0] == '.') { 146 if (ent->d_name[0] == '.') {
120 if (ent->d_name[1] == '\0') 147 if (ent->d_name[1] == '\0')
121 continue; 148 continue;
122 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 149 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
123 continue; 150 continue;
124 } 151 }
125 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 152 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
126 if (!buf) { 153 if (!buf) {
127 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 154 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
128 path, strerror(errno), errno); 155 path, strerror(errno), errno);
129 exit(1); 156 exit(1);
130 } 157 }
131 sprintf(buf, "%s/%s", path, ent->d_name); 158 sprintf(buf, "%s/%s", path, ent->d_name);
132 if (stat(buf, &st)) { 159 if (stat(buf, &st)) {
133 fprintf(stderr, "Error checking path %s: %s (%d)\n", 160 fprintf(stderr, "Error checking path %s: %s (%d)\n",
134 buf, strerror(errno), errno); 161 buf, strerror(errno), errno);
135 free(buf); 162 free(buf);
136 continue; 163 continue;
137 } 164 }
138 if (S_ISDIR(st.st_mode)) 165 if (S_ISDIR(st.st_mode))
139 scan_path(base, buf, fn); 166 scan_path(base, buf, fn);
140 free(buf); 167 free(buf);
141 } 168 }
142 closedir(dir); 169 closedir(dir);
143} 170}
144 171
172#define lastc(s) s[strlen(s) - 1]
173
174void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
175{
176 char line[MAX_PATH * 2], *z;
177 FILE *projects;
178 int err;
179
180 projects = fopen(projectsfile, "r");
181 if (!projects) {
182 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
183 projectsfile, strerror(errno), errno);
184 }
185 while (fgets(line, sizeof(line), projects) != NULL) {
186 for (z = &lastc(line);
187 strlen(line) && strchr("\n\r", *z);
188 z = &lastc(line))
189 *z = '\0';
190 if (strlen(line))
191 scan_path(path, fmt("%s/%s", path, line), fn);
192 }
193 if ((err = ferror(projects))) {
194 fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
195 projectsfile, strerror(err), err);
196 }
197 fclose(projects);
198}
199
145void scan_tree(const char *path, repo_config_fn fn) 200void scan_tree(const char *path, repo_config_fn fn)
146{ 201{
147 scan_path(path, path, fn); 202 scan_path(path, path, fn);
148} 203}
diff --git a/scan-tree.h b/scan-tree.h
index 11539f4..1afbd4b 100644
--- a/scan-tree.h
+++ b/scan-tree.h
@@ -1,3 +1,2 @@
1 1extern void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn);
2
3extern void scan_tree(const char *path, repo_config_fn fn); 2extern void scan_tree(const char *path, repo_config_fn fn);