summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c3
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt6
-rw-r--r--scan-tree.c3
4 files changed, 13 insertions, 0 deletions
diff --git a/cgit.c b/cgit.c
index 2364d1c..f9a42bb 100644
--- a/cgit.c
+++ b/cgit.c
@@ -144,225 +144,228 @@ void config_cb(const char *name, const char *value)
144 else if (!strcmp(name, "enable-remote-branches")) 144 else if (!strcmp(name, "enable-remote-branches"))
145 ctx.cfg.enable_remote_branches = atoi(value); 145 ctx.cfg.enable_remote_branches = atoi(value);
146 else if (!strcmp(name, "enable-subject-links")) 146 else if (!strcmp(name, "enable-subject-links"))
147 ctx.cfg.enable_subject_links = atoi(value); 147 ctx.cfg.enable_subject_links = atoi(value);
148 else if (!strcmp(name, "enable-tree-linenumbers")) 148 else if (!strcmp(name, "enable-tree-linenumbers"))
149 ctx.cfg.enable_tree_linenumbers = atoi(value); 149 ctx.cfg.enable_tree_linenumbers = atoi(value);
150 else if (!strcmp(name, "max-stats")) 150 else if (!strcmp(name, "max-stats"))
151 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 151 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
152 else if (!strcmp(name, "cache-size")) 152 else if (!strcmp(name, "cache-size"))
153 ctx.cfg.cache_size = atoi(value); 153 ctx.cfg.cache_size = atoi(value);
154 else if (!strcmp(name, "cache-root")) 154 else if (!strcmp(name, "cache-root"))
155 ctx.cfg.cache_root = xstrdup(expand_macros(value)); 155 ctx.cfg.cache_root = xstrdup(expand_macros(value));
156 else if (!strcmp(name, "cache-root-ttl")) 156 else if (!strcmp(name, "cache-root-ttl"))
157 ctx.cfg.cache_root_ttl = atoi(value); 157 ctx.cfg.cache_root_ttl = atoi(value);
158 else if (!strcmp(name, "cache-repo-ttl")) 158 else if (!strcmp(name, "cache-repo-ttl"))
159 ctx.cfg.cache_repo_ttl = atoi(value); 159 ctx.cfg.cache_repo_ttl = atoi(value);
160 else if (!strcmp(name, "cache-scanrc-ttl")) 160 else if (!strcmp(name, "cache-scanrc-ttl"))
161 ctx.cfg.cache_scanrc_ttl = atoi(value); 161 ctx.cfg.cache_scanrc_ttl = atoi(value);
162 else if (!strcmp(name, "cache-static-ttl")) 162 else if (!strcmp(name, "cache-static-ttl"))
163 ctx.cfg.cache_static_ttl = atoi(value); 163 ctx.cfg.cache_static_ttl = atoi(value);
164 else if (!strcmp(name, "cache-dynamic-ttl")) 164 else if (!strcmp(name, "cache-dynamic-ttl"))
165 ctx.cfg.cache_dynamic_ttl = atoi(value); 165 ctx.cfg.cache_dynamic_ttl = atoi(value);
166 else if (!strcmp(name, "about-filter")) 166 else if (!strcmp(name, "about-filter"))
167 ctx.cfg.about_filter = new_filter(value, 0); 167 ctx.cfg.about_filter = new_filter(value, 0);
168 else if (!strcmp(name, "commit-filter")) 168 else if (!strcmp(name, "commit-filter"))
169 ctx.cfg.commit_filter = new_filter(value, 0); 169 ctx.cfg.commit_filter = new_filter(value, 0);
170 else if (!strcmp(name, "embedded")) 170 else if (!strcmp(name, "embedded"))
171 ctx.cfg.embedded = atoi(value); 171 ctx.cfg.embedded = atoi(value);
172 else if (!strcmp(name, "max-atom-items")) 172 else if (!strcmp(name, "max-atom-items"))
173 ctx.cfg.max_atom_items = atoi(value); 173 ctx.cfg.max_atom_items = atoi(value);
174 else if (!strcmp(name, "max-message-length")) 174 else if (!strcmp(name, "max-message-length"))
175 ctx.cfg.max_msg_len = atoi(value); 175 ctx.cfg.max_msg_len = atoi(value);
176 else if (!strcmp(name, "max-repodesc-length")) 176 else if (!strcmp(name, "max-repodesc-length"))
177 ctx.cfg.max_repodesc_len = atoi(value); 177 ctx.cfg.max_repodesc_len = atoi(value);
178 else if (!strcmp(name, "max-blob-size")) 178 else if (!strcmp(name, "max-blob-size"))
179 ctx.cfg.max_blob_size = atoi(value); 179 ctx.cfg.max_blob_size = atoi(value);
180 else if (!strcmp(name, "max-repo-count")) 180 else if (!strcmp(name, "max-repo-count"))
181 ctx.cfg.max_repo_count = atoi(value); 181 ctx.cfg.max_repo_count = atoi(value);
182 else if (!strcmp(name, "max-commit-count")) 182 else if (!strcmp(name, "max-commit-count"))
183 ctx.cfg.max_commit_count = atoi(value); 183 ctx.cfg.max_commit_count = atoi(value);
184 else if (!strcmp(name, "project-list")) 184 else if (!strcmp(name, "project-list"))
185 ctx.cfg.project_list = xstrdup(expand_macros(value)); 185 ctx.cfg.project_list = xstrdup(expand_macros(value));
186 else if (!strcmp(name, "scan-path")) 186 else if (!strcmp(name, "scan-path"))
187 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 187 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
188 process_cached_repolist(expand_macros(value)); 188 process_cached_repolist(expand_macros(value));
189 else if (ctx.cfg.project_list) 189 else if (ctx.cfg.project_list)
190 scan_projects(expand_macros(value), 190 scan_projects(expand_macros(value),
191 ctx.cfg.project_list, repo_config); 191 ctx.cfg.project_list, repo_config);
192 else 192 else
193 scan_tree(expand_macros(value), repo_config); 193 scan_tree(expand_macros(value), repo_config);
194 else if (!strcmp(name, "source-filter")) 194 else if (!strcmp(name, "source-filter"))
195 ctx.cfg.source_filter = new_filter(value, 1); 195 ctx.cfg.source_filter = new_filter(value, 1);
196 else if (!strcmp(name, "summary-log")) 196 else if (!strcmp(name, "summary-log"))
197 ctx.cfg.summary_log = atoi(value); 197 ctx.cfg.summary_log = atoi(value);
198 else if (!strcmp(name, "summary-branches")) 198 else if (!strcmp(name, "summary-branches"))
199 ctx.cfg.summary_branches = atoi(value); 199 ctx.cfg.summary_branches = atoi(value);
200 else if (!strcmp(name, "summary-tags")) 200 else if (!strcmp(name, "summary-tags"))
201 ctx.cfg.summary_tags = atoi(value); 201 ctx.cfg.summary_tags = atoi(value);
202 else if (!strcmp(name, "side-by-side-diffs")) 202 else if (!strcmp(name, "side-by-side-diffs"))
203 ctx.cfg.ssdiff = atoi(value); 203 ctx.cfg.ssdiff = atoi(value);
204 else if (!strcmp(name, "agefile")) 204 else if (!strcmp(name, "agefile"))
205 ctx.cfg.agefile = xstrdup(value); 205 ctx.cfg.agefile = xstrdup(value);
206 else if (!strcmp(name, "renamelimit")) 206 else if (!strcmp(name, "renamelimit"))
207 ctx.cfg.renamelimit = atoi(value); 207 ctx.cfg.renamelimit = atoi(value);
208 else if (!strcmp(name, "remove-suffix"))
209 ctx.cfg.remove_suffix = atoi(value);
208 else if (!strcmp(name, "robots")) 210 else if (!strcmp(name, "robots"))
209 ctx.cfg.robots = xstrdup(value); 211 ctx.cfg.robots = xstrdup(value);
210 else if (!strcmp(name, "clone-prefix")) 212 else if (!strcmp(name, "clone-prefix"))
211 ctx.cfg.clone_prefix = xstrdup(value); 213 ctx.cfg.clone_prefix = xstrdup(value);
212 else if (!strcmp(name, "local-time")) 214 else if (!strcmp(name, "local-time"))
213 ctx.cfg.local_time = atoi(value); 215 ctx.cfg.local_time = atoi(value);
214 else if (!prefixcmp(name, "mimetype.")) 216 else if (!prefixcmp(name, "mimetype."))
215 add_mimetype(name + 9, value); 217 add_mimetype(name + 9, value);
216 else if (!strcmp(name, "include")) 218 else if (!strcmp(name, "include"))
217 parse_configfile(expand_macros(value), config_cb); 219 parse_configfile(expand_macros(value), config_cb);
218} 220}
219 221
220static void querystring_cb(const char *name, const char *value) 222static void querystring_cb(const char *name, const char *value)
221{ 223{
222 if (!value) 224 if (!value)
223 value = ""; 225 value = "";
224 226
225 if (!strcmp(name,"r")) { 227 if (!strcmp(name,"r")) {
226 ctx.qry.repo = xstrdup(value); 228 ctx.qry.repo = xstrdup(value);
227 ctx.repo = cgit_get_repoinfo(value); 229 ctx.repo = cgit_get_repoinfo(value);
228 } else if (!strcmp(name, "p")) { 230 } else if (!strcmp(name, "p")) {
229 ctx.qry.page = xstrdup(value); 231 ctx.qry.page = xstrdup(value);
230 } else if (!strcmp(name, "url")) { 232 } else if (!strcmp(name, "url")) {
231 if (*value == '/') 233 if (*value == '/')
232 value++; 234 value++;
233 ctx.qry.url = xstrdup(value); 235 ctx.qry.url = xstrdup(value);
234 cgit_parse_url(value); 236 cgit_parse_url(value);
235 } else if (!strcmp(name, "qt")) { 237 } else if (!strcmp(name, "qt")) {
236 ctx.qry.grep = xstrdup(value); 238 ctx.qry.grep = xstrdup(value);
237 } else if (!strcmp(name, "q")) { 239 } else if (!strcmp(name, "q")) {
238 ctx.qry.search = xstrdup(value); 240 ctx.qry.search = xstrdup(value);
239 } else if (!strcmp(name, "h")) { 241 } else if (!strcmp(name, "h")) {
240 ctx.qry.head = xstrdup(value); 242 ctx.qry.head = xstrdup(value);
241 ctx.qry.has_symref = 1; 243 ctx.qry.has_symref = 1;
242 } else if (!strcmp(name, "id")) { 244 } else if (!strcmp(name, "id")) {
243 ctx.qry.sha1 = xstrdup(value); 245 ctx.qry.sha1 = xstrdup(value);
244 ctx.qry.has_sha1 = 1; 246 ctx.qry.has_sha1 = 1;
245 } else if (!strcmp(name, "id2")) { 247 } else if (!strcmp(name, "id2")) {
246 ctx.qry.sha2 = xstrdup(value); 248 ctx.qry.sha2 = xstrdup(value);
247 ctx.qry.has_sha1 = 1; 249 ctx.qry.has_sha1 = 1;
248 } else if (!strcmp(name, "ofs")) { 250 } else if (!strcmp(name, "ofs")) {
249 ctx.qry.ofs = atoi(value); 251 ctx.qry.ofs = atoi(value);
250 } else if (!strcmp(name, "path")) { 252 } else if (!strcmp(name, "path")) {
251 ctx.qry.path = trim_end(value, '/'); 253 ctx.qry.path = trim_end(value, '/');
252 } else if (!strcmp(name, "name")) { 254 } else if (!strcmp(name, "name")) {
253 ctx.qry.name = xstrdup(value); 255 ctx.qry.name = xstrdup(value);
254 } else if (!strcmp(name, "mimetype")) { 256 } else if (!strcmp(name, "mimetype")) {
255 ctx.qry.mimetype = xstrdup(value); 257 ctx.qry.mimetype = xstrdup(value);
256 } else if (!strcmp(name, "s")){ 258 } else if (!strcmp(name, "s")){
257 ctx.qry.sort = xstrdup(value); 259 ctx.qry.sort = xstrdup(value);
258 } else if (!strcmp(name, "showmsg")) { 260 } else if (!strcmp(name, "showmsg")) {
259 ctx.qry.showmsg = atoi(value); 261 ctx.qry.showmsg = atoi(value);
260 } else if (!strcmp(name, "period")) { 262 } else if (!strcmp(name, "period")) {
261 ctx.qry.period = xstrdup(value); 263 ctx.qry.period = xstrdup(value);
262 } else if (!strcmp(name, "ss")) { 264 } else if (!strcmp(name, "ss")) {
263 ctx.qry.ssdiff = atoi(value); 265 ctx.qry.ssdiff = atoi(value);
264 } else if (!strcmp(name, "all")) { 266 } else if (!strcmp(name, "all")) {
265 ctx.qry.show_all = atoi(value); 267 ctx.qry.show_all = atoi(value);
266 } else if (!strcmp(name, "context")) { 268 } else if (!strcmp(name, "context")) {
267 ctx.qry.context = atoi(value); 269 ctx.qry.context = atoi(value);
268 } else if (!strcmp(name, "ignorews")) { 270 } else if (!strcmp(name, "ignorews")) {
269 ctx.qry.ignorews = atoi(value); 271 ctx.qry.ignorews = atoi(value);
270 } 272 }
271} 273}
272 274
273char *xstrdupn(const char *str) 275char *xstrdupn(const char *str)
274{ 276{
275 return (str ? xstrdup(str) : NULL); 277 return (str ? xstrdup(str) : NULL);
276} 278}
277 279
278static void prepare_context(struct cgit_context *ctx) 280static void prepare_context(struct cgit_context *ctx)
279{ 281{
280 memset(ctx, 0, sizeof(*ctx)); 282 memset(ctx, 0, sizeof(*ctx));
281 ctx->cfg.agefile = "info/web/last-modified"; 283 ctx->cfg.agefile = "info/web/last-modified";
282 ctx->cfg.nocache = 0; 284 ctx->cfg.nocache = 0;
283 ctx->cfg.cache_size = 0; 285 ctx->cfg.cache_size = 0;
284 ctx->cfg.cache_dynamic_ttl = 5; 286 ctx->cfg.cache_dynamic_ttl = 5;
285 ctx->cfg.cache_max_create_time = 5; 287 ctx->cfg.cache_max_create_time = 5;
286 ctx->cfg.cache_repo_ttl = 5; 288 ctx->cfg.cache_repo_ttl = 5;
287 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 289 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
288 ctx->cfg.cache_root_ttl = 5; 290 ctx->cfg.cache_root_ttl = 5;
289 ctx->cfg.cache_scanrc_ttl = 15; 291 ctx->cfg.cache_scanrc_ttl = 15;
290 ctx->cfg.cache_static_ttl = -1; 292 ctx->cfg.cache_static_ttl = -1;
291 ctx->cfg.css = "/cgit.css"; 293 ctx->cfg.css = "/cgit.css";
292 ctx->cfg.logo = "/cgit.png"; 294 ctx->cfg.logo = "/cgit.png";
293 ctx->cfg.local_time = 0; 295 ctx->cfg.local_time = 0;
294 ctx->cfg.enable_tree_linenumbers = 1; 296 ctx->cfg.enable_tree_linenumbers = 1;
295 ctx->cfg.max_repo_count = 50; 297 ctx->cfg.max_repo_count = 50;
296 ctx->cfg.max_commit_count = 50; 298 ctx->cfg.max_commit_count = 50;
297 ctx->cfg.max_lock_attempts = 5; 299 ctx->cfg.max_lock_attempts = 5;
298 ctx->cfg.max_msg_len = 80; 300 ctx->cfg.max_msg_len = 80;
299 ctx->cfg.max_repodesc_len = 80; 301 ctx->cfg.max_repodesc_len = 80;
300 ctx->cfg.max_blob_size = 0; 302 ctx->cfg.max_blob_size = 0;
301 ctx->cfg.max_stats = 0; 303 ctx->cfg.max_stats = 0;
302 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 304 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
303 ctx->cfg.project_list = NULL; 305 ctx->cfg.project_list = NULL;
304 ctx->cfg.renamelimit = -1; 306 ctx->cfg.renamelimit = -1;
307 ctx->cfg.remove_suffix = 0;
305 ctx->cfg.robots = "index, nofollow"; 308 ctx->cfg.robots = "index, nofollow";
306 ctx->cfg.root_title = "Git repository browser"; 309 ctx->cfg.root_title = "Git repository browser";
307 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 310 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
308 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 311 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
309 ctx->cfg.section = ""; 312 ctx->cfg.section = "";
310 ctx->cfg.summary_branches = 10; 313 ctx->cfg.summary_branches = 10;
311 ctx->cfg.summary_log = 10; 314 ctx->cfg.summary_log = 10;
312 ctx->cfg.summary_tags = 10; 315 ctx->cfg.summary_tags = 10;
313 ctx->cfg.max_atom_items = 10; 316 ctx->cfg.max_atom_items = 10;
314 ctx->cfg.ssdiff = 0; 317 ctx->cfg.ssdiff = 0;
315 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 318 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
316 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 319 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
317 ctx->env.https = xstrdupn(getenv("HTTPS")); 320 ctx->env.https = xstrdupn(getenv("HTTPS"));
318 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 321 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
319 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 322 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
320 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 323 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
321 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 324 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
322 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 325 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
323 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 326 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
324 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 327 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
325 ctx->page.mimetype = "text/html"; 328 ctx->page.mimetype = "text/html";
326 ctx->page.charset = PAGE_ENCODING; 329 ctx->page.charset = PAGE_ENCODING;
327 ctx->page.filename = NULL; 330 ctx->page.filename = NULL;
328 ctx->page.size = 0; 331 ctx->page.size = 0;
329 ctx->page.modified = time(NULL); 332 ctx->page.modified = time(NULL);
330 ctx->page.expires = ctx->page.modified; 333 ctx->page.expires = ctx->page.modified;
331 ctx->page.etag = NULL; 334 ctx->page.etag = NULL;
332 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 335 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
333 if (ctx->env.script_name) 336 if (ctx->env.script_name)
334 ctx->cfg.script_name = ctx->env.script_name; 337 ctx->cfg.script_name = ctx->env.script_name;
335 if (ctx->env.query_string) 338 if (ctx->env.query_string)
336 ctx->qry.raw = ctx->env.query_string; 339 ctx->qry.raw = ctx->env.query_string;
337 if (!ctx->env.cgit_config) 340 if (!ctx->env.cgit_config)
338 ctx->env.cgit_config = CGIT_CONFIG; 341 ctx->env.cgit_config = CGIT_CONFIG;
339} 342}
340 343
341struct refmatch { 344struct refmatch {
342 char *req_ref; 345 char *req_ref;
343 char *first_ref; 346 char *first_ref;
344 int match; 347 int match;
345}; 348};
346 349
347int find_current_ref(const char *refname, const unsigned char *sha1, 350int find_current_ref(const char *refname, const unsigned char *sha1,
348 int flags, void *cb_data) 351 int flags, void *cb_data)
349{ 352{
350 struct refmatch *info; 353 struct refmatch *info;
351 354
352 info = (struct refmatch *)cb_data; 355 info = (struct refmatch *)cb_data;
353 if (!strcmp(refname, info->req_ref)) 356 if (!strcmp(refname, info->req_ref))
354 info->match = 1; 357 info->match = 1;
355 if (!info->first_ref) 358 if (!info->first_ref)
356 info->first_ref = xstrdup(refname); 359 info->first_ref = xstrdup(refname);
357 return info->match; 360 return info->match;
358} 361}
359 362
360char *find_default_branch(struct cgit_repo *repo) 363char *find_default_branch(struct cgit_repo *repo)
361{ 364{
362 struct refmatch info; 365 struct refmatch info;
363 char *ref; 366 char *ref;
364 367
365 info.req_ref = repo->defbranch; 368 info.req_ref = repo->defbranch;
366 info.first_ref = NULL; 369 info.first_ref = NULL;
367 info.match = 0; 370 info.match = 0;
368 for_each_branch_ref(find_current_ref, &info); 371 for_each_branch_ref(find_current_ref, &info);
diff --git a/cgit.h b/cgit.h
index 4591f8c..ada8535 100644
--- a/cgit.h
+++ b/cgit.h
@@ -141,128 +141,129 @@ struct cgit_query {
141 char *mimetype; 141 char *mimetype;
142 char *url; 142 char *url;
143 char *period; 143 char *period;
144 int ofs; 144 int ofs;
145 int nohead; 145 int nohead;
146 char *sort; 146 char *sort;
147 int showmsg; 147 int showmsg;
148 int ssdiff; 148 int ssdiff;
149 int show_all; 149 int show_all;
150 int context; 150 int context;
151 int ignorews; 151 int ignorews;
152 char *vpath; 152 char *vpath;
153}; 153};
154 154
155struct cgit_config { 155struct cgit_config {
156 char *agefile; 156 char *agefile;
157 char *cache_root; 157 char *cache_root;
158 char *clone_prefix; 158 char *clone_prefix;
159 char *css; 159 char *css;
160 char *favicon; 160 char *favicon;
161 char *footer; 161 char *footer;
162 char *head_include; 162 char *head_include;
163 char *header; 163 char *header;
164 char *index_header; 164 char *index_header;
165 char *index_info; 165 char *index_info;
166 char *logo; 166 char *logo;
167 char *logo_link; 167 char *logo_link;
168 char *module_link; 168 char *module_link;
169 char *project_list; 169 char *project_list;
170 char *robots; 170 char *robots;
171 char *root_title; 171 char *root_title;
172 char *root_desc; 172 char *root_desc;
173 char *root_readme; 173 char *root_readme;
174 char *script_name; 174 char *script_name;
175 char *section; 175 char *section;
176 char *virtual_root; 176 char *virtual_root;
177 int cache_size; 177 int cache_size;
178 int cache_dynamic_ttl; 178 int cache_dynamic_ttl;
179 int cache_max_create_time; 179 int cache_max_create_time;
180 int cache_repo_ttl; 180 int cache_repo_ttl;
181 int cache_root_ttl; 181 int cache_root_ttl;
182 int cache_scanrc_ttl; 182 int cache_scanrc_ttl;
183 int cache_static_ttl; 183 int cache_static_ttl;
184 int embedded; 184 int embedded;
185 int enable_filter_overrides; 185 int enable_filter_overrides;
186 int enable_index_links; 186 int enable_index_links;
187 int enable_log_filecount; 187 int enable_log_filecount;
188 int enable_log_linecount; 188 int enable_log_linecount;
189 int enable_remote_branches; 189 int enable_remote_branches;
190 int enable_subject_links; 190 int enable_subject_links;
191 int enable_tree_linenumbers; 191 int enable_tree_linenumbers;
192 int local_time; 192 int local_time;
193 int max_atom_items; 193 int max_atom_items;
194 int max_repo_count; 194 int max_repo_count;
195 int max_commit_count; 195 int max_commit_count;
196 int max_lock_attempts; 196 int max_lock_attempts;
197 int max_msg_len; 197 int max_msg_len;
198 int max_repodesc_len; 198 int max_repodesc_len;
199 int max_blob_size; 199 int max_blob_size;
200 int max_stats; 200 int max_stats;
201 int nocache; 201 int nocache;
202 int noplainemail; 202 int noplainemail;
203 int noheader; 203 int noheader;
204 int renamelimit; 204 int renamelimit;
205 int remove_suffix;
205 int snapshots; 206 int snapshots;
206 int summary_branches; 207 int summary_branches;
207 int summary_log; 208 int summary_log;
208 int summary_tags; 209 int summary_tags;
209 int ssdiff; 210 int ssdiff;
210 struct string_list mimetypes; 211 struct string_list mimetypes;
211 struct cgit_filter *about_filter; 212 struct cgit_filter *about_filter;
212 struct cgit_filter *commit_filter; 213 struct cgit_filter *commit_filter;
213 struct cgit_filter *source_filter; 214 struct cgit_filter *source_filter;
214}; 215};
215 216
216struct cgit_page { 217struct cgit_page {
217 time_t modified; 218 time_t modified;
218 time_t expires; 219 time_t expires;
219 size_t size; 220 size_t size;
220 char *mimetype; 221 char *mimetype;
221 char *charset; 222 char *charset;
222 char *filename; 223 char *filename;
223 char *etag; 224 char *etag;
224 char *title; 225 char *title;
225 int status; 226 int status;
226 char *statusmsg; 227 char *statusmsg;
227}; 228};
228 229
229struct cgit_environment { 230struct cgit_environment {
230 char *cgit_config; 231 char *cgit_config;
231 char *http_host; 232 char *http_host;
232 char *https; 233 char *https;
233 char *no_http; 234 char *no_http;
234 char *path_info; 235 char *path_info;
235 char *query_string; 236 char *query_string;
236 char *request_method; 237 char *request_method;
237 char *script_name; 238 char *script_name;
238 char *server_name; 239 char *server_name;
239 char *server_port; 240 char *server_port;
240}; 241};
241 242
242struct cgit_context { 243struct cgit_context {
243 struct cgit_environment env; 244 struct cgit_environment env;
244 struct cgit_query qry; 245 struct cgit_query qry;
245 struct cgit_config cfg; 246 struct cgit_config cfg;
246 struct cgit_repo *repo; 247 struct cgit_repo *repo;
247 struct cgit_page page; 248 struct cgit_page page;
248}; 249};
249 250
250struct cgit_snapshot_format { 251struct cgit_snapshot_format {
251 const char *suffix; 252 const char *suffix;
252 const char *mimetype; 253 const char *mimetype;
253 write_archive_fn_t write_func; 254 write_archive_fn_t write_func;
254 int bit; 255 int bit;
255}; 256};
256 257
257extern const char *cgit_version; 258extern const char *cgit_version;
258 259
259extern struct cgit_repolist cgit_repolist; 260extern struct cgit_repolist cgit_repolist;
260extern struct cgit_context ctx; 261extern struct cgit_context ctx;
261extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 262extern const struct cgit_snapshot_format cgit_snapshot_formats[];
262 263
263extern struct cgit_repo *cgit_add_repo(const char *url); 264extern struct cgit_repo *cgit_add_repo(const char *url);
264extern struct cgit_repo *cgit_get_repoinfo(const char *url); 265extern struct cgit_repo *cgit_get_repoinfo(const char *url);
265extern void cgit_repo_config_cb(const char *name, const char *value); 266extern void cgit_repo_config_cb(const char *name, const char *value);
266 267
267extern int chk_zero(int result, char *msg); 268extern int chk_zero(int result, char *msg);
268extern int chk_positive(int result, char *msg); 269extern int chk_positive(int result, char *msg);
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index ec004d4..6fb1083 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -168,128 +168,133 @@ logo::
168 on all cgit pages. Default value: "/cgit.png". 168 on all cgit pages. Default value: "/cgit.png".
169 169
170logo-link:: 170logo-link::
171 Url loaded when clicking on the cgit logo image. If unspecified the 171 Url loaded when clicking on the cgit logo image. If unspecified the
172 calculated url of the repository index page will be used. Default 172 calculated url of the repository index page will be used. Default
173 value: none. 173 value: none.
174 174
175max-atom-items:: 175max-atom-items::
176 Specifies the number of items to display in atom feeds view. Default 176 Specifies the number of items to display in atom feeds view. Default
177 value: "10". 177 value: "10".
178 178
179max-commit-count:: 179max-commit-count::
180 Specifies the number of entries to list per page in "log" view. Default 180 Specifies the number of entries to list per page in "log" view. Default
181 value: "50". 181 value: "50".
182 182
183max-message-length:: 183max-message-length::
184 Specifies the maximum number of commit message characters to display in 184 Specifies the maximum number of commit message characters to display in
185 "log" view. Default value: "80". 185 "log" view. Default value: "80".
186 186
187max-repo-count:: 187max-repo-count::
188 Specifies the number of entries to list per page on therepository 188 Specifies the number of entries to list per page on therepository
189 index page. Default value: "50". 189 index page. Default value: "50".
190 190
191max-repodesc-length:: 191max-repodesc-length::
192 Specifies the maximum number of repo description characters to display 192 Specifies the maximum number of repo description characters to display
193 on the repository index page. Default value: "80". 193 on the repository index page. Default value: "80".
194 194
195max-blob-size:: 195max-blob-size::
196 Specifies the maximum size of a blob to display HTML for in KBytes. 196 Specifies the maximum size of a blob to display HTML for in KBytes.
197 Default value: "0" (limit disabled). 197 Default value: "0" (limit disabled).
198 198
199max-stats:: 199max-stats::
200 Set the default maximum statistics period. Valid values are "week", 200 Set the default maximum statistics period. Valid values are "week",
201 "month", "quarter" and "year". If unspecified, statistics are 201 "month", "quarter" and "year". If unspecified, statistics are
202 disabled. Default value: none. See also: "repo.max-stats". 202 disabled. Default value: none. See also: "repo.max-stats".
203 203
204mimetype.<ext>:: 204mimetype.<ext>::
205 Set the mimetype for the specified filename extension. This is used 205 Set the mimetype for the specified filename extension. This is used
206 by the `plain` command when returning blob content. 206 by the `plain` command when returning blob content.
207 207
208module-link:: 208module-link::
209 Text which will be used as the formatstring for a hyperlink when a 209 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 210 submodule is printed in a directory listing. The arguments for the
211 formatstring are the path and SHA1 of the submodule commit. Default 211 formatstring are the path and SHA1 of the submodule commit. Default
212 value: "./?repo=%s&page=commit&id=%s" 212 value: "./?repo=%s&page=commit&id=%s"
213 213
214nocache:: 214nocache::
215 If set to the value "1" caching will be disabled. This settings is 215 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 216 deprecated, and will not be honored starting with cgit-1.0. Default
217 value: "0". 217 value: "0".
218 218
219noplainemail:: 219noplainemail::
220 If set to "1" showing full author email adresses will be disabled. 220 If set to "1" showing full author email adresses will be disabled.
221 Default value: "0". 221 Default value: "0".
222 222
223noheader:: 223noheader::
224 Flag which, when set to "1", will make cgit omit the standard header 224 Flag which, when set to "1", will make cgit omit the standard header
225 on all pages. Default value: none. See also: "embedded". 225 on all pages. Default value: none. See also: "embedded".
226 226
227project-list:: 227project-list::
228 A list of subdirectories inside of scan-path, relative to it, that 228 A list of subdirectories inside of scan-path, relative to it, that
229 should loaded as git repositories. This must be defined prior to 229 should loaded as git repositories. This must be defined prior to
230 scan-path. Default value: none. See also: scan-path. 230 scan-path. Default value: none. See also: scan-path.
231 231
232remove-suffix::
233 If set to "1" and scan-path is enabled, if any repositories are found
234 with a suffix of ".git", this suffix will be removed for the url and
235 name. Default value: "0". See also: scan-path.
236
232renamelimit:: 237renamelimit::
233 Maximum number of files to consider when detecting renames. The value 238 Maximum number of files to consider when detecting renames. The value
234 "-1" uses the compiletime value in git (for further info, look at 239 "-1" uses the compiletime value in git (for further info, look at
235 `man git-diff`). Default value: "-1". 240 `man git-diff`). Default value: "-1".
236 241
237repo.group:: 242repo.group::
238 Legacy alias for "section". This option is deprecated and will not be 243 Legacy alias for "section". This option is deprecated and will not be
239 supported in cgit-1.0. 244 supported in cgit-1.0.
240 245
241robots:: 246robots::
242 Text used as content for the "robots" meta-tag. Default value: 247 Text used as content for the "robots" meta-tag. Default value:
243 "index, nofollow". 248 "index, nofollow".
244 249
245root-desc:: 250root-desc::
246 Text printed below the heading on the repository index page. Default 251 Text printed below the heading on the repository index page. Default
247 value: "a fast webinterface for the git dscm". 252 value: "a fast webinterface for the git dscm".
248 253
249root-readme:: 254root-readme::
250 The content of the file specified with this option will be included 255 The content of the file specified with this option will be included
251 verbatim below the "about" link on the repository index page. Default 256 verbatim below the "about" link on the repository index page. Default
252 value: none. 257 value: none.
253 258
254root-title:: 259root-title::
255 Text printed as heading on the repository index page. Default value: 260 Text printed as heading on the repository index page. Default value:
256 "Git Repository Browser". 261 "Git Repository Browser".
257 262
258scan-path:: 263scan-path::
259 A path which will be scanned for repositories. If caching is enabled, 264 A path which will be scanned for repositories. If caching is enabled,
260 the result will be cached as a cgitrc include-file in the cache 265 the result will be cached as a cgitrc include-file in the cache
261 directory. If project-list has been defined prior to scan-path, 266 directory. If project-list has been defined prior to scan-path,
262 scan-path loads only the directories listed in the file pointed to by 267 scan-path loads only the directories listed in the file pointed to by
263 project-list. Default value: none. See also: cache-scanrc-ttl, 268 project-list. Default value: none. See also: cache-scanrc-ttl,
264 project-list. 269 project-list.
265 270
266section:: 271section::
267 The name of the current repository section - all repositories defined 272 The name of the current repository section - all repositories defined
268 after this option will inherit the current section name. Default value: 273 after this option will inherit the current section name. Default value:
269 none. 274 none.
270 275
271side-by-side-diffs:: 276side-by-side-diffs::
272 If set to "1" shows side-by-side diffs instead of unidiffs per 277 If set to "1" shows side-by-side diffs instead of unidiffs per
273 default. Default value: "0". 278 default. Default value: "0".
274 279
275snapshots:: 280snapshots::
276 Text which specifies the default set of snapshot formats generated by 281 Text which specifies the default set of snapshot formats generated by
277 cgit. The value is a space-separated list of zero or more of the 282 cgit. The value is a space-separated list of zero or more of the
278 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 283 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
279 284
280source-filter:: 285source-filter::
281 Specifies a command which will be invoked to format plaintext blobs 286 Specifies a command which will be invoked to format plaintext blobs
282 in the tree view. The command will get the blob content on its STDIN 287 in the tree view. The command will get the blob content on its STDIN
283 and the name of the blob as its only command line argument. The STDOUT 288 and the name of the blob as its only command line argument. The STDOUT
284 from the command will be included verbatim as the blob contents, i.e. 289 from the command will be included verbatim as the blob contents, i.e.
285 this can be used to implement e.g. syntax highlighting. Default value: 290 this can be used to implement e.g. syntax highlighting. Default value:
286 none. 291 none.
287 292
288summary-branches:: 293summary-branches::
289 Specifies the number of branches to display in the repository "summary" 294 Specifies the number of branches to display in the repository "summary"
290 view. Default value: "10". 295 view. Default value: "10".
291 296
292summary-log:: 297summary-log::
293 Specifies the number of log entries to display in the repository 298 Specifies the number of log entries to display in the repository
294 "summary" view. Default value: "10". 299 "summary" view. Default value: "10".
295 300
@@ -477,64 +482,65 @@ repo.desc=the master foo repository
477repo.owner=fooman@foobar.com 482repo.owner=fooman@foobar.com
478repo.readme=info/web/about.html 483repo.readme=info/web/about.html
479 484
480 485
481repo.url=bar 486repo.url=bar
482repo.path=/pub/git/bar.git 487repo.path=/pub/git/bar.git
483repo.desc=the bars for your foo 488repo.desc=the bars for your foo
484repo.owner=barman@foobar.com 489repo.owner=barman@foobar.com
485repo.readme=info/web/about.html 490repo.readme=info/web/about.html
486 491
487 492
488# The next repositories will be displayed under the 'extras' heading 493# The next repositories will be displayed under the 'extras' heading
489section=extras 494section=extras
490 495
491 496
492repo.url=baz 497repo.url=baz
493repo.path=/pub/git/baz.git 498repo.path=/pub/git/baz.git
494repo.desc=a set of extensions for bar users 499repo.desc=a set of extensions for bar users
495 500
496repo.url=wiz 501repo.url=wiz
497repo.path=/pub/git/wiz.git 502repo.path=/pub/git/wiz.git
498repo.desc=the wizard of foo 503repo.desc=the wizard of foo
499 504
500 505
501# Add some mirrored repositories 506# Add some mirrored repositories
502section=mirrors 507section=mirrors
503 508
504 509
505repo.url=git 510repo.url=git
506repo.path=/pub/git/git.git 511repo.path=/pub/git/git.git
507repo.desc=the dscm 512repo.desc=the dscm
508 513
509 514
510repo.url=linux 515repo.url=linux
511repo.path=/pub/git/linux.git 516repo.path=/pub/git/linux.git
512repo.desc=the kernel 517repo.desc=the kernel
513 518
514# Disable adhoc downloads of this repo 519# Disable adhoc downloads of this repo
515repo.snapshots=0 520repo.snapshots=0
516 521
517# Disable line-counts for this repo 522# Disable line-counts for this repo
518repo.enable-log-linecount=0 523repo.enable-log-linecount=0
519 524
520# Restrict the max statistics period for this repo 525# Restrict the max statistics period for this repo
521repo.max-stats=month 526repo.max-stats=month
522.... 527....
523 528
524 529
525BUGS 530BUGS
526---- 531----
527Comments currently cannot appear on the same line as a setting; the comment 532Comments currently cannot appear on the same line as a setting; the comment
528will be included as part of the value. E.g. this line: 533will be included as part of the value. E.g. this line:
529 534
530 robots=index # allow indexing 535 robots=index # allow indexing
531 536
532will generate the following html element: 537will generate the following html element:
533 538
534 <meta name='robots' content='index # allow indexing'/> 539 <meta name='robots' content='index # allow indexing'/>
535 540
536 541
537 542
538AUTHOR 543AUTHOR
539------ 544------
540Lars Hjemli <hjemli@gmail.com> 545Lars Hjemli <hjemli@gmail.com>
546Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/scan-tree.c b/scan-tree.c
index 9bf9b38..a83a78c 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -20,128 +20,131 @@ static int is_git_dir(const char *path)
20 static char buf[MAX_PATH]; 20 static char buf[MAX_PATH];
21 21
22 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) { 22 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) {
23 fprintf(stderr, "Insanely long path: %s\n", path); 23 fprintf(stderr, "Insanely long path: %s\n", path);
24 return 0; 24 return 0;
25 } 25 }
26 if (stat(buf, &st)) { 26 if (stat(buf, &st)) {
27 if (errno != ENOENT) 27 if (errno != ENOENT)
28 fprintf(stderr, "Error checking path %s: %s (%d)\n", 28 fprintf(stderr, "Error checking path %s: %s (%d)\n",
29 path, strerror(errno), errno); 29 path, strerror(errno), errno);
30 return 0; 30 return 0;
31 } 31 }
32 if (!S_ISDIR(st.st_mode)) 32 if (!S_ISDIR(st.st_mode))
33 return 0; 33 return 0;
34 34
35 sprintf(buf, "%s/HEAD", path); 35 sprintf(buf, "%s/HEAD", path);
36 if (stat(buf, &st)) { 36 if (stat(buf, &st)) {
37 if (errno != ENOENT) 37 if (errno != ENOENT)
38 fprintf(stderr, "Error checking path %s: %s (%d)\n", 38 fprintf(stderr, "Error checking path %s: %s (%d)\n",
39 path, strerror(errno), errno); 39 path, strerror(errno), errno);
40 return 0; 40 return 0;
41 } 41 }
42 if (!S_ISREG(st.st_mode)) 42 if (!S_ISREG(st.st_mode))
43 return 0; 43 return 0;
44 44
45 return 1; 45 return 1;
46} 46}
47 47
48struct cgit_repo *repo; 48struct cgit_repo *repo;
49repo_config_fn config_fn; 49repo_config_fn config_fn;
50 50
51static void repo_config(const char *name, const char *value) 51static void repo_config(const char *name, const char *value)
52{ 52{
53 config_fn(repo, name, value); 53 config_fn(repo, name, value);
54} 54}
55 55
56static void add_repo(const char *base, const char *path, repo_config_fn fn) 56static void add_repo(const char *base, const char *path, repo_config_fn fn)
57{ 57{
58 struct stat st; 58 struct stat st;
59 struct passwd *pwd; 59 struct passwd *pwd;
60 char *p; 60 char *p;
61 size_t size; 61 size_t size;
62 62
63 if (stat(path, &st)) { 63 if (stat(path, &st)) {
64 fprintf(stderr, "Error accessing %s: %s (%d)\n", 64 fprintf(stderr, "Error accessing %s: %s (%d)\n",
65 path, strerror(errno), errno); 65 path, strerror(errno), errno);
66 return; 66 return;
67 } 67 }
68 if (!stat(fmt("%s/noweb", path), &st)) 68 if (!stat(fmt("%s/noweb", path), &st))
69 return; 69 return;
70 if ((pwd = getpwuid(st.st_uid)) == NULL) { 70 if ((pwd = getpwuid(st.st_uid)) == NULL) {
71 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 71 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
72 path, strerror(errno), errno); 72 path, strerror(errno), errno);
73 return; 73 return;
74 } 74 }
75 if (base == path) 75 if (base == path)
76 p = fmt("%s", path); 76 p = fmt("%s", path);
77 else 77 else
78 p = fmt("%s", path + strlen(base) + 1); 78 p = fmt("%s", path + strlen(base) + 1);
79 79
80 if (!strcmp(p + strlen(p) - 5, "/.git")) 80 if (!strcmp(p + strlen(p) - 5, "/.git"))
81 p[strlen(p) - 5] = '\0'; 81 p[strlen(p) - 5] = '\0';
82 82
83 repo = cgit_add_repo(xstrdup(p)); 83 repo = cgit_add_repo(xstrdup(p));
84 if (ctx.cfg.remove_suffix)
85 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
86 *p = '\0';
84 repo->name = repo->url; 87 repo->name = repo->url;
85 repo->path = xstrdup(path); 88 repo->path = xstrdup(path);
86 p = (pwd && pwd->pw_gecos) ? strchr(pwd->pw_gecos, ',') : NULL; 89 p = (pwd && pwd->pw_gecos) ? strchr(pwd->pw_gecos, ',') : NULL;
87 if (p) 90 if (p)
88 *p = '\0'; 91 *p = '\0';
89 repo->owner = (pwd ? xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name) : ""); 92 repo->owner = (pwd ? xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name) : "");
90 93
91 p = fmt("%s/description", path); 94 p = fmt("%s/description", path);
92 if (!stat(p, &st)) 95 if (!stat(p, &st))
93 readfile(p, &repo->desc, &size); 96 readfile(p, &repo->desc, &size);
94 97
95 p = fmt("%s/README.html", path); 98 p = fmt("%s/README.html", path);
96 if (!stat(p, &st)) 99 if (!stat(p, &st))
97 repo->readme = "README.html"; 100 repo->readme = "README.html";
98 101
99 p = fmt("%s/cgitrc", path); 102 p = fmt("%s/cgitrc", path);
100 if (!stat(p, &st)) { 103 if (!stat(p, &st)) {
101 config_fn = fn; 104 config_fn = fn;
102 parse_configfile(xstrdup(p), &repo_config); 105 parse_configfile(xstrdup(p), &repo_config);
103 } 106 }
104} 107}
105 108
106static void scan_path(const char *base, const char *path, repo_config_fn fn) 109static void scan_path(const char *base, const char *path, repo_config_fn fn)
107{ 110{
108 DIR *dir; 111 DIR *dir;
109 struct dirent *ent; 112 struct dirent *ent;
110 char *buf; 113 char *buf;
111 struct stat st; 114 struct stat st;
112 115
113 if (is_git_dir(path)) { 116 if (is_git_dir(path)) {
114 add_repo(base, path, fn); 117 add_repo(base, path, fn);
115 return; 118 return;
116 } 119 }
117 if (is_git_dir(fmt("%s/.git", path))) { 120 if (is_git_dir(fmt("%s/.git", path))) {
118 add_repo(base, fmt("%s/.git", path), fn); 121 add_repo(base, fmt("%s/.git", path), fn);
119 return; 122 return;
120 } 123 }
121 dir = opendir(path); 124 dir = opendir(path);
122 if (!dir) { 125 if (!dir) {
123 fprintf(stderr, "Error opening directory %s: %s (%d)\n", 126 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
124 path, strerror(errno), errno); 127 path, strerror(errno), errno);
125 return; 128 return;
126 } 129 }
127 while((ent = readdir(dir)) != NULL) { 130 while((ent = readdir(dir)) != NULL) {
128 if (ent->d_name[0] == '.') { 131 if (ent->d_name[0] == '.') {
129 if (ent->d_name[1] == '\0') 132 if (ent->d_name[1] == '\0')
130 continue; 133 continue;
131 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 134 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
132 continue; 135 continue;
133 } 136 }
134 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 137 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
135 if (!buf) { 138 if (!buf) {
136 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 139 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
137 path, strerror(errno), errno); 140 path, strerror(errno), errno);
138 exit(1); 141 exit(1);
139 } 142 }
140 sprintf(buf, "%s/%s", path, ent->d_name); 143 sprintf(buf, "%s/%s", path, ent->d_name);
141 if (stat(buf, &st)) { 144 if (stat(buf, &st)) {
142 fprintf(stderr, "Error checking path %s: %s (%d)\n", 145 fprintf(stderr, "Error checking path %s: %s (%d)\n",
143 buf, strerror(errno), errno); 146 buf, strerror(errno), errno);
144 free(buf); 147 free(buf);
145 continue; 148 continue;
146 } 149 }
147 if (S_ISDIR(st.st_mode)) 150 if (S_ISDIR(st.st_mode))