summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2011-02-19 13:25:55 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2011-02-19 13:25:55 (UTC)
commitfb9e6d1594a24fe4e551fd57a9c91fd18b14806e (patch) (unidiff)
treebc0b99adcce3a19f127e6eb2509585ed9486831e
parent31e1f9af1d46bd7dfdb3b2ac580c0d0cc8dbaa63 (diff)
parentdf522794c38934be3229a11e0e2432a1f2a3bc8d (diff)
downloadcgit-fb9e6d1594a24fe4e551fd57a9c91fd18b14806e.zip
cgit-fb9e6d1594a24fe4e551fd57a9c91fd18b14806e.tar.gz
cgit-fb9e6d1594a24fe4e551fd57a9c91fd18b14806e.tar.bz2
Merge branch 'jh/scan-path'
* jh/scan-path: scan_path(): Do not recurse into hidden directories by default scan_path(): Improve handling of inaccessible directories
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c3
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt8
-rw-r--r--scan-tree.c20
4 files changed, 23 insertions, 9 deletions
diff --git a/cgit.c b/cgit.c
index 53ab68d..71f3fc8 100644
--- a/cgit.c
+++ b/cgit.c
@@ -106,312 +106,315 @@ void config_cb(const char *name, const char *value)
106 else if (!strcmp(name, "css")) 106 else if (!strcmp(name, "css"))
107 ctx.cfg.css = xstrdup(value); 107 ctx.cfg.css = xstrdup(value);
108 else if (!strcmp(name, "favicon")) 108 else if (!strcmp(name, "favicon"))
109 ctx.cfg.favicon = xstrdup(value); 109 ctx.cfg.favicon = xstrdup(value);
110 else if (!strcmp(name, "footer")) 110 else if (!strcmp(name, "footer"))
111 ctx.cfg.footer = xstrdup(value); 111 ctx.cfg.footer = xstrdup(value);
112 else if (!strcmp(name, "head-include")) 112 else if (!strcmp(name, "head-include"))
113 ctx.cfg.head_include = xstrdup(value); 113 ctx.cfg.head_include = xstrdup(value);
114 else if (!strcmp(name, "header")) 114 else if (!strcmp(name, "header"))
115 ctx.cfg.header = xstrdup(value); 115 ctx.cfg.header = xstrdup(value);
116 else if (!strcmp(name, "logo")) 116 else if (!strcmp(name, "logo"))
117 ctx.cfg.logo = xstrdup(value); 117 ctx.cfg.logo = xstrdup(value);
118 else if (!strcmp(name, "index-header")) 118 else if (!strcmp(name, "index-header"))
119 ctx.cfg.index_header = xstrdup(value); 119 ctx.cfg.index_header = xstrdup(value);
120 else if (!strcmp(name, "index-info")) 120 else if (!strcmp(name, "index-info"))
121 ctx.cfg.index_info = xstrdup(value); 121 ctx.cfg.index_info = xstrdup(value);
122 else if (!strcmp(name, "logo-link")) 122 else if (!strcmp(name, "logo-link"))
123 ctx.cfg.logo_link = xstrdup(value); 123 ctx.cfg.logo_link = xstrdup(value);
124 else if (!strcmp(name, "module-link")) 124 else if (!strcmp(name, "module-link"))
125 ctx.cfg.module_link = xstrdup(value); 125 ctx.cfg.module_link = xstrdup(value);
126 else if (!strcmp(name, "strict-export")) 126 else if (!strcmp(name, "strict-export"))
127 ctx.cfg.strict_export = xstrdup(value); 127 ctx.cfg.strict_export = xstrdup(value);
128 else if (!strcmp(name, "virtual-root")) { 128 else if (!strcmp(name, "virtual-root")) {
129 ctx.cfg.virtual_root = trim_end(value, '/'); 129 ctx.cfg.virtual_root = trim_end(value, '/');
130 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 130 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
131 ctx.cfg.virtual_root = ""; 131 ctx.cfg.virtual_root = "";
132 } else if (!strcmp(name, "nocache")) 132 } else if (!strcmp(name, "nocache"))
133 ctx.cfg.nocache = atoi(value); 133 ctx.cfg.nocache = atoi(value);
134 else if (!strcmp(name, "noplainemail")) 134 else if (!strcmp(name, "noplainemail"))
135 ctx.cfg.noplainemail = atoi(value); 135 ctx.cfg.noplainemail = atoi(value);
136 else if (!strcmp(name, "noheader")) 136 else if (!strcmp(name, "noheader"))
137 ctx.cfg.noheader = atoi(value); 137 ctx.cfg.noheader = atoi(value);
138 else if (!strcmp(name, "snapshots")) 138 else if (!strcmp(name, "snapshots"))
139 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 139 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
140 else if (!strcmp(name, "enable-filter-overrides")) 140 else if (!strcmp(name, "enable-filter-overrides"))
141 ctx.cfg.enable_filter_overrides = atoi(value); 141 ctx.cfg.enable_filter_overrides = atoi(value);
142 else if (!strcmp(name, "enable-gitweb-owner")) 142 else if (!strcmp(name, "enable-gitweb-owner"))
143 ctx.cfg.enable_gitweb_owner = atoi(value); 143 ctx.cfg.enable_gitweb_owner = atoi(value);
144 else if (!strcmp(name, "enable-index-links")) 144 else if (!strcmp(name, "enable-index-links"))
145 ctx.cfg.enable_index_links = atoi(value); 145 ctx.cfg.enable_index_links = atoi(value);
146 else if (!strcmp(name, "enable-commit-graph")) 146 else if (!strcmp(name, "enable-commit-graph"))
147 ctx.cfg.enable_commit_graph = atoi(value); 147 ctx.cfg.enable_commit_graph = atoi(value);
148 else if (!strcmp(name, "enable-log-filecount")) 148 else if (!strcmp(name, "enable-log-filecount"))
149 ctx.cfg.enable_log_filecount = atoi(value); 149 ctx.cfg.enable_log_filecount = atoi(value);
150 else if (!strcmp(name, "enable-log-linecount")) 150 else if (!strcmp(name, "enable-log-linecount"))
151 ctx.cfg.enable_log_linecount = atoi(value); 151 ctx.cfg.enable_log_linecount = atoi(value);
152 else if (!strcmp(name, "enable-remote-branches")) 152 else if (!strcmp(name, "enable-remote-branches"))
153 ctx.cfg.enable_remote_branches = atoi(value); 153 ctx.cfg.enable_remote_branches = atoi(value);
154 else if (!strcmp(name, "enable-subject-links")) 154 else if (!strcmp(name, "enable-subject-links"))
155 ctx.cfg.enable_subject_links = atoi(value); 155 ctx.cfg.enable_subject_links = atoi(value);
156 else if (!strcmp(name, "enable-tree-linenumbers")) 156 else if (!strcmp(name, "enable-tree-linenumbers"))
157 ctx.cfg.enable_tree_linenumbers = atoi(value); 157 ctx.cfg.enable_tree_linenumbers = atoi(value);
158 else if (!strcmp(name, "max-stats")) 158 else if (!strcmp(name, "max-stats"))
159 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 159 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
160 else if (!strcmp(name, "cache-size")) 160 else if (!strcmp(name, "cache-size"))
161 ctx.cfg.cache_size = atoi(value); 161 ctx.cfg.cache_size = atoi(value);
162 else if (!strcmp(name, "cache-root")) 162 else if (!strcmp(name, "cache-root"))
163 ctx.cfg.cache_root = xstrdup(expand_macros(value)); 163 ctx.cfg.cache_root = xstrdup(expand_macros(value));
164 else if (!strcmp(name, "cache-root-ttl")) 164 else if (!strcmp(name, "cache-root-ttl"))
165 ctx.cfg.cache_root_ttl = atoi(value); 165 ctx.cfg.cache_root_ttl = atoi(value);
166 else if (!strcmp(name, "cache-repo-ttl")) 166 else if (!strcmp(name, "cache-repo-ttl"))
167 ctx.cfg.cache_repo_ttl = atoi(value); 167 ctx.cfg.cache_repo_ttl = atoi(value);
168 else if (!strcmp(name, "cache-scanrc-ttl")) 168 else if (!strcmp(name, "cache-scanrc-ttl"))
169 ctx.cfg.cache_scanrc_ttl = atoi(value); 169 ctx.cfg.cache_scanrc_ttl = atoi(value);
170 else if (!strcmp(name, "cache-static-ttl")) 170 else if (!strcmp(name, "cache-static-ttl"))
171 ctx.cfg.cache_static_ttl = atoi(value); 171 ctx.cfg.cache_static_ttl = atoi(value);
172 else if (!strcmp(name, "cache-dynamic-ttl")) 172 else if (!strcmp(name, "cache-dynamic-ttl"))
173 ctx.cfg.cache_dynamic_ttl = atoi(value); 173 ctx.cfg.cache_dynamic_ttl = atoi(value);
174 else if (!strcmp(name, "about-filter")) 174 else if (!strcmp(name, "about-filter"))
175 ctx.cfg.about_filter = new_filter(value, 0); 175 ctx.cfg.about_filter = new_filter(value, 0);
176 else if (!strcmp(name, "commit-filter")) 176 else if (!strcmp(name, "commit-filter"))
177 ctx.cfg.commit_filter = new_filter(value, 0); 177 ctx.cfg.commit_filter = new_filter(value, 0);
178 else if (!strcmp(name, "embedded")) 178 else if (!strcmp(name, "embedded"))
179 ctx.cfg.embedded = atoi(value); 179 ctx.cfg.embedded = atoi(value);
180 else if (!strcmp(name, "max-atom-items")) 180 else if (!strcmp(name, "max-atom-items"))
181 ctx.cfg.max_atom_items = atoi(value); 181 ctx.cfg.max_atom_items = atoi(value);
182 else if (!strcmp(name, "max-message-length")) 182 else if (!strcmp(name, "max-message-length"))
183 ctx.cfg.max_msg_len = atoi(value); 183 ctx.cfg.max_msg_len = atoi(value);
184 else if (!strcmp(name, "max-repodesc-length")) 184 else if (!strcmp(name, "max-repodesc-length"))
185 ctx.cfg.max_repodesc_len = atoi(value); 185 ctx.cfg.max_repodesc_len = atoi(value);
186 else if (!strcmp(name, "max-blob-size")) 186 else if (!strcmp(name, "max-blob-size"))
187 ctx.cfg.max_blob_size = atoi(value); 187 ctx.cfg.max_blob_size = atoi(value);
188 else if (!strcmp(name, "max-repo-count")) 188 else if (!strcmp(name, "max-repo-count"))
189 ctx.cfg.max_repo_count = atoi(value); 189 ctx.cfg.max_repo_count = atoi(value);
190 else if (!strcmp(name, "max-commit-count")) 190 else if (!strcmp(name, "max-commit-count"))
191 ctx.cfg.max_commit_count = atoi(value); 191 ctx.cfg.max_commit_count = atoi(value);
192 else if (!strcmp(name, "project-list")) 192 else if (!strcmp(name, "project-list"))
193 ctx.cfg.project_list = xstrdup(expand_macros(value)); 193 ctx.cfg.project_list = xstrdup(expand_macros(value));
194 else if (!strcmp(name, "scan-path")) 194 else if (!strcmp(name, "scan-path"))
195 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 195 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
196 process_cached_repolist(expand_macros(value)); 196 process_cached_repolist(expand_macros(value));
197 else if (ctx.cfg.project_list) 197 else if (ctx.cfg.project_list)
198 scan_projects(expand_macros(value), 198 scan_projects(expand_macros(value),
199 ctx.cfg.project_list, repo_config); 199 ctx.cfg.project_list, repo_config);
200 else 200 else
201 scan_tree(expand_macros(value), repo_config); 201 scan_tree(expand_macros(value), repo_config);
202 else if (!strcmp(name, "scan-hidden-path"))
203 ctx.cfg.scan_hidden_path = atoi(value);
202 else if (!strcmp(name, "section-from-path")) 204 else if (!strcmp(name, "section-from-path"))
203 ctx.cfg.section_from_path = atoi(value); 205 ctx.cfg.section_from_path = atoi(value);
204 else if (!strcmp(name, "source-filter")) 206 else if (!strcmp(name, "source-filter"))
205 ctx.cfg.source_filter = new_filter(value, 1); 207 ctx.cfg.source_filter = new_filter(value, 1);
206 else if (!strcmp(name, "summary-log")) 208 else if (!strcmp(name, "summary-log"))
207 ctx.cfg.summary_log = atoi(value); 209 ctx.cfg.summary_log = atoi(value);
208 else if (!strcmp(name, "summary-branches")) 210 else if (!strcmp(name, "summary-branches"))
209 ctx.cfg.summary_branches = atoi(value); 211 ctx.cfg.summary_branches = atoi(value);
210 else if (!strcmp(name, "summary-tags")) 212 else if (!strcmp(name, "summary-tags"))
211 ctx.cfg.summary_tags = atoi(value); 213 ctx.cfg.summary_tags = atoi(value);
212 else if (!strcmp(name, "side-by-side-diffs")) 214 else if (!strcmp(name, "side-by-side-diffs"))
213 ctx.cfg.ssdiff = atoi(value); 215 ctx.cfg.ssdiff = atoi(value);
214 else if (!strcmp(name, "agefile")) 216 else if (!strcmp(name, "agefile"))
215 ctx.cfg.agefile = xstrdup(value); 217 ctx.cfg.agefile = xstrdup(value);
216 else if (!strcmp(name, "renamelimit")) 218 else if (!strcmp(name, "renamelimit"))
217 ctx.cfg.renamelimit = atoi(value); 219 ctx.cfg.renamelimit = atoi(value);
218 else if (!strcmp(name, "remove-suffix")) 220 else if (!strcmp(name, "remove-suffix"))
219 ctx.cfg.remove_suffix = atoi(value); 221 ctx.cfg.remove_suffix = atoi(value);
220 else if (!strcmp(name, "robots")) 222 else if (!strcmp(name, "robots"))
221 ctx.cfg.robots = xstrdup(value); 223 ctx.cfg.robots = xstrdup(value);
222 else if (!strcmp(name, "clone-prefix")) 224 else if (!strcmp(name, "clone-prefix"))
223 ctx.cfg.clone_prefix = xstrdup(value); 225 ctx.cfg.clone_prefix = xstrdup(value);
224 else if (!strcmp(name, "local-time")) 226 else if (!strcmp(name, "local-time"))
225 ctx.cfg.local_time = atoi(value); 227 ctx.cfg.local_time = atoi(value);
226 else if (!prefixcmp(name, "mimetype.")) 228 else if (!prefixcmp(name, "mimetype."))
227 add_mimetype(name + 9, value); 229 add_mimetype(name + 9, value);
228 else if (!strcmp(name, "include")) 230 else if (!strcmp(name, "include"))
229 parse_configfile(expand_macros(value), config_cb); 231 parse_configfile(expand_macros(value), config_cb);
230} 232}
231 233
232static void querystring_cb(const char *name, const char *value) 234static void querystring_cb(const char *name, const char *value)
233{ 235{
234 if (!value) 236 if (!value)
235 value = ""; 237 value = "";
236 238
237 if (!strcmp(name,"r")) { 239 if (!strcmp(name,"r")) {
238 ctx.qry.repo = xstrdup(value); 240 ctx.qry.repo = xstrdup(value);
239 ctx.repo = cgit_get_repoinfo(value); 241 ctx.repo = cgit_get_repoinfo(value);
240 } else if (!strcmp(name, "p")) { 242 } else if (!strcmp(name, "p")) {
241 ctx.qry.page = xstrdup(value); 243 ctx.qry.page = xstrdup(value);
242 } else if (!strcmp(name, "url")) { 244 } else if (!strcmp(name, "url")) {
243 if (*value == '/') 245 if (*value == '/')
244 value++; 246 value++;
245 ctx.qry.url = xstrdup(value); 247 ctx.qry.url = xstrdup(value);
246 cgit_parse_url(value); 248 cgit_parse_url(value);
247 } else if (!strcmp(name, "qt")) { 249 } else if (!strcmp(name, "qt")) {
248 ctx.qry.grep = xstrdup(value); 250 ctx.qry.grep = xstrdup(value);
249 } else if (!strcmp(name, "q")) { 251 } else if (!strcmp(name, "q")) {
250 ctx.qry.search = xstrdup(value); 252 ctx.qry.search = xstrdup(value);
251 } else if (!strcmp(name, "h")) { 253 } else if (!strcmp(name, "h")) {
252 ctx.qry.head = xstrdup(value); 254 ctx.qry.head = xstrdup(value);
253 ctx.qry.has_symref = 1; 255 ctx.qry.has_symref = 1;
254 } else if (!strcmp(name, "id")) { 256 } else if (!strcmp(name, "id")) {
255 ctx.qry.sha1 = xstrdup(value); 257 ctx.qry.sha1 = xstrdup(value);
256 ctx.qry.has_sha1 = 1; 258 ctx.qry.has_sha1 = 1;
257 } else if (!strcmp(name, "id2")) { 259 } else if (!strcmp(name, "id2")) {
258 ctx.qry.sha2 = xstrdup(value); 260 ctx.qry.sha2 = xstrdup(value);
259 ctx.qry.has_sha1 = 1; 261 ctx.qry.has_sha1 = 1;
260 } else if (!strcmp(name, "ofs")) { 262 } else if (!strcmp(name, "ofs")) {
261 ctx.qry.ofs = atoi(value); 263 ctx.qry.ofs = atoi(value);
262 } else if (!strcmp(name, "path")) { 264 } else if (!strcmp(name, "path")) {
263 ctx.qry.path = trim_end(value, '/'); 265 ctx.qry.path = trim_end(value, '/');
264 } else if (!strcmp(name, "name")) { 266 } else if (!strcmp(name, "name")) {
265 ctx.qry.name = xstrdup(value); 267 ctx.qry.name = xstrdup(value);
266 } else if (!strcmp(name, "mimetype")) { 268 } else if (!strcmp(name, "mimetype")) {
267 ctx.qry.mimetype = xstrdup(value); 269 ctx.qry.mimetype = xstrdup(value);
268 } else if (!strcmp(name, "s")){ 270 } else if (!strcmp(name, "s")){
269 ctx.qry.sort = xstrdup(value); 271 ctx.qry.sort = xstrdup(value);
270 } else if (!strcmp(name, "showmsg")) { 272 } else if (!strcmp(name, "showmsg")) {
271 ctx.qry.showmsg = atoi(value); 273 ctx.qry.showmsg = atoi(value);
272 } else if (!strcmp(name, "period")) { 274 } else if (!strcmp(name, "period")) {
273 ctx.qry.period = xstrdup(value); 275 ctx.qry.period = xstrdup(value);
274 } else if (!strcmp(name, "ss")) { 276 } else if (!strcmp(name, "ss")) {
275 ctx.qry.ssdiff = atoi(value); 277 ctx.qry.ssdiff = atoi(value);
276 } else if (!strcmp(name, "all")) { 278 } else if (!strcmp(name, "all")) {
277 ctx.qry.show_all = atoi(value); 279 ctx.qry.show_all = atoi(value);
278 } else if (!strcmp(name, "context")) { 280 } else if (!strcmp(name, "context")) {
279 ctx.qry.context = atoi(value); 281 ctx.qry.context = atoi(value);
280 } else if (!strcmp(name, "ignorews")) { 282 } else if (!strcmp(name, "ignorews")) {
281 ctx.qry.ignorews = atoi(value); 283 ctx.qry.ignorews = atoi(value);
282 } 284 }
283} 285}
284 286
285char *xstrdupn(const char *str) 287char *xstrdupn(const char *str)
286{ 288{
287 return (str ? xstrdup(str) : NULL); 289 return (str ? xstrdup(str) : NULL);
288} 290}
289 291
290static void prepare_context(struct cgit_context *ctx) 292static void prepare_context(struct cgit_context *ctx)
291{ 293{
292 memset(ctx, 0, sizeof(*ctx)); 294 memset(ctx, 0, sizeof(*ctx));
293 ctx->cfg.agefile = "info/web/last-modified"; 295 ctx->cfg.agefile = "info/web/last-modified";
294 ctx->cfg.nocache = 0; 296 ctx->cfg.nocache = 0;
295 ctx->cfg.cache_size = 0; 297 ctx->cfg.cache_size = 0;
296 ctx->cfg.cache_dynamic_ttl = 5; 298 ctx->cfg.cache_dynamic_ttl = 5;
297 ctx->cfg.cache_max_create_time = 5; 299 ctx->cfg.cache_max_create_time = 5;
298 ctx->cfg.cache_repo_ttl = 5; 300 ctx->cfg.cache_repo_ttl = 5;
299 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 301 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
300 ctx->cfg.cache_root_ttl = 5; 302 ctx->cfg.cache_root_ttl = 5;
301 ctx->cfg.cache_scanrc_ttl = 15; 303 ctx->cfg.cache_scanrc_ttl = 15;
302 ctx->cfg.cache_static_ttl = -1; 304 ctx->cfg.cache_static_ttl = -1;
303 ctx->cfg.css = "/cgit.css"; 305 ctx->cfg.css = "/cgit.css";
304 ctx->cfg.logo = "/cgit.png"; 306 ctx->cfg.logo = "/cgit.png";
305 ctx->cfg.local_time = 0; 307 ctx->cfg.local_time = 0;
306 ctx->cfg.enable_gitweb_owner = 1; 308 ctx->cfg.enable_gitweb_owner = 1;
307 ctx->cfg.enable_tree_linenumbers = 1; 309 ctx->cfg.enable_tree_linenumbers = 1;
308 ctx->cfg.max_repo_count = 50; 310 ctx->cfg.max_repo_count = 50;
309 ctx->cfg.max_commit_count = 50; 311 ctx->cfg.max_commit_count = 50;
310 ctx->cfg.max_lock_attempts = 5; 312 ctx->cfg.max_lock_attempts = 5;
311 ctx->cfg.max_msg_len = 80; 313 ctx->cfg.max_msg_len = 80;
312 ctx->cfg.max_repodesc_len = 80; 314 ctx->cfg.max_repodesc_len = 80;
313 ctx->cfg.max_blob_size = 0; 315 ctx->cfg.max_blob_size = 0;
314 ctx->cfg.max_stats = 0; 316 ctx->cfg.max_stats = 0;
315 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 317 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
316 ctx->cfg.project_list = NULL; 318 ctx->cfg.project_list = NULL;
317 ctx->cfg.renamelimit = -1; 319 ctx->cfg.renamelimit = -1;
318 ctx->cfg.remove_suffix = 0; 320 ctx->cfg.remove_suffix = 0;
319 ctx->cfg.robots = "index, nofollow"; 321 ctx->cfg.robots = "index, nofollow";
320 ctx->cfg.root_title = "Git repository browser"; 322 ctx->cfg.root_title = "Git repository browser";
321 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 323 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
324 ctx->cfg.scan_hidden_path = 0;
322 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 325 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
323 ctx->cfg.section = ""; 326 ctx->cfg.section = "";
324 ctx->cfg.summary_branches = 10; 327 ctx->cfg.summary_branches = 10;
325 ctx->cfg.summary_log = 10; 328 ctx->cfg.summary_log = 10;
326 ctx->cfg.summary_tags = 10; 329 ctx->cfg.summary_tags = 10;
327 ctx->cfg.max_atom_items = 10; 330 ctx->cfg.max_atom_items = 10;
328 ctx->cfg.ssdiff = 0; 331 ctx->cfg.ssdiff = 0;
329 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 332 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
330 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 333 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
331 ctx->env.https = xstrdupn(getenv("HTTPS")); 334 ctx->env.https = xstrdupn(getenv("HTTPS"));
332 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 335 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
333 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 336 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
334 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 337 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
335 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 338 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
336 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 339 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
337 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 340 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
338 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 341 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
339 ctx->page.mimetype = "text/html"; 342 ctx->page.mimetype = "text/html";
340 ctx->page.charset = PAGE_ENCODING; 343 ctx->page.charset = PAGE_ENCODING;
341 ctx->page.filename = NULL; 344 ctx->page.filename = NULL;
342 ctx->page.size = 0; 345 ctx->page.size = 0;
343 ctx->page.modified = time(NULL); 346 ctx->page.modified = time(NULL);
344 ctx->page.expires = ctx->page.modified; 347 ctx->page.expires = ctx->page.modified;
345 ctx->page.etag = NULL; 348 ctx->page.etag = NULL;
346 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 349 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
347 if (ctx->env.script_name) 350 if (ctx->env.script_name)
348 ctx->cfg.script_name = ctx->env.script_name; 351 ctx->cfg.script_name = ctx->env.script_name;
349 if (ctx->env.query_string) 352 if (ctx->env.query_string)
350 ctx->qry.raw = ctx->env.query_string; 353 ctx->qry.raw = ctx->env.query_string;
351 if (!ctx->env.cgit_config) 354 if (!ctx->env.cgit_config)
352 ctx->env.cgit_config = CGIT_CONFIG; 355 ctx->env.cgit_config = CGIT_CONFIG;
353} 356}
354 357
355struct refmatch { 358struct refmatch {
356 char *req_ref; 359 char *req_ref;
357 char *first_ref; 360 char *first_ref;
358 int match; 361 int match;
359}; 362};
360 363
361int find_current_ref(const char *refname, const unsigned char *sha1, 364int find_current_ref(const char *refname, const unsigned char *sha1,
362 int flags, void *cb_data) 365 int flags, void *cb_data)
363{ 366{
364 struct refmatch *info; 367 struct refmatch *info;
365 368
366 info = (struct refmatch *)cb_data; 369 info = (struct refmatch *)cb_data;
367 if (!strcmp(refname, info->req_ref)) 370 if (!strcmp(refname, info->req_ref))
368 info->match = 1; 371 info->match = 1;
369 if (!info->first_ref) 372 if (!info->first_ref)
370 info->first_ref = xstrdup(refname); 373 info->first_ref = xstrdup(refname);
371 return info->match; 374 return info->match;
372} 375}
373 376
374char *find_default_branch(struct cgit_repo *repo) 377char *find_default_branch(struct cgit_repo *repo)
375{ 378{
376 struct refmatch info; 379 struct refmatch info;
377 char *ref; 380 char *ref;
378 381
379 info.req_ref = repo->defbranch; 382 info.req_ref = repo->defbranch;
380 info.first_ref = NULL; 383 info.first_ref = NULL;
381 info.match = 0; 384 info.match = 0;
382 for_each_branch_ref(find_current_ref, &info); 385 for_each_branch_ref(find_current_ref, &info);
383 if (info.match) 386 if (info.match)
384 ref = info.req_ref; 387 ref = info.req_ref;
385 else 388 else
386 ref = info.first_ref; 389 ref = info.first_ref;
387 if (ref) 390 if (ref)
388 ref = xstrdup(ref); 391 ref = xstrdup(ref);
389 return ref; 392 return ref;
390} 393}
391 394
392static int prepare_repo_cmd(struct cgit_context *ctx) 395static int prepare_repo_cmd(struct cgit_context *ctx)
393{ 396{
394 char *tmp; 397 char *tmp;
395 unsigned char sha1[20]; 398 unsigned char sha1[20];
396 int nongit = 0; 399 int nongit = 0;
397 400
398 setenv("GIT_DIR", ctx->repo->path, 1); 401 setenv("GIT_DIR", ctx->repo->path, 1);
399 setup_git_directory_gently(&nongit); 402 setup_git_directory_gently(&nongit);
400 if (nongit) { 403 if (nongit) {
401 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 404 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
402 "config error"); 405 "config error");
403 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 406 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
404 ctx->repo = NULL; 407 ctx->repo = NULL;
405 cgit_print_http_headers(ctx); 408 cgit_print_http_headers(ctx);
406 cgit_print_docstart(ctx); 409 cgit_print_docstart(ctx);
407 cgit_print_pageheader(ctx); 410 cgit_print_pageheader(ctx);
408 cgit_print_error(tmp); 411 cgit_print_error(tmp);
409 cgit_print_docend(); 412 cgit_print_docend();
410 return 1; 413 return 1;
411 } 414 }
412 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 415 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
413 416
414 if (!ctx->qry.head) { 417 if (!ctx->qry.head) {
415 ctx->qry.nohead = 1; 418 ctx->qry.nohead = 1;
416 ctx->qry.head = find_default_branch(ctx->repo); 419 ctx->qry.head = find_default_branch(ctx->repo);
417 ctx->repo->defbranch = ctx->qry.head; 420 ctx->repo->defbranch = ctx->qry.head;
diff --git a/cgit.h b/cgit.h
index bed770b..74aa340 100644
--- a/cgit.h
+++ b/cgit.h
@@ -117,192 +117,193 @@ struct refinfo {
117 const char *refname; 117 const char *refname;
118 struct object *object; 118 struct object *object;
119 union { 119 union {
120 struct taginfo *tag; 120 struct taginfo *tag;
121 struct commitinfo *commit; 121 struct commitinfo *commit;
122 }; 122 };
123}; 123};
124 124
125struct reflist { 125struct reflist {
126 struct refinfo **refs; 126 struct refinfo **refs;
127 int alloc; 127 int alloc;
128 int count; 128 int count;
129}; 129};
130 130
131struct cgit_query { 131struct cgit_query {
132 int has_symref; 132 int has_symref;
133 int has_sha1; 133 int has_sha1;
134 char *raw; 134 char *raw;
135 char *repo; 135 char *repo;
136 char *page; 136 char *page;
137 char *search; 137 char *search;
138 char *grep; 138 char *grep;
139 char *head; 139 char *head;
140 char *sha1; 140 char *sha1;
141 char *sha2; 141 char *sha2;
142 char *path; 142 char *path;
143 char *name; 143 char *name;
144 char *mimetype; 144 char *mimetype;
145 char *url; 145 char *url;
146 char *period; 146 char *period;
147 int ofs; 147 int ofs;
148 int nohead; 148 int nohead;
149 char *sort; 149 char *sort;
150 int showmsg; 150 int showmsg;
151 int ssdiff; 151 int ssdiff;
152 int show_all; 152 int show_all;
153 int context; 153 int context;
154 int ignorews; 154 int ignorews;
155 char *vpath; 155 char *vpath;
156}; 156};
157 157
158struct cgit_config { 158struct cgit_config {
159 char *agefile; 159 char *agefile;
160 char *cache_root; 160 char *cache_root;
161 char *clone_prefix; 161 char *clone_prefix;
162 char *css; 162 char *css;
163 char *favicon; 163 char *favicon;
164 char *footer; 164 char *footer;
165 char *head_include; 165 char *head_include;
166 char *header; 166 char *header;
167 char *index_header; 167 char *index_header;
168 char *index_info; 168 char *index_info;
169 char *logo; 169 char *logo;
170 char *logo_link; 170 char *logo_link;
171 char *module_link; 171 char *module_link;
172 char *project_list; 172 char *project_list;
173 char *readme; 173 char *readme;
174 char *robots; 174 char *robots;
175 char *root_title; 175 char *root_title;
176 char *root_desc; 176 char *root_desc;
177 char *root_readme; 177 char *root_readme;
178 char *script_name; 178 char *script_name;
179 char *section; 179 char *section;
180 char *virtual_root; 180 char *virtual_root;
181 char *strict_export; 181 char *strict_export;
182 int cache_size; 182 int cache_size;
183 int cache_dynamic_ttl; 183 int cache_dynamic_ttl;
184 int cache_max_create_time; 184 int cache_max_create_time;
185 int cache_repo_ttl; 185 int cache_repo_ttl;
186 int cache_root_ttl; 186 int cache_root_ttl;
187 int cache_scanrc_ttl; 187 int cache_scanrc_ttl;
188 int cache_static_ttl; 188 int cache_static_ttl;
189 int embedded; 189 int embedded;
190 int enable_filter_overrides; 190 int enable_filter_overrides;
191 int enable_gitweb_owner; 191 int enable_gitweb_owner;
192 int enable_index_links; 192 int enable_index_links;
193 int enable_commit_graph; 193 int enable_commit_graph;
194 int enable_log_filecount; 194 int enable_log_filecount;
195 int enable_log_linecount; 195 int enable_log_linecount;
196 int enable_remote_branches; 196 int enable_remote_branches;
197 int enable_subject_links; 197 int enable_subject_links;
198 int enable_tree_linenumbers; 198 int enable_tree_linenumbers;
199 int local_time; 199 int local_time;
200 int max_atom_items; 200 int max_atom_items;
201 int max_repo_count; 201 int max_repo_count;
202 int max_commit_count; 202 int max_commit_count;
203 int max_lock_attempts; 203 int max_lock_attempts;
204 int max_msg_len; 204 int max_msg_len;
205 int max_repodesc_len; 205 int max_repodesc_len;
206 int max_blob_size; 206 int max_blob_size;
207 int max_stats; 207 int max_stats;
208 int nocache; 208 int nocache;
209 int noplainemail; 209 int noplainemail;
210 int noheader; 210 int noheader;
211 int renamelimit; 211 int renamelimit;
212 int remove_suffix; 212 int remove_suffix;
213 int scan_hidden_path;
213 int section_from_path; 214 int section_from_path;
214 int snapshots; 215 int snapshots;
215 int summary_branches; 216 int summary_branches;
216 int summary_log; 217 int summary_log;
217 int summary_tags; 218 int summary_tags;
218 int ssdiff; 219 int ssdiff;
219 struct string_list mimetypes; 220 struct string_list mimetypes;
220 struct cgit_filter *about_filter; 221 struct cgit_filter *about_filter;
221 struct cgit_filter *commit_filter; 222 struct cgit_filter *commit_filter;
222 struct cgit_filter *source_filter; 223 struct cgit_filter *source_filter;
223}; 224};
224 225
225struct cgit_page { 226struct cgit_page {
226 time_t modified; 227 time_t modified;
227 time_t expires; 228 time_t expires;
228 size_t size; 229 size_t size;
229 char *mimetype; 230 char *mimetype;
230 char *charset; 231 char *charset;
231 char *filename; 232 char *filename;
232 char *etag; 233 char *etag;
233 char *title; 234 char *title;
234 int status; 235 int status;
235 char *statusmsg; 236 char *statusmsg;
236}; 237};
237 238
238struct cgit_environment { 239struct cgit_environment {
239 char *cgit_config; 240 char *cgit_config;
240 char *http_host; 241 char *http_host;
241 char *https; 242 char *https;
242 char *no_http; 243 char *no_http;
243 char *path_info; 244 char *path_info;
244 char *query_string; 245 char *query_string;
245 char *request_method; 246 char *request_method;
246 char *script_name; 247 char *script_name;
247 char *server_name; 248 char *server_name;
248 char *server_port; 249 char *server_port;
249}; 250};
250 251
251struct cgit_context { 252struct cgit_context {
252 struct cgit_environment env; 253 struct cgit_environment env;
253 struct cgit_query qry; 254 struct cgit_query qry;
254 struct cgit_config cfg; 255 struct cgit_config cfg;
255 struct cgit_repo *repo; 256 struct cgit_repo *repo;
256 struct cgit_page page; 257 struct cgit_page page;
257}; 258};
258 259
259struct cgit_snapshot_format { 260struct cgit_snapshot_format {
260 const char *suffix; 261 const char *suffix;
261 const char *mimetype; 262 const char *mimetype;
262 write_archive_fn_t write_func; 263 write_archive_fn_t write_func;
263 int bit; 264 int bit;
264}; 265};
265 266
266extern const char *cgit_version; 267extern const char *cgit_version;
267 268
268extern struct cgit_repolist cgit_repolist; 269extern struct cgit_repolist cgit_repolist;
269extern struct cgit_context ctx; 270extern struct cgit_context ctx;
270extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 271extern const struct cgit_snapshot_format cgit_snapshot_formats[];
271 272
272extern struct cgit_repo *cgit_add_repo(const char *url); 273extern struct cgit_repo *cgit_add_repo(const char *url);
273extern struct cgit_repo *cgit_get_repoinfo(const char *url); 274extern struct cgit_repo *cgit_get_repoinfo(const char *url);
274extern void cgit_repo_config_cb(const char *name, const char *value); 275extern void cgit_repo_config_cb(const char *name, const char *value);
275 276
276extern int chk_zero(int result, char *msg); 277extern int chk_zero(int result, char *msg);
277extern int chk_positive(int result, char *msg); 278extern int chk_positive(int result, char *msg);
278extern int chk_non_negative(int result, char *msg); 279extern int chk_non_negative(int result, char *msg);
279 280
280extern char *trim_end(const char *str, char c); 281extern char *trim_end(const char *str, char c);
281extern char *strlpart(char *txt, int maxlen); 282extern char *strlpart(char *txt, int maxlen);
282extern char *strrpart(char *txt, int maxlen); 283extern char *strrpart(char *txt, int maxlen);
283 284
284extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 285extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
285extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 286extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
286 int flags, void *cb_data); 287 int flags, void *cb_data);
287 288
288extern void *cgit_free_commitinfo(struct commitinfo *info); 289extern void *cgit_free_commitinfo(struct commitinfo *info);
289 290
290extern int cgit_diff_files(const unsigned char *old_sha1, 291extern int cgit_diff_files(const unsigned char *old_sha1,
291 const unsigned char *new_sha1, 292 const unsigned char *new_sha1,
292 unsigned long *old_size, unsigned long *new_size, 293 unsigned long *old_size, unsigned long *new_size,
293 int *binary, int context, int ignorews, 294 int *binary, int context, int ignorews,
294 linediff_fn fn); 295 linediff_fn fn);
295 296
296extern void cgit_diff_tree(const unsigned char *old_sha1, 297extern void cgit_diff_tree(const unsigned char *old_sha1,
297 const unsigned char *new_sha1, 298 const unsigned char *new_sha1,
298 filepair_fn fn, const char *prefix, int ignorews); 299 filepair_fn fn, const char *prefix, int ignorews);
299 300
300extern void cgit_diff_commit(struct commit *commit, filepair_fn fn, 301extern void cgit_diff_commit(struct commit *commit, filepair_fn fn,
301 const char *prefix); 302 const char *prefix);
302 303
303__attribute__((format (printf,1,2))) 304__attribute__((format (printf,1,2)))
304extern char *fmt(const char *format,...); 305extern char *fmt(const char *format,...);
305 306
306extern struct commitinfo *cgit_parse_commit(struct commit *commit); 307extern struct commitinfo *cgit_parse_commit(struct commit *commit);
307extern struct taginfo *cgit_parse_tag(struct tag *tag); 308extern struct taginfo *cgit_parse_tag(struct tag *tag);
308extern void cgit_parse_url(const char *url); 309extern void cgit_parse_url(const char *url);
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 3c20fe1..a832830 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -181,192 +181,200 @@ logo-link::
181 Url loaded when clicking on the cgit logo image. If unspecified the 181 Url loaded when clicking on the cgit logo image. If unspecified the
182 calculated url of the repository index page will be used. Default 182 calculated url of the repository index page will be used. Default
183 value: none. 183 value: none.
184 184
185max-atom-items:: 185max-atom-items::
186 Specifies the number of items to display in atom feeds view. Default 186 Specifies the number of items to display in atom feeds view. Default
187 value: "10". 187 value: "10".
188 188
189max-commit-count:: 189max-commit-count::
190 Specifies the number of entries to list per page in "log" view. Default 190 Specifies the number of entries to list per page in "log" view. Default
191 value: "50". 191 value: "50".
192 192
193max-message-length:: 193max-message-length::
194 Specifies the maximum number of commit message characters to display in 194 Specifies the maximum number of commit message characters to display in
195 "log" view. Default value: "80". 195 "log" view. Default value: "80".
196 196
197max-repo-count:: 197max-repo-count::
198 Specifies the number of entries to list per page on therepository 198 Specifies the number of entries to list per page on therepository
199 index page. Default value: "50". 199 index page. Default value: "50".
200 200
201max-repodesc-length:: 201max-repodesc-length::
202 Specifies the maximum number of repo description characters to display 202 Specifies the maximum number of repo description characters to display
203 on the repository index page. Default value: "80". 203 on the repository index page. Default value: "80".
204 204
205max-blob-size:: 205max-blob-size::
206 Specifies the maximum size of a blob to display HTML for in KBytes. 206 Specifies the maximum size of a blob to display HTML for in KBytes.
207 Default value: "0" (limit disabled). 207 Default value: "0" (limit disabled).
208 208
209max-stats:: 209max-stats::
210 Set the default maximum statistics period. Valid values are "week", 210 Set the default maximum statistics period. Valid values are "week",
211 "month", "quarter" and "year". If unspecified, statistics are 211 "month", "quarter" and "year". If unspecified, statistics are
212 disabled. Default value: none. See also: "repo.max-stats". 212 disabled. Default value: none. See also: "repo.max-stats".
213 213
214mimetype.<ext>:: 214mimetype.<ext>::
215 Set the mimetype for the specified filename extension. This is used 215 Set the mimetype for the specified filename extension. This is used
216 by the `plain` command when returning blob content. 216 by the `plain` command when returning blob content.
217 217
218module-link:: 218module-link::
219 Text which will be used as the formatstring for a hyperlink when a 219 Text which will be used as the formatstring for a hyperlink when a
220 submodule is printed in a directory listing. The arguments for the 220 submodule is printed in a directory listing. The arguments for the
221 formatstring are the path and SHA1 of the submodule commit. Default 221 formatstring are the path and SHA1 of the submodule commit. Default
222 value: "./?repo=%s&page=commit&id=%s" 222 value: "./?repo=%s&page=commit&id=%s"
223 223
224nocache:: 224nocache::
225 If set to the value "1" caching will be disabled. This settings is 225 If set to the value "1" caching will be disabled. This settings is
226 deprecated, and will not be honored starting with cgit-1.0. Default 226 deprecated, and will not be honored starting with cgit-1.0. Default
227 value: "0". 227 value: "0".
228 228
229noplainemail:: 229noplainemail::
230 If set to "1" showing full author email adresses will be disabled. 230 If set to "1" showing full author email adresses will be disabled.
231 Default value: "0". 231 Default value: "0".
232 232
233noheader:: 233noheader::
234 Flag which, when set to "1", will make cgit omit the standard header 234 Flag which, when set to "1", will make cgit omit the standard header
235 on all pages. Default value: none. See also: "embedded". 235 on all pages. Default value: none. See also: "embedded".
236 236
237project-list:: 237project-list::
238 A list of subdirectories inside of scan-path, relative to it, that 238 A list of subdirectories inside of scan-path, relative to it, that
239 should loaded as git repositories. This must be defined prior to 239 should loaded as git repositories. This must be defined prior to
240 scan-path. Default value: none. See also: scan-path. 240 scan-path. Default value: none. See also: scan-path.
241 241
242readme:: 242readme::
243 Text which will be used as default value for "repo.readme". Default 243 Text which will be used as default value for "repo.readme". Default
244 value: none. 244 value: none.
245 245
246remove-suffix:: 246remove-suffix::
247 If set to "1" and scan-path is enabled, if any repositories are found 247 If set to "1" and scan-path is enabled, if any repositories are found
248 with a suffix of ".git", this suffix will be removed for the url and 248 with a suffix of ".git", this suffix will be removed for the url and
249 name. Default value: "0". See also: scan-path. 249 name. Default value: "0". See also: scan-path.
250 250
251renamelimit:: 251renamelimit::
252 Maximum number of files to consider when detecting renames. The value 252 Maximum number of files to consider when detecting renames. The value
253 "-1" uses the compiletime value in git (for further info, look at 253 "-1" uses the compiletime value in git (for further info, look at
254 `man git-diff`). Default value: "-1". 254 `man git-diff`). Default value: "-1".
255 255
256repo.group:: 256repo.group::
257 Legacy alias for "section". This option is deprecated and will not be 257 Legacy alias for "section". This option is deprecated and will not be
258 supported in cgit-1.0. 258 supported in cgit-1.0.
259 259
260robots:: 260robots::
261 Text used as content for the "robots" meta-tag. Default value: 261 Text used as content for the "robots" meta-tag. Default value:
262 "index, nofollow". 262 "index, nofollow".
263 263
264root-desc:: 264root-desc::
265 Text printed below the heading on the repository index page. Default 265 Text printed below the heading on the repository index page. Default
266 value: "a fast webinterface for the git dscm". 266 value: "a fast webinterface for the git dscm".
267 267
268root-readme:: 268root-readme::
269 The content of the file specified with this option will be included 269 The content of the file specified with this option will be included
270 verbatim below the "about" link on the repository index page. Default 270 verbatim below the "about" link on the repository index page. Default
271 value: none. 271 value: none.
272 272
273root-title:: 273root-title::
274 Text printed as heading on the repository index page. Default value: 274 Text printed as heading on the repository index page. Default value:
275 "Git Repository Browser". 275 "Git Repository Browser".
276 276
277scan-hidden-path::
278 If set to "1" and scan-path is enabled, scan-path will recurse into
279 directories whose name starts with a period ('.'). Otherwise,
280 scan-path will stay away from such directories (considered as
281 "hidden"). Note that this does not apply to the ".git" directory in
282 non-bare repos. This must be defined prior to scan-path.
283 Default value: 0. See also: scan-path.
284
277scan-path:: 285scan-path::
278 A path which will be scanned for repositories. If caching is enabled, 286 A path which will be scanned for repositories. If caching is enabled,
279 the result will be cached as a cgitrc include-file in the cache 287 the result will be cached as a cgitrc include-file in the cache
280 directory. If project-list has been defined prior to scan-path, 288 directory. If project-list has been defined prior to scan-path,
281 scan-path loads only the directories listed in the file pointed to by 289 scan-path loads only the directories listed in the file pointed to by
282 project-list. Default value: none. See also: cache-scanrc-ttl, 290 project-list. Default value: none. See also: cache-scanrc-ttl,
283 project-list. 291 project-list.
284 292
285section:: 293section::
286 The name of the current repository section - all repositories defined 294 The name of the current repository section - all repositories defined
287 after this option will inherit the current section name. Default value: 295 after this option will inherit the current section name. Default value:
288 none. 296 none.
289 297
290section-from-path:: 298section-from-path::
291 A number which, if specified before scan-path, specifies how many 299 A number which, if specified before scan-path, specifies how many
292 path elements from each repo path to use as a default section name. 300 path elements from each repo path to use as a default section name.
293 If negative, cgit will discard the specified number of path elements 301 If negative, cgit will discard the specified number of path elements
294 above the repo directory. Default value: 0. 302 above the repo directory. Default value: 0.
295 303
296side-by-side-diffs:: 304side-by-side-diffs::
297 If set to "1" shows side-by-side diffs instead of unidiffs per 305 If set to "1" shows side-by-side diffs instead of unidiffs per
298 default. Default value: "0". 306 default. Default value: "0".
299 307
300snapshots:: 308snapshots::
301 Text which specifies the default set of snapshot formats generated by 309 Text which specifies the default set of snapshot formats generated by
302 cgit. The value is a space-separated list of zero or more of the 310 cgit. The value is a space-separated list of zero or more of the
303 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 311 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
304 312
305source-filter:: 313source-filter::
306 Specifies a command which will be invoked to format plaintext blobs 314 Specifies a command which will be invoked to format plaintext blobs
307 in the tree view. The command will get the blob content on its STDIN 315 in the tree view. The command will get the blob content on its STDIN
308 and the name of the blob as its only command line argument. The STDOUT 316 and the name of the blob as its only command line argument. The STDOUT
309 from the command will be included verbatim as the blob contents, i.e. 317 from the command will be included verbatim as the blob contents, i.e.
310 this can be used to implement e.g. syntax highlighting. Default value: 318 this can be used to implement e.g. syntax highlighting. Default value:
311 none. 319 none.
312 320
313summary-branches:: 321summary-branches::
314 Specifies the number of branches to display in the repository "summary" 322 Specifies the number of branches to display in the repository "summary"
315 view. Default value: "10". 323 view. Default value: "10".
316 324
317summary-log:: 325summary-log::
318 Specifies the number of log entries to display in the repository 326 Specifies the number of log entries to display in the repository
319 "summary" view. Default value: "10". 327 "summary" view. Default value: "10".
320 328
321summary-tags:: 329summary-tags::
322 Specifies the number of tags to display in the repository "summary" 330 Specifies the number of tags to display in the repository "summary"
323 view. Default value: "10". 331 view. Default value: "10".
324 332
325strict-export:: 333strict-export::
326 Filename which, if specified, needs to be present within the repository 334 Filename which, if specified, needs to be present within the repository
327 for cgit to allow access to that repository. This can be used to emulate 335 for cgit to allow access to that repository. This can be used to emulate
328 gitweb's EXPORT_OK and STRICT_EXPORT functionality and limit cgit's 336 gitweb's EXPORT_OK and STRICT_EXPORT functionality and limit cgit's
329 repositories to match those exported by git-daemon. This option MUST come 337 repositories to match those exported by git-daemon. This option MUST come
330 before 'scan-path'. 338 before 'scan-path'.
331 339
332virtual-root:: 340virtual-root::
333 Url which, if specified, will be used as root for all cgit links. It 341 Url which, if specified, will be used as root for all cgit links. It
334 will also cause cgit to generate 'virtual urls', i.e. urls like 342 will also cause cgit to generate 'virtual urls', i.e. urls like
335 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 343 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
336 value: none. 344 value: none.
337 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 345 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
338 same kind of virtual urls, so this option will probably be deprecated. 346 same kind of virtual urls, so this option will probably be deprecated.
339 347
340REPOSITORY SETTINGS 348REPOSITORY SETTINGS
341------------------- 349-------------------
342repo.about-filter:: 350repo.about-filter::
343 Override the default about-filter. Default value: none. See also: 351 Override the default about-filter. Default value: none. See also:
344 "enable-filter-overrides". 352 "enable-filter-overrides".
345 353
346repo.clone-url:: 354repo.clone-url::
347 A list of space-separated urls which can be used to clone this repo. 355 A list of space-separated urls which can be used to clone this repo.
348 Default value: none. 356 Default value: none.
349 357
350repo.commit-filter:: 358repo.commit-filter::
351 Override the default commit-filter. Default value: none. See also: 359 Override the default commit-filter. Default value: none. See also:
352 "enable-filter-overrides". 360 "enable-filter-overrides".
353 361
354repo.defbranch:: 362repo.defbranch::
355 The name of the default branch for this repository. If no such branch 363 The name of the default branch for this repository. If no such branch
356 exists in the repository, the first branch name (when sorted) is used 364 exists in the repository, the first branch name (when sorted) is used
357 as default instead. Default value: "master". 365 as default instead. Default value: "master".
358 366
359repo.desc:: 367repo.desc::
360 The value to show as repository description. Default value: none. 368 The value to show as repository description. Default value: none.
361 369
362repo.enable-commit-graph:: 370repo.enable-commit-graph::
363 A flag which can be used to disable the global setting 371 A flag which can be used to disable the global setting
364 `enable-commit-graph'. Default value: none. 372 `enable-commit-graph'. Default value: none.
365 373
366repo.enable-log-filecount:: 374repo.enable-log-filecount::
367 A flag which can be used to disable the global setting 375 A flag which can be used to disable the global setting
368 `enable-log-filecount'. Default value: none. 376 `enable-log-filecount'. Default value: none.
369 377
370repo.enable-log-linecount:: 378repo.enable-log-linecount::
371 A flag which can be used to disable the global setting 379 A flag which can be used to disable the global setting
372 `enable-log-linecount'. Default value: none. 380 `enable-log-linecount'. Default value: none.
diff --git a/scan-tree.c b/scan-tree.c
index a0e09ce..627af1b 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -66,174 +66,176 @@ static char *xstrrchr(char *s, char *from, int c)
66 while (from >= s && *from != c) 66 while (from >= s && *from != c)
67 from--; 67 from--;
68 return from < s ? NULL : from; 68 return from < s ? NULL : from;
69} 69}
70 70
71static void add_repo(const char *base, const char *path, repo_config_fn fn) 71static void add_repo(const char *base, const char *path, repo_config_fn fn)
72{ 72{
73 struct stat st; 73 struct stat st;
74 struct passwd *pwd; 74 struct passwd *pwd;
75 char *rel, *p, *slash; 75 char *rel, *p, *slash;
76 int n; 76 int n;
77 size_t size; 77 size_t size;
78 78
79 if (stat(path, &st)) { 79 if (stat(path, &st)) {
80 fprintf(stderr, "Error accessing %s: %s (%d)\n", 80 fprintf(stderr, "Error accessing %s: %s (%d)\n",
81 path, strerror(errno), errno); 81 path, strerror(errno), errno);
82 return; 82 return;
83 } 83 }
84 84
85 if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st)) 85 if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st))
86 return; 86 return;
87 87
88 if (!stat(fmt("%s/noweb", path), &st)) 88 if (!stat(fmt("%s/noweb", path), &st))
89 return; 89 return;
90 90
91 owner = NULL; 91 owner = NULL;
92 if (ctx.cfg.enable_gitweb_owner) 92 if (ctx.cfg.enable_gitweb_owner)
93 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL); 93 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
94 if (base == path) 94 if (base == path)
95 rel = xstrdup(fmt("%s", path)); 95 rel = xstrdup(fmt("%s", path));
96 else 96 else
97 rel = xstrdup(fmt("%s", path + strlen(base) + 1)); 97 rel = xstrdup(fmt("%s", path + strlen(base) + 1));
98 98
99 if (!strcmp(rel + strlen(rel) - 5, "/.git")) 99 if (!strcmp(rel + strlen(rel) - 5, "/.git"))
100 rel[strlen(rel) - 5] = '\0'; 100 rel[strlen(rel) - 5] = '\0';
101 101
102 repo = cgit_add_repo(rel); 102 repo = cgit_add_repo(rel);
103 if (ctx.cfg.remove_suffix) 103 if (ctx.cfg.remove_suffix)
104 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) 104 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
105 *p = '\0'; 105 *p = '\0';
106 repo->name = repo->url; 106 repo->name = repo->url;
107 repo->path = xstrdup(path); 107 repo->path = xstrdup(path);
108 while (!owner) { 108 while (!owner) {
109 if ((pwd = getpwuid(st.st_uid)) == NULL) { 109 if ((pwd = getpwuid(st.st_uid)) == NULL) {
110 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 110 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
111 path, strerror(errno), errno); 111 path, strerror(errno), errno);
112 break; 112 break;
113 } 113 }
114 if (pwd->pw_gecos) 114 if (pwd->pw_gecos)
115 if ((p = strchr(pwd->pw_gecos, ','))) 115 if ((p = strchr(pwd->pw_gecos, ',')))
116 *p = '\0'; 116 *p = '\0';
117 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); 117 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
118 } 118 }
119 repo->owner = owner; 119 repo->owner = owner;
120 120
121 p = fmt("%s/description", path); 121 p = fmt("%s/description", path);
122 if (!stat(p, &st)) 122 if (!stat(p, &st))
123 readfile(p, &repo->desc, &size); 123 readfile(p, &repo->desc, &size);
124 124
125 if (!repo->readme) { 125 if (!repo->readme) {
126 p = fmt("%s/README.html", path); 126 p = fmt("%s/README.html", path);
127 if (!stat(p, &st)) 127 if (!stat(p, &st))
128 repo->readme = "README.html"; 128 repo->readme = "README.html";
129 } 129 }
130 if (ctx.cfg.section_from_path) { 130 if (ctx.cfg.section_from_path) {
131 n = ctx.cfg.section_from_path; 131 n = ctx.cfg.section_from_path;
132 if (n > 0) { 132 if (n > 0) {
133 slash = rel; 133 slash = rel;
134 while (slash && n && (slash = strchr(slash, '/'))) 134 while (slash && n && (slash = strchr(slash, '/')))
135 n--; 135 n--;
136 } else { 136 } else {
137 slash = rel + strlen(rel); 137 slash = rel + strlen(rel);
138 while (slash && n && (slash = xstrrchr(rel, slash, '/'))) 138 while (slash && n && (slash = xstrrchr(rel, slash, '/')))
139 n++; 139 n++;
140 } 140 }
141 if (slash && !n) { 141 if (slash && !n) {
142 *slash = '\0'; 142 *slash = '\0';
143 repo->section = xstrdup(rel); 143 repo->section = xstrdup(rel);
144 *slash = '/'; 144 *slash = '/';
145 if (!prefixcmp(repo->name, repo->section)) { 145 if (!prefixcmp(repo->name, repo->section)) {
146 repo->name += strlen(repo->section); 146 repo->name += strlen(repo->section);
147 if (*repo->name == '/') 147 if (*repo->name == '/')
148 repo->name++; 148 repo->name++;
149 } 149 }
150 } 150 }
151 } 151 }
152 152
153 p = fmt("%s/cgitrc", path); 153 p = fmt("%s/cgitrc", path);
154 if (!stat(p, &st)) { 154 if (!stat(p, &st)) {
155 config_fn = fn; 155 config_fn = fn;
156 parse_configfile(xstrdup(p), &repo_config); 156 parse_configfile(xstrdup(p), &repo_config);
157 } 157 }
158} 158}
159 159
160static void scan_path(const char *base, const char *path, repo_config_fn fn) 160static void scan_path(const char *base, const char *path, repo_config_fn fn)
161{ 161{
162 DIR *dir; 162 DIR *dir = opendir(path);
163 struct dirent *ent; 163 struct dirent *ent;
164 char *buf; 164 char *buf;
165 struct stat st; 165 struct stat st;
166 166
167 if (!dir) {
168 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
169 path, strerror(errno), errno);
170 return;
171 }
167 if (is_git_dir(path)) { 172 if (is_git_dir(path)) {
168 add_repo(base, path, fn); 173 add_repo(base, path, fn);
169 return; 174 goto end;
170 } 175 }
171 if (is_git_dir(fmt("%s/.git", path))) { 176 if (is_git_dir(fmt("%s/.git", path))) {
172 add_repo(base, fmt("%s/.git", path), fn); 177 add_repo(base, fmt("%s/.git", path), fn);
173 return; 178 goto end;
174 }
175 dir = opendir(path);
176 if (!dir) {
177 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
178 path, strerror(errno), errno);
179 return;
180 } 179 }
181 while((ent = readdir(dir)) != NULL) { 180 while((ent = readdir(dir)) != NULL) {
182 if (ent->d_name[0] == '.') { 181 if (ent->d_name[0] == '.') {
183 if (ent->d_name[1] == '\0') 182 if (ent->d_name[1] == '\0')
184 continue; 183 continue;
185 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 184 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
186 continue; 185 continue;
186 if (!ctx.cfg.scan_hidden_path)
187 continue;
187 } 188 }
188 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 189 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
189 if (!buf) { 190 if (!buf) {
190 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 191 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
191 path, strerror(errno), errno); 192 path, strerror(errno), errno);
192 exit(1); 193 exit(1);
193 } 194 }
194 sprintf(buf, "%s/%s", path, ent->d_name); 195 sprintf(buf, "%s/%s", path, ent->d_name);
195 if (stat(buf, &st)) { 196 if (stat(buf, &st)) {
196 fprintf(stderr, "Error checking path %s: %s (%d)\n", 197 fprintf(stderr, "Error checking path %s: %s (%d)\n",
197 buf, strerror(errno), errno); 198 buf, strerror(errno), errno);
198 free(buf); 199 free(buf);
199 continue; 200 continue;
200 } 201 }
201 if (S_ISDIR(st.st_mode)) 202 if (S_ISDIR(st.st_mode))
202 scan_path(base, buf, fn); 203 scan_path(base, buf, fn);
203 free(buf); 204 free(buf);
204 } 205 }
206end:
205 closedir(dir); 207 closedir(dir);
206} 208}
207 209
208#define lastc(s) s[strlen(s) - 1] 210#define lastc(s) s[strlen(s) - 1]
209 211
210void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) 212void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
211{ 213{
212 char line[MAX_PATH * 2], *z; 214 char line[MAX_PATH * 2], *z;
213 FILE *projects; 215 FILE *projects;
214 int err; 216 int err;
215 217
216 projects = fopen(projectsfile, "r"); 218 projects = fopen(projectsfile, "r");
217 if (!projects) { 219 if (!projects) {
218 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n", 220 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
219 projectsfile, strerror(errno), errno); 221 projectsfile, strerror(errno), errno);
220 } 222 }
221 while (fgets(line, sizeof(line), projects) != NULL) { 223 while (fgets(line, sizeof(line), projects) != NULL) {
222 for (z = &lastc(line); 224 for (z = &lastc(line);
223 strlen(line) && strchr("\n\r", *z); 225 strlen(line) && strchr("\n\r", *z);
224 z = &lastc(line)) 226 z = &lastc(line))
225 *z = '\0'; 227 *z = '\0';
226 if (strlen(line)) 228 if (strlen(line))
227 scan_path(path, fmt("%s/%s", path, line), fn); 229 scan_path(path, fmt("%s/%s", path, line), fn);
228 } 230 }
229 if ((err = ferror(projects))) { 231 if ((err = ferror(projects))) {
230 fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n", 232 fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
231 projectsfile, strerror(err), err); 233 projectsfile, strerror(err), err);
232 } 234 }
233 fclose(projects); 235 fclose(projects);
234} 236}
235 237
236void scan_tree(const char *path, repo_config_fn fn) 238void scan_tree(const char *path, repo_config_fn fn)
237{ 239{
238 scan_path(path, path, fn); 240 scan_path(path, path, fn);
239} 241}