summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--cgit.c23
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt10
-rw-r--r--scan-tree.c37
-rw-r--r--scan-tree.h3
5 files changed, 68 insertions, 6 deletions
diff --git a/cgit.c b/cgit.c
index c263872..2364d1c 100644
--- a/cgit.c
+++ b/cgit.c
@@ -160,51 +160,56 @@ void config_cb(const char *name, const char *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"))
185 ctx.cfg.project_list = xstrdup(expand_macros(value));
184 else if (!strcmp(name, "scan-path")) 186 else if (!strcmp(name, "scan-path"))
185 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 187 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
186 process_cached_repolist(expand_macros(value)); 188 process_cached_repolist(expand_macros(value));
189 else if (ctx.cfg.project_list)
190 scan_projects(expand_macros(value),
191 ctx.cfg.project_list, repo_config);
187 else 192 else
188 scan_tree(expand_macros(value), repo_config); 193 scan_tree(expand_macros(value), repo_config);
189 else if (!strcmp(name, "source-filter")) 194 else if (!strcmp(name, "source-filter"))
190 ctx.cfg.source_filter = new_filter(value, 1); 195 ctx.cfg.source_filter = new_filter(value, 1);
191 else if (!strcmp(name, "summary-log")) 196 else if (!strcmp(name, "summary-log"))
192 ctx.cfg.summary_log = atoi(value); 197 ctx.cfg.summary_log = atoi(value);
193 else if (!strcmp(name, "summary-branches")) 198 else if (!strcmp(name, "summary-branches"))
194 ctx.cfg.summary_branches = atoi(value); 199 ctx.cfg.summary_branches = atoi(value);
195 else if (!strcmp(name, "summary-tags")) 200 else if (!strcmp(name, "summary-tags"))
196 ctx.cfg.summary_tags = atoi(value); 201 ctx.cfg.summary_tags = atoi(value);
197 else if (!strcmp(name, "side-by-side-diffs")) 202 else if (!strcmp(name, "side-by-side-diffs"))
198 ctx.cfg.ssdiff = atoi(value); 203 ctx.cfg.ssdiff = atoi(value);
199 else if (!strcmp(name, "agefile")) 204 else if (!strcmp(name, "agefile"))
200 ctx.cfg.agefile = xstrdup(value); 205 ctx.cfg.agefile = xstrdup(value);
201 else if (!strcmp(name, "renamelimit")) 206 else if (!strcmp(name, "renamelimit"))
202 ctx.cfg.renamelimit = atoi(value); 207 ctx.cfg.renamelimit = atoi(value);
203 else if (!strcmp(name, "robots")) 208 else if (!strcmp(name, "robots"))
204 ctx.cfg.robots = xstrdup(value); 209 ctx.cfg.robots = xstrdup(value);
205 else if (!strcmp(name, "clone-prefix")) 210 else if (!strcmp(name, "clone-prefix"))
206 ctx.cfg.clone_prefix = xstrdup(value); 211 ctx.cfg.clone_prefix = xstrdup(value);
207 else if (!strcmp(name, "local-time")) 212 else if (!strcmp(name, "local-time"))
208 ctx.cfg.local_time = atoi(value); 213 ctx.cfg.local_time = atoi(value);
209 else if (!prefixcmp(name, "mimetype.")) 214 else if (!prefixcmp(name, "mimetype."))
210 add_mimetype(name + 9, value); 215 add_mimetype(name + 9, value);
@@ -274,48 +279,49 @@ static void prepare_context(struct cgit_context *ctx)
274{ 279{
275 memset(ctx, 0, sizeof(*ctx)); 280 memset(ctx, 0, sizeof(*ctx));
276 ctx->cfg.agefile = "info/web/last-modified"; 281 ctx->cfg.agefile = "info/web/last-modified";
277 ctx->cfg.nocache = 0; 282 ctx->cfg.nocache = 0;
278 ctx->cfg.cache_size = 0; 283 ctx->cfg.cache_size = 0;
279 ctx->cfg.cache_dynamic_ttl = 5; 284 ctx->cfg.cache_dynamic_ttl = 5;
280 ctx->cfg.cache_max_create_time = 5; 285 ctx->cfg.cache_max_create_time = 5;
281 ctx->cfg.cache_repo_ttl = 5; 286 ctx->cfg.cache_repo_ttl = 5;
282 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 287 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
283 ctx->cfg.cache_root_ttl = 5; 288 ctx->cfg.cache_root_ttl = 5;
284 ctx->cfg.cache_scanrc_ttl = 15; 289 ctx->cfg.cache_scanrc_ttl = 15;
285 ctx->cfg.cache_static_ttl = -1; 290 ctx->cfg.cache_static_ttl = -1;
286 ctx->cfg.css = "/cgit.css"; 291 ctx->cfg.css = "/cgit.css";
287 ctx->cfg.logo = "/cgit.png"; 292 ctx->cfg.logo = "/cgit.png";
288 ctx->cfg.local_time = 0; 293 ctx->cfg.local_time = 0;
289 ctx->cfg.enable_tree_linenumbers = 1; 294 ctx->cfg.enable_tree_linenumbers = 1;
290 ctx->cfg.max_repo_count = 50; 295 ctx->cfg.max_repo_count = 50;
291 ctx->cfg.max_commit_count = 50; 296 ctx->cfg.max_commit_count = 50;
292 ctx->cfg.max_lock_attempts = 5; 297 ctx->cfg.max_lock_attempts = 5;
293 ctx->cfg.max_msg_len = 80; 298 ctx->cfg.max_msg_len = 80;
294 ctx->cfg.max_repodesc_len = 80; 299 ctx->cfg.max_repodesc_len = 80;
295 ctx->cfg.max_blob_size = 0; 300 ctx->cfg.max_blob_size = 0;
296 ctx->cfg.max_stats = 0; 301 ctx->cfg.max_stats = 0;
297 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 302 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
303 ctx->cfg.project_list = NULL;
298 ctx->cfg.renamelimit = -1; 304 ctx->cfg.renamelimit = -1;
299 ctx->cfg.robots = "index, nofollow"; 305 ctx->cfg.robots = "index, nofollow";
300 ctx->cfg.root_title = "Git repository browser"; 306 ctx->cfg.root_title = "Git repository browser";
301 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 307 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
302 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 308 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
303 ctx->cfg.section = ""; 309 ctx->cfg.section = "";
304 ctx->cfg.summary_branches = 10; 310 ctx->cfg.summary_branches = 10;
305 ctx->cfg.summary_log = 10; 311 ctx->cfg.summary_log = 10;
306 ctx->cfg.summary_tags = 10; 312 ctx->cfg.summary_tags = 10;
307 ctx->cfg.max_atom_items = 10; 313 ctx->cfg.max_atom_items = 10;
308 ctx->cfg.ssdiff = 0; 314 ctx->cfg.ssdiff = 0;
309 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 315 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
310 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 316 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
311 ctx->env.https = xstrdupn(getenv("HTTPS")); 317 ctx->env.https = xstrdupn(getenv("HTTPS"));
312 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 318 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
313 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 319 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
314 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 320 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
315 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 321 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
316 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 322 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
317 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 323 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
318 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 324 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
319 ctx->page.mimetype = "text/html"; 325 ctx->page.mimetype = "text/html";
320 ctx->page.charset = PAGE_ENCODING; 326 ctx->page.charset = PAGE_ENCODING;
321 ctx->page.filename = NULL; 327 ctx->page.filename = NULL;
@@ -553,73 +559,84 @@ void print_repolist(FILE *f, struct cgit_repolist *list, int start)
553 print_repo(f, &list->repos[i]); 559 print_repo(f, &list->repos[i]);
554} 560}
555 561
556/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 562/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
557 * and return 0 on success. 563 * and return 0 on success.
558 */ 564 */
559static int generate_cached_repolist(const char *path, const char *cached_rc) 565static int generate_cached_repolist(const char *path, const char *cached_rc)
560{ 566{
561 char *locked_rc; 567 char *locked_rc;
562 int idx; 568 int idx;
563 FILE *f; 569 FILE *f;
564 570
565 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 571 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
566 f = fopen(locked_rc, "wx"); 572 f = fopen(locked_rc, "wx");
567 if (!f) { 573 if (!f) {
568 /* Inform about the error unless the lockfile already existed, 574 /* Inform about the error unless the lockfile already existed,
569 * since that only means we've got concurrent requests. 575 * since that only means we've got concurrent requests.
570 */ 576 */
571 if (errno != EEXIST) 577 if (errno != EEXIST)
572 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 578 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
573 locked_rc, strerror(errno), errno); 579 locked_rc, strerror(errno), errno);
574 return errno; 580 return errno;
575 } 581 }
576 idx = cgit_repolist.count; 582 idx = cgit_repolist.count;
583 if (ctx.cfg.project_list)
584 scan_projects(path, ctx.cfg.project_list, repo_config);
585 else
577 scan_tree(path, repo_config); 586 scan_tree(path, repo_config);
578 print_repolist(f, &cgit_repolist, idx); 587 print_repolist(f, &cgit_repolist, idx);
579 if (rename(locked_rc, cached_rc)) 588 if (rename(locked_rc, cached_rc))
580 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 589 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
581 locked_rc, cached_rc, strerror(errno), errno); 590 locked_rc, cached_rc, strerror(errno), errno);
582 fclose(f); 591 fclose(f);
583 return 0; 592 return 0;
584} 593}
585 594
586static void process_cached_repolist(const char *path) 595static void process_cached_repolist(const char *path)
587{ 596{
588 struct stat st; 597 struct stat st;
589 char *cached_rc; 598 char *cached_rc;
590 time_t age; 599 time_t age;
600 unsigned long hash;
591 601
592 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 602 hash = hash_str(path);
593 hash_str(path))); 603 if (ctx.cfg.project_list)
604 hash += hash_str(ctx.cfg.project_list);
605 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, hash));
594 606
595 if (stat(cached_rc, &st)) { 607 if (stat(cached_rc, &st)) {
596 /* Nothing is cached, we need to scan without forking. And 608 /* Nothing is cached, we need to scan without forking. And
597 * if we fail to generate a cached repolist, we need to 609 * if we fail to generate a cached repolist, we need to
598 * invoke scan_tree manually. 610 * invoke scan_tree manually.
599 */ 611 */
600 if (generate_cached_repolist(path, cached_rc)) 612 if (generate_cached_repolist(path, cached_rc)) {
613 if (ctx.cfg.project_list)
614 scan_projects(path, ctx.cfg.project_list,
615 repo_config);
616 else
601 scan_tree(path, repo_config); 617 scan_tree(path, repo_config);
618 }
602 return; 619 return;
603 } 620 }
604 621
605 parse_configfile(cached_rc, config_cb); 622 parse_configfile(cached_rc, config_cb);
606 623
607 /* If the cached configfile hasn't expired, lets exit now */ 624 /* If the cached configfile hasn't expired, lets exit now */
608 age = time(NULL) - st.st_mtime; 625 age = time(NULL) - st.st_mtime;
609 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 626 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
610 return; 627 return;
611 628
612 /* The cached repolist has been parsed, but it was old. So lets 629 /* The cached repolist has been parsed, but it was old. So lets
613 * rescan the specified path and generate a new cached repolist 630 * rescan the specified path and generate a new cached repolist
614 * in a child-process to avoid latency for the current request. 631 * in a child-process to avoid latency for the current request.
615 */ 632 */
616 if (fork()) 633 if (fork())
617 return; 634 return;
618 635
619 exit(generate_cached_repolist(path, cached_rc)); 636 exit(generate_cached_repolist(path, cached_rc));
620} 637}
621 638
622static void cgit_parse_args(int argc, const char **argv) 639static void cgit_parse_args(int argc, const char **argv)
623{ 640{
624 int i; 641 int i;
625 int scan = 0; 642 int scan = 0;
diff --git a/cgit.h b/cgit.h
index e9e2718..4591f8c 100644
--- a/cgit.h
+++ b/cgit.h
@@ -145,48 +145,49 @@ struct cgit_query {
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 *robots; 170 char *robots;
170 char *root_title; 171 char *root_title;
171 char *root_desc; 172 char *root_desc;
172 char *root_readme; 173 char *root_readme;
173 char *script_name; 174 char *script_name;
174 char *section; 175 char *section;
175 char *virtual_root; 176 char *virtual_root;
176 int cache_size; 177 int cache_size;
177 int cache_dynamic_ttl; 178 int cache_dynamic_ttl;
178 int cache_max_create_time; 179 int cache_max_create_time;
179 int cache_repo_ttl; 180 int cache_repo_ttl;
180 int cache_root_ttl; 181 int cache_root_ttl;
181 int cache_scanrc_ttl; 182 int cache_scanrc_ttl;
182 int cache_static_ttl; 183 int cache_static_ttl;
183 int embedded; 184 int embedded;
184 int enable_filter_overrides; 185 int enable_filter_overrides;
185 int enable_index_links; 186 int enable_index_links;
186 int enable_log_filecount; 187 int enable_log_filecount;
187 int enable_log_linecount; 188 int enable_log_linecount;
188 int enable_remote_branches; 189 int enable_remote_branches;
189 int enable_subject_links; 190 int enable_subject_links;
190 int enable_tree_linenumbers; 191 int enable_tree_linenumbers;
191 int local_time; 192 int local_time;
192 int max_atom_items; 193 int max_atom_items;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index a853522..ec004d4 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -203,78 +203,86 @@ 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::
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
230 scan-path. Default value: none. See also: scan-path.
231
227renamelimit:: 232renamelimit::
228 Maximum number of files to consider when detecting renames. The value 233 Maximum number of files to consider when detecting renames. The value
229 "-1" uses the compiletime value in git (for further info, look at 234 "-1" uses the compiletime value in git (for further info, look at
230 `man git-diff`). Default value: "-1". 235 `man git-diff`). Default value: "-1".
231 236
232repo.group:: 237repo.group::
233 Legacy alias for "section". This option is deprecated and will not be 238 Legacy alias for "section". This option is deprecated and will not be
234 supported in cgit-1.0. 239 supported in cgit-1.0.
235 240
236robots:: 241robots::
237 Text used as content for the "robots" meta-tag. Default value: 242 Text used as content for the "robots" meta-tag. Default value:
238 "index, nofollow". 243 "index, nofollow".
239 244
240root-desc:: 245root-desc::
241 Text printed below the heading on the repository index page. Default 246 Text printed below the heading on the repository index page. Default
242 value: "a fast webinterface for the git dscm". 247 value: "a fast webinterface for the git dscm".
243 248
244root-readme:: 249root-readme::
245 The content of the file specified with this option will be included 250 The content of the file specified with this option will be included
246 verbatim below the "about" link on the repository index page. Default 251 verbatim below the "about" link on the repository index page. Default
247 value: none. 252 value: none.
248 253
249root-title:: 254root-title::
250 Text printed as heading on the repository index page. Default value: 255 Text printed as heading on the repository index page. Default value:
251 "Git Repository Browser". 256 "Git Repository Browser".
252 257
253scan-path:: 258scan-path::
254 A path which will be scanned for repositories. If caching is enabled, 259 A path which will be scanned for repositories. If caching is enabled,
255 the result will be cached as a cgitrc include-file in the cache 260 the result will be cached as a cgitrc include-file in the cache
256 directory. Default value: none. See also: cache-scanrc-ttl. 261 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
263 project-list. Default value: none. See also: cache-scanrc-ttl,
264 project-list.
257 265
258section:: 266section::
259 The name of the current repository section - all repositories defined 267 The name of the current repository section - all repositories defined
260 after this option will inherit the current section name. Default value: 268 after this option will inherit the current section name. Default value:
261 none. 269 none.
262 270
263side-by-side-diffs:: 271side-by-side-diffs::
264 If set to "1" shows side-by-side diffs instead of unidiffs per 272 If set to "1" shows side-by-side diffs instead of unidiffs per
265 default. Default value: "0". 273 default. Default value: "0".
266 274
267snapshots:: 275snapshots::
268 Text which specifies the default set of snapshot formats generated by 276 Text which specifies the default set of snapshot formats generated by
269 cgit. The value is a space-separated list of zero or more of the 277 cgit. The value is a space-separated list of zero or more of the
270 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 278 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
271 279
272source-filter:: 280source-filter::
273 Specifies a command which will be invoked to format plaintext blobs 281 Specifies a command which will be invoked to format plaintext blobs
274 in the tree view. The command will get the blob content on its STDIN 282 in the tree view. The command will get the blob content on its STDIN
275 and the name of the blob as its only command line argument. The STDOUT 283 and the name of the blob as its only command line argument. The STDOUT
276 from the command will be included verbatim as the blob contents, i.e. 284 from the command will be included verbatim as the blob contents, i.e.
277 this can be used to implement e.g. syntax highlighting. Default value: 285 this can be used to implement e.g. syntax highlighting. Default value:
278 none. 286 none.
279 287
280summary-branches:: 288summary-branches::
diff --git a/scan-tree.c b/scan-tree.c
index 1e18f3c..9bf9b38 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -1,24 +1,33 @@
1/* scan-tree.c
2 *
3 * Copyright (C) 2008-2009 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
5 *
6 * Licensed under GNU General Public License v2
7 * (see COPYING for full license text)
8 */
9
1#include "cgit.h" 10#include "cgit.h"
2#include "configfile.h" 11#include "configfile.h"
3#include "html.h" 12#include "html.h"
4 13
5#define MAX_PATH 4096 14#define MAX_PATH 4096
6 15
7/* return 1 if path contains a objects/ directory and a HEAD file */ 16/* return 1 if path contains a objects/ directory and a HEAD file */
8static int is_git_dir(const char *path) 17static int is_git_dir(const char *path)
9{ 18{
10 struct stat st; 19 struct stat st;
11 static char buf[MAX_PATH]; 20 static char buf[MAX_PATH];
12 21
13 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) { 22 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) {
14 fprintf(stderr, "Insanely long path: %s\n", path); 23 fprintf(stderr, "Insanely long path: %s\n", path);
15 return 0; 24 return 0;
16 } 25 }
17 if (stat(buf, &st)) { 26 if (stat(buf, &st)) {
18 if (errno != ENOENT) 27 if (errno != ENOENT)
19 fprintf(stderr, "Error checking path %s: %s (%d)\n", 28 fprintf(stderr, "Error checking path %s: %s (%d)\n",
20 path, strerror(errno), errno); 29 path, strerror(errno), errno);
21 return 0; 30 return 0;
22 } 31 }
23 if (!S_ISDIR(st.st_mode)) 32 if (!S_ISDIR(st.st_mode))
24 return 0; 33 return 0;
@@ -121,28 +130,56 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn)
121 continue; 130 continue;
122 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 131 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
123 continue; 132 continue;
124 } 133 }
125 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 134 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
126 if (!buf) { 135 if (!buf) {
127 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 136 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
128 path, strerror(errno), errno); 137 path, strerror(errno), errno);
129 exit(1); 138 exit(1);
130 } 139 }
131 sprintf(buf, "%s/%s", path, ent->d_name); 140 sprintf(buf, "%s/%s", path, ent->d_name);
132 if (stat(buf, &st)) { 141 if (stat(buf, &st)) {
133 fprintf(stderr, "Error checking path %s: %s (%d)\n", 142 fprintf(stderr, "Error checking path %s: %s (%d)\n",
134 buf, strerror(errno), errno); 143 buf, strerror(errno), errno);
135 free(buf); 144 free(buf);
136 continue; 145 continue;
137 } 146 }
138 if (S_ISDIR(st.st_mode)) 147 if (S_ISDIR(st.st_mode))
139 scan_path(base, buf, fn); 148 scan_path(base, buf, fn);
140 free(buf); 149 free(buf);
141 } 150 }
142 closedir(dir); 151 closedir(dir);
143} 152}
144 153
154#define lastc(s) s[strlen(s) - 1]
155
156void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
157{
158 char line[MAX_PATH * 2], *z;
159 FILE *projects;
160 int err;
161
162 projects = fopen(projectsfile, "r");
163 if (!projects) {
164 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
165 projectsfile, strerror(errno), errno);
166 }
167 while (fgets(line, sizeof(line), projects) != NULL) {
168 for (z = &lastc(line);
169 strlen(line) && strchr("\n\r", *z);
170 z = &lastc(line))
171 *z = '\0';
172 if (strlen(line))
173 scan_path(path, fmt("%s/%s", path, line), fn);
174 }
175 if ((err = ferror(projects))) {
176 fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
177 projectsfile, strerror(err), err);
178 }
179 fclose(projects);
180}
181
145void scan_tree(const char *path, repo_config_fn fn) 182void scan_tree(const char *path, repo_config_fn fn)
146{ 183{
147 scan_path(path, path, fn); 184 scan_path(path, path, fn);
148} 185}
diff --git a/scan-tree.h b/scan-tree.h
index 11539f4..1afbd4b 100644
--- a/scan-tree.h
+++ b/scan-tree.h
@@ -1,3 +1,2 @@
1 1extern void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn);
2
3extern void scan_tree(const char *path, repo_config_fn fn); 2extern void scan_tree(const char *path, repo_config_fn fn);