summaryrefslogtreecommitdiffabout
authorFelix Hanley <felix@seconddrawer.com.au>2010-11-08 18:41:13 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2010-11-08 18:41:13 (UTC)
commite0c6f23789e4893781bcd5af2281d468991ccf3a (patch) (unidiff)
tree1cffe4d35b6404dd59cd0f0a314e11944e37d0f2
parent49d09d4fd1b0cab8189cf1809a1b1241f54c2e48 (diff)
downloadcgit-e0c6f23789e4893781bcd5af2281d468991ccf3a.zip
cgit-e0c6f23789e4893781bcd5af2281d468991ccf3a.tar.gz
cgit-e0c6f23789e4893781bcd5af2281d468991ccf3a.tar.bz2
Add `strict-export` option
This option is used to specify a filename which needs to be present in the repositories found during `scan-path` processing. By setting this option to 'git-daemon-export-ok', only repositories explicitly marked for git daemon export will be included in the cgit configuration. Signed-off-by: Felix Hanley <felix@seconddrawer.com.au> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt7
-rw-r--r--scan-tree.c4
4 files changed, 14 insertions, 0 deletions
diff --git a/cgit.c b/cgit.c
index 96900bb..412fbf0 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,251 +1,253 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com> 4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
5 * 5 *
6 * Licensed under GNU General Public License v2 6 * Licensed under GNU General Public License v2
7 * (see COPYING for full license text) 7 * (see COPYING for full license text)
8 */ 8 */
9 9
10#include "cgit.h" 10#include "cgit.h"
11#include "cache.h" 11#include "cache.h"
12#include "cmd.h" 12#include "cmd.h"
13#include "configfile.h" 13#include "configfile.h"
14#include "html.h" 14#include "html.h"
15#include "ui-shared.h" 15#include "ui-shared.h"
16#include "ui-stats.h" 16#include "ui-stats.h"
17#include "scan-tree.h" 17#include "scan-tree.h"
18 18
19const char *cgit_version = CGIT_VERSION; 19const char *cgit_version = CGIT_VERSION;
20 20
21void add_mimetype(const char *name, const char *value) 21void add_mimetype(const char *name, const char *value)
22{ 22{
23 struct string_list_item *item; 23 struct string_list_item *item;
24 24
25 item = string_list_insert(&ctx.cfg.mimetypes, xstrdup(name)); 25 item = string_list_insert(&ctx.cfg.mimetypes, xstrdup(name));
26 item->util = xstrdup(value); 26 item->util = xstrdup(value);
27} 27}
28 28
29struct cgit_filter *new_filter(const char *cmd, int extra_args) 29struct cgit_filter *new_filter(const char *cmd, int extra_args)
30{ 30{
31 struct cgit_filter *f; 31 struct cgit_filter *f;
32 32
33 if (!cmd || !cmd[0]) 33 if (!cmd || !cmd[0])
34 return NULL; 34 return NULL;
35 35
36 f = xmalloc(sizeof(struct cgit_filter)); 36 f = xmalloc(sizeof(struct cgit_filter));
37 f->cmd = xstrdup(cmd); 37 f->cmd = xstrdup(cmd);
38 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 38 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
39 f->argv[0] = f->cmd; 39 f->argv[0] = f->cmd;
40 f->argv[1] = NULL; 40 f->argv[1] = NULL;
41 return f; 41 return f;
42} 42}
43 43
44static void process_cached_repolist(const char *path); 44static void process_cached_repolist(const char *path);
45 45
46void repo_config(struct cgit_repo *repo, const char *name, const char *value) 46void repo_config(struct cgit_repo *repo, const char *name, const char *value)
47{ 47{
48 if (!strcmp(name, "name")) 48 if (!strcmp(name, "name"))
49 repo->name = xstrdup(value); 49 repo->name = xstrdup(value);
50 else if (!strcmp(name, "clone-url")) 50 else if (!strcmp(name, "clone-url"))
51 repo->clone_url = xstrdup(value); 51 repo->clone_url = xstrdup(value);
52 else if (!strcmp(name, "desc")) 52 else if (!strcmp(name, "desc"))
53 repo->desc = xstrdup(value); 53 repo->desc = xstrdup(value);
54 else if (!strcmp(name, "owner")) 54 else if (!strcmp(name, "owner"))
55 repo->owner = xstrdup(value); 55 repo->owner = xstrdup(value);
56 else if (!strcmp(name, "defbranch")) 56 else if (!strcmp(name, "defbranch"))
57 repo->defbranch = xstrdup(value); 57 repo->defbranch = xstrdup(value);
58 else if (!strcmp(name, "snapshots")) 58 else if (!strcmp(name, "snapshots"))
59 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 59 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
60 else if (!strcmp(name, "enable-log-filecount")) 60 else if (!strcmp(name, "enable-log-filecount"))
61 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 61 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
62 else if (!strcmp(name, "enable-log-linecount")) 62 else if (!strcmp(name, "enable-log-linecount"))
63 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 63 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
64 else if (!strcmp(name, "enable-remote-branches")) 64 else if (!strcmp(name, "enable-remote-branches"))
65 repo->enable_remote_branches = atoi(value); 65 repo->enable_remote_branches = atoi(value);
66 else if (!strcmp(name, "enable-subject-links")) 66 else if (!strcmp(name, "enable-subject-links"))
67 repo->enable_subject_links = atoi(value); 67 repo->enable_subject_links = atoi(value);
68 else if (!strcmp(name, "max-stats")) 68 else if (!strcmp(name, "max-stats"))
69 repo->max_stats = cgit_find_stats_period(value, NULL); 69 repo->max_stats = cgit_find_stats_period(value, NULL);
70 else if (!strcmp(name, "module-link")) 70 else if (!strcmp(name, "module-link"))
71 repo->module_link= xstrdup(value); 71 repo->module_link= xstrdup(value);
72 else if (!strcmp(name, "section")) 72 else if (!strcmp(name, "section"))
73 repo->section = xstrdup(value); 73 repo->section = xstrdup(value);
74 else if (!strcmp(name, "readme") && value != NULL) { 74 else if (!strcmp(name, "readme") && value != NULL) {
75 repo->readme = xstrdup(value); 75 repo->readme = xstrdup(value);
76 } else if (ctx.cfg.enable_filter_overrides) { 76 } else if (ctx.cfg.enable_filter_overrides) {
77 if (!strcmp(name, "about-filter")) 77 if (!strcmp(name, "about-filter"))
78 repo->about_filter = new_filter(value, 0); 78 repo->about_filter = new_filter(value, 0);
79 else if (!strcmp(name, "commit-filter")) 79 else if (!strcmp(name, "commit-filter"))
80 repo->commit_filter = new_filter(value, 0); 80 repo->commit_filter = new_filter(value, 0);
81 else if (!strcmp(name, "source-filter")) 81 else if (!strcmp(name, "source-filter"))
82 repo->source_filter = new_filter(value, 1); 82 repo->source_filter = new_filter(value, 1);
83 } 83 }
84} 84}
85 85
86void config_cb(const char *name, const char *value) 86void config_cb(const char *name, const char *value)
87{ 87{
88 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 88 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
89 ctx.cfg.section = xstrdup(value); 89 ctx.cfg.section = xstrdup(value);
90 else if (!strcmp(name, "repo.url")) 90 else if (!strcmp(name, "repo.url"))
91 ctx.repo = cgit_add_repo(value); 91 ctx.repo = cgit_add_repo(value);
92 else if (ctx.repo && !strcmp(name, "repo.path")) 92 else if (ctx.repo && !strcmp(name, "repo.path"))
93 ctx.repo->path = trim_end(value, '/'); 93 ctx.repo->path = trim_end(value, '/');
94 else if (ctx.repo && !prefixcmp(name, "repo.")) 94 else if (ctx.repo && !prefixcmp(name, "repo."))
95 repo_config(ctx.repo, name + 5, value); 95 repo_config(ctx.repo, name + 5, value);
96 else if (!strcmp(name, "readme")) 96 else if (!strcmp(name, "readme"))
97 ctx.cfg.readme = xstrdup(value); 97 ctx.cfg.readme = xstrdup(value);
98 else if (!strcmp(name, "root-title")) 98 else if (!strcmp(name, "root-title"))
99 ctx.cfg.root_title = xstrdup(value); 99 ctx.cfg.root_title = xstrdup(value);
100 else if (!strcmp(name, "root-desc")) 100 else if (!strcmp(name, "root-desc"))
101 ctx.cfg.root_desc = xstrdup(value); 101 ctx.cfg.root_desc = xstrdup(value);
102 else if (!strcmp(name, "root-readme")) 102 else if (!strcmp(name, "root-readme"))
103 ctx.cfg.root_readme = xstrdup(value); 103 ctx.cfg.root_readme = xstrdup(value);
104 else if (!strcmp(name, "css")) 104 else if (!strcmp(name, "css"))
105 ctx.cfg.css = xstrdup(value); 105 ctx.cfg.css = xstrdup(value);
106 else if (!strcmp(name, "favicon")) 106 else if (!strcmp(name, "favicon"))
107 ctx.cfg.favicon = xstrdup(value); 107 ctx.cfg.favicon = xstrdup(value);
108 else if (!strcmp(name, "footer")) 108 else if (!strcmp(name, "footer"))
109 ctx.cfg.footer = xstrdup(value); 109 ctx.cfg.footer = xstrdup(value);
110 else if (!strcmp(name, "head-include")) 110 else if (!strcmp(name, "head-include"))
111 ctx.cfg.head_include = xstrdup(value); 111 ctx.cfg.head_include = xstrdup(value);
112 else if (!strcmp(name, "header")) 112 else if (!strcmp(name, "header"))
113 ctx.cfg.header = xstrdup(value); 113 ctx.cfg.header = xstrdup(value);
114 else if (!strcmp(name, "logo")) 114 else if (!strcmp(name, "logo"))
115 ctx.cfg.logo = xstrdup(value); 115 ctx.cfg.logo = xstrdup(value);
116 else if (!strcmp(name, "index-header")) 116 else if (!strcmp(name, "index-header"))
117 ctx.cfg.index_header = xstrdup(value); 117 ctx.cfg.index_header = xstrdup(value);
118 else if (!strcmp(name, "index-info")) 118 else if (!strcmp(name, "index-info"))
119 ctx.cfg.index_info = xstrdup(value); 119 ctx.cfg.index_info = xstrdup(value);
120 else if (!strcmp(name, "logo-link")) 120 else if (!strcmp(name, "logo-link"))
121 ctx.cfg.logo_link = xstrdup(value); 121 ctx.cfg.logo_link = xstrdup(value);
122 else if (!strcmp(name, "module-link")) 122 else if (!strcmp(name, "module-link"))
123 ctx.cfg.module_link = xstrdup(value); 123 ctx.cfg.module_link = xstrdup(value);
124 else if (!strcmp(name, "strict-export"))
125 ctx.cfg.strict_export = xstrdup(value);
124 else if (!strcmp(name, "virtual-root")) { 126 else if (!strcmp(name, "virtual-root")) {
125 ctx.cfg.virtual_root = trim_end(value, '/'); 127 ctx.cfg.virtual_root = trim_end(value, '/');
126 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 128 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
127 ctx.cfg.virtual_root = ""; 129 ctx.cfg.virtual_root = "";
128 } else if (!strcmp(name, "nocache")) 130 } else if (!strcmp(name, "nocache"))
129 ctx.cfg.nocache = atoi(value); 131 ctx.cfg.nocache = atoi(value);
130 else if (!strcmp(name, "noplainemail")) 132 else if (!strcmp(name, "noplainemail"))
131 ctx.cfg.noplainemail = atoi(value); 133 ctx.cfg.noplainemail = atoi(value);
132 else if (!strcmp(name, "noheader")) 134 else if (!strcmp(name, "noheader"))
133 ctx.cfg.noheader = atoi(value); 135 ctx.cfg.noheader = atoi(value);
134 else if (!strcmp(name, "snapshots")) 136 else if (!strcmp(name, "snapshots"))
135 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 137 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
136 else if (!strcmp(name, "enable-filter-overrides")) 138 else if (!strcmp(name, "enable-filter-overrides"))
137 ctx.cfg.enable_filter_overrides = atoi(value); 139 ctx.cfg.enable_filter_overrides = atoi(value);
138 else if (!strcmp(name, "enable-gitweb-owner")) 140 else if (!strcmp(name, "enable-gitweb-owner"))
139 ctx.cfg.enable_gitweb_owner = atoi(value); 141 ctx.cfg.enable_gitweb_owner = atoi(value);
140 else if (!strcmp(name, "enable-index-links")) 142 else if (!strcmp(name, "enable-index-links"))
141 ctx.cfg.enable_index_links = atoi(value); 143 ctx.cfg.enable_index_links = atoi(value);
142 else if (!strcmp(name, "enable-log-filecount")) 144 else if (!strcmp(name, "enable-log-filecount"))
143 ctx.cfg.enable_log_filecount = atoi(value); 145 ctx.cfg.enable_log_filecount = atoi(value);
144 else if (!strcmp(name, "enable-log-linecount")) 146 else if (!strcmp(name, "enable-log-linecount"))
145 ctx.cfg.enable_log_linecount = atoi(value); 147 ctx.cfg.enable_log_linecount = atoi(value);
146 else if (!strcmp(name, "enable-remote-branches")) 148 else if (!strcmp(name, "enable-remote-branches"))
147 ctx.cfg.enable_remote_branches = atoi(value); 149 ctx.cfg.enable_remote_branches = atoi(value);
148 else if (!strcmp(name, "enable-subject-links")) 150 else if (!strcmp(name, "enable-subject-links"))
149 ctx.cfg.enable_subject_links = atoi(value); 151 ctx.cfg.enable_subject_links = atoi(value);
150 else if (!strcmp(name, "enable-tree-linenumbers")) 152 else if (!strcmp(name, "enable-tree-linenumbers"))
151 ctx.cfg.enable_tree_linenumbers = atoi(value); 153 ctx.cfg.enable_tree_linenumbers = atoi(value);
152 else if (!strcmp(name, "max-stats")) 154 else if (!strcmp(name, "max-stats"))
153 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 155 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
154 else if (!strcmp(name, "cache-size")) 156 else if (!strcmp(name, "cache-size"))
155 ctx.cfg.cache_size = atoi(value); 157 ctx.cfg.cache_size = atoi(value);
156 else if (!strcmp(name, "cache-root")) 158 else if (!strcmp(name, "cache-root"))
157 ctx.cfg.cache_root = xstrdup(expand_macros(value)); 159 ctx.cfg.cache_root = xstrdup(expand_macros(value));
158 else if (!strcmp(name, "cache-root-ttl")) 160 else if (!strcmp(name, "cache-root-ttl"))
159 ctx.cfg.cache_root_ttl = atoi(value); 161 ctx.cfg.cache_root_ttl = atoi(value);
160 else if (!strcmp(name, "cache-repo-ttl")) 162 else if (!strcmp(name, "cache-repo-ttl"))
161 ctx.cfg.cache_repo_ttl = atoi(value); 163 ctx.cfg.cache_repo_ttl = atoi(value);
162 else if (!strcmp(name, "cache-scanrc-ttl")) 164 else if (!strcmp(name, "cache-scanrc-ttl"))
163 ctx.cfg.cache_scanrc_ttl = atoi(value); 165 ctx.cfg.cache_scanrc_ttl = atoi(value);
164 else if (!strcmp(name, "cache-static-ttl")) 166 else if (!strcmp(name, "cache-static-ttl"))
165 ctx.cfg.cache_static_ttl = atoi(value); 167 ctx.cfg.cache_static_ttl = atoi(value);
166 else if (!strcmp(name, "cache-dynamic-ttl")) 168 else if (!strcmp(name, "cache-dynamic-ttl"))
167 ctx.cfg.cache_dynamic_ttl = atoi(value); 169 ctx.cfg.cache_dynamic_ttl = atoi(value);
168 else if (!strcmp(name, "about-filter")) 170 else if (!strcmp(name, "about-filter"))
169 ctx.cfg.about_filter = new_filter(value, 0); 171 ctx.cfg.about_filter = new_filter(value, 0);
170 else if (!strcmp(name, "commit-filter")) 172 else if (!strcmp(name, "commit-filter"))
171 ctx.cfg.commit_filter = new_filter(value, 0); 173 ctx.cfg.commit_filter = new_filter(value, 0);
172 else if (!strcmp(name, "embedded")) 174 else if (!strcmp(name, "embedded"))
173 ctx.cfg.embedded = atoi(value); 175 ctx.cfg.embedded = atoi(value);
174 else if (!strcmp(name, "max-atom-items")) 176 else if (!strcmp(name, "max-atom-items"))
175 ctx.cfg.max_atom_items = atoi(value); 177 ctx.cfg.max_atom_items = atoi(value);
176 else if (!strcmp(name, "max-message-length")) 178 else if (!strcmp(name, "max-message-length"))
177 ctx.cfg.max_msg_len = atoi(value); 179 ctx.cfg.max_msg_len = atoi(value);
178 else if (!strcmp(name, "max-repodesc-length")) 180 else if (!strcmp(name, "max-repodesc-length"))
179 ctx.cfg.max_repodesc_len = atoi(value); 181 ctx.cfg.max_repodesc_len = atoi(value);
180 else if (!strcmp(name, "max-blob-size")) 182 else if (!strcmp(name, "max-blob-size"))
181 ctx.cfg.max_blob_size = atoi(value); 183 ctx.cfg.max_blob_size = atoi(value);
182 else if (!strcmp(name, "max-repo-count")) 184 else if (!strcmp(name, "max-repo-count"))
183 ctx.cfg.max_repo_count = atoi(value); 185 ctx.cfg.max_repo_count = atoi(value);
184 else if (!strcmp(name, "max-commit-count")) 186 else if (!strcmp(name, "max-commit-count"))
185 ctx.cfg.max_commit_count = atoi(value); 187 ctx.cfg.max_commit_count = atoi(value);
186 else if (!strcmp(name, "project-list")) 188 else if (!strcmp(name, "project-list"))
187 ctx.cfg.project_list = xstrdup(expand_macros(value)); 189 ctx.cfg.project_list = xstrdup(expand_macros(value));
188 else if (!strcmp(name, "scan-path")) 190 else if (!strcmp(name, "scan-path"))
189 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 191 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
190 process_cached_repolist(expand_macros(value)); 192 process_cached_repolist(expand_macros(value));
191 else if (ctx.cfg.project_list) 193 else if (ctx.cfg.project_list)
192 scan_projects(expand_macros(value), 194 scan_projects(expand_macros(value),
193 ctx.cfg.project_list, repo_config); 195 ctx.cfg.project_list, repo_config);
194 else 196 else
195 scan_tree(expand_macros(value), repo_config); 197 scan_tree(expand_macros(value), repo_config);
196 else if (!strcmp(name, "section-from-path")) 198 else if (!strcmp(name, "section-from-path"))
197 ctx.cfg.section_from_path = atoi(value); 199 ctx.cfg.section_from_path = atoi(value);
198 else if (!strcmp(name, "source-filter")) 200 else if (!strcmp(name, "source-filter"))
199 ctx.cfg.source_filter = new_filter(value, 1); 201 ctx.cfg.source_filter = new_filter(value, 1);
200 else if (!strcmp(name, "summary-log")) 202 else if (!strcmp(name, "summary-log"))
201 ctx.cfg.summary_log = atoi(value); 203 ctx.cfg.summary_log = atoi(value);
202 else if (!strcmp(name, "summary-branches")) 204 else if (!strcmp(name, "summary-branches"))
203 ctx.cfg.summary_branches = atoi(value); 205 ctx.cfg.summary_branches = atoi(value);
204 else if (!strcmp(name, "summary-tags")) 206 else if (!strcmp(name, "summary-tags"))
205 ctx.cfg.summary_tags = atoi(value); 207 ctx.cfg.summary_tags = atoi(value);
206 else if (!strcmp(name, "side-by-side-diffs")) 208 else if (!strcmp(name, "side-by-side-diffs"))
207 ctx.cfg.ssdiff = atoi(value); 209 ctx.cfg.ssdiff = atoi(value);
208 else if (!strcmp(name, "agefile")) 210 else if (!strcmp(name, "agefile"))
209 ctx.cfg.agefile = xstrdup(value); 211 ctx.cfg.agefile = xstrdup(value);
210 else if (!strcmp(name, "renamelimit")) 212 else if (!strcmp(name, "renamelimit"))
211 ctx.cfg.renamelimit = atoi(value); 213 ctx.cfg.renamelimit = atoi(value);
212 else if (!strcmp(name, "remove-suffix")) 214 else if (!strcmp(name, "remove-suffix"))
213 ctx.cfg.remove_suffix = atoi(value); 215 ctx.cfg.remove_suffix = atoi(value);
214 else if (!strcmp(name, "robots")) 216 else if (!strcmp(name, "robots"))
215 ctx.cfg.robots = xstrdup(value); 217 ctx.cfg.robots = xstrdup(value);
216 else if (!strcmp(name, "clone-prefix")) 218 else if (!strcmp(name, "clone-prefix"))
217 ctx.cfg.clone_prefix = xstrdup(value); 219 ctx.cfg.clone_prefix = xstrdup(value);
218 else if (!strcmp(name, "local-time")) 220 else if (!strcmp(name, "local-time"))
219 ctx.cfg.local_time = atoi(value); 221 ctx.cfg.local_time = atoi(value);
220 else if (!prefixcmp(name, "mimetype.")) 222 else if (!prefixcmp(name, "mimetype."))
221 add_mimetype(name + 9, value); 223 add_mimetype(name + 9, value);
222 else if (!strcmp(name, "include")) 224 else if (!strcmp(name, "include"))
223 parse_configfile(expand_macros(value), config_cb); 225 parse_configfile(expand_macros(value), config_cb);
224} 226}
225 227
226static void querystring_cb(const char *name, const char *value) 228static void querystring_cb(const char *name, const char *value)
227{ 229{
228 if (!value) 230 if (!value)
229 value = ""; 231 value = "";
230 232
231 if (!strcmp(name,"r")) { 233 if (!strcmp(name,"r")) {
232 ctx.qry.repo = xstrdup(value); 234 ctx.qry.repo = xstrdup(value);
233 ctx.repo = cgit_get_repoinfo(value); 235 ctx.repo = cgit_get_repoinfo(value);
234 } else if (!strcmp(name, "p")) { 236 } else if (!strcmp(name, "p")) {
235 ctx.qry.page = xstrdup(value); 237 ctx.qry.page = xstrdup(value);
236 } else if (!strcmp(name, "url")) { 238 } else if (!strcmp(name, "url")) {
237 if (*value == '/') 239 if (*value == '/')
238 value++; 240 value++;
239 ctx.qry.url = xstrdup(value); 241 ctx.qry.url = xstrdup(value);
240 cgit_parse_url(value); 242 cgit_parse_url(value);
241 } else if (!strcmp(name, "qt")) { 243 } else if (!strcmp(name, "qt")) {
242 ctx.qry.grep = xstrdup(value); 244 ctx.qry.grep = xstrdup(value);
243 } else if (!strcmp(name, "q")) { 245 } else if (!strcmp(name, "q")) {
244 ctx.qry.search = xstrdup(value); 246 ctx.qry.search = xstrdup(value);
245 } else if (!strcmp(name, "h")) { 247 } else if (!strcmp(name, "h")) {
246 ctx.qry.head = xstrdup(value); 248 ctx.qry.head = xstrdup(value);
247 ctx.qry.has_symref = 1; 249 ctx.qry.has_symref = 1;
248 } else if (!strcmp(name, "id")) { 250 } else if (!strcmp(name, "id")) {
249 ctx.qry.sha1 = xstrdup(value); 251 ctx.qry.sha1 = xstrdup(value);
250 ctx.qry.has_sha1 = 1; 252 ctx.qry.has_sha1 = 1;
251 } else if (!strcmp(name, "id2")) { 253 } else if (!strcmp(name, "id2")) {
diff --git a/cgit.h b/cgit.h
index 8f5dd2a..a9896cf 100644
--- a/cgit.h
+++ b/cgit.h
@@ -51,256 +51,257 @@ typedef void (*filepair_fn)(struct diff_filepair *pair);
51typedef void (*linediff_fn)(char *line, int len); 51typedef void (*linediff_fn)(char *line, int len);
52 52
53struct cgit_filter { 53struct cgit_filter {
54 char *cmd; 54 char *cmd;
55 char **argv; 55 char **argv;
56 int old_stdout; 56 int old_stdout;
57 int pipe_fh[2]; 57 int pipe_fh[2];
58 int pid; 58 int pid;
59 int exitstatus; 59 int exitstatus;
60}; 60};
61 61
62struct cgit_repo { 62struct cgit_repo {
63 char *url; 63 char *url;
64 char *name; 64 char *name;
65 char *path; 65 char *path;
66 char *desc; 66 char *desc;
67 char *owner; 67 char *owner;
68 char *defbranch; 68 char *defbranch;
69 char *module_link; 69 char *module_link;
70 char *readme; 70 char *readme;
71 char *section; 71 char *section;
72 char *clone_url; 72 char *clone_url;
73 int snapshots; 73 int snapshots;
74 int enable_log_filecount; 74 int enable_log_filecount;
75 int enable_log_linecount; 75 int enable_log_linecount;
76 int enable_remote_branches; 76 int enable_remote_branches;
77 int enable_subject_links; 77 int enable_subject_links;
78 int max_stats; 78 int max_stats;
79 time_t mtime; 79 time_t mtime;
80 struct cgit_filter *about_filter; 80 struct cgit_filter *about_filter;
81 struct cgit_filter *commit_filter; 81 struct cgit_filter *commit_filter;
82 struct cgit_filter *source_filter; 82 struct cgit_filter *source_filter;
83}; 83};
84 84
85typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 85typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
86 const char *value); 86 const char *value);
87 87
88struct cgit_repolist { 88struct cgit_repolist {
89 int length; 89 int length;
90 int count; 90 int count;
91 struct cgit_repo *repos; 91 struct cgit_repo *repos;
92}; 92};
93 93
94struct commitinfo { 94struct commitinfo {
95 struct commit *commit; 95 struct commit *commit;
96 char *author; 96 char *author;
97 char *author_email; 97 char *author_email;
98 unsigned long author_date; 98 unsigned long author_date;
99 char *committer; 99 char *committer;
100 char *committer_email; 100 char *committer_email;
101 unsigned long committer_date; 101 unsigned long committer_date;
102 char *subject; 102 char *subject;
103 char *msg; 103 char *msg;
104 char *msg_encoding; 104 char *msg_encoding;
105}; 105};
106 106
107struct taginfo { 107struct taginfo {
108 char *tagger; 108 char *tagger;
109 char *tagger_email; 109 char *tagger_email;
110 unsigned long tagger_date; 110 unsigned long tagger_date;
111 char *msg; 111 char *msg;
112}; 112};
113 113
114struct refinfo { 114struct refinfo {
115 const char *refname; 115 const char *refname;
116 struct object *object; 116 struct object *object;
117 union { 117 union {
118 struct taginfo *tag; 118 struct taginfo *tag;
119 struct commitinfo *commit; 119 struct commitinfo *commit;
120 }; 120 };
121}; 121};
122 122
123struct reflist { 123struct reflist {
124 struct refinfo **refs; 124 struct refinfo **refs;
125 int alloc; 125 int alloc;
126 int count; 126 int count;
127}; 127};
128 128
129struct cgit_query { 129struct cgit_query {
130 int has_symref; 130 int has_symref;
131 int has_sha1; 131 int has_sha1;
132 char *raw; 132 char *raw;
133 char *repo; 133 char *repo;
134 char *page; 134 char *page;
135 char *search; 135 char *search;
136 char *grep; 136 char *grep;
137 char *head; 137 char *head;
138 char *sha1; 138 char *sha1;
139 char *sha2; 139 char *sha2;
140 char *path; 140 char *path;
141 char *name; 141 char *name;
142 char *mimetype; 142 char *mimetype;
143 char *url; 143 char *url;
144 char *period; 144 char *period;
145 int ofs; 145 int ofs;
146 int nohead; 146 int nohead;
147 char *sort; 147 char *sort;
148 int showmsg; 148 int showmsg;
149 int ssdiff; 149 int ssdiff;
150 int show_all; 150 int show_all;
151 int context; 151 int context;
152 int ignorews; 152 int ignorews;
153 char *vpath; 153 char *vpath;
154}; 154};
155 155
156struct cgit_config { 156struct cgit_config {
157 char *agefile; 157 char *agefile;
158 char *cache_root; 158 char *cache_root;
159 char *clone_prefix; 159 char *clone_prefix;
160 char *css; 160 char *css;
161 char *favicon; 161 char *favicon;
162 char *footer; 162 char *footer;
163 char *head_include; 163 char *head_include;
164 char *header; 164 char *header;
165 char *index_header; 165 char *index_header;
166 char *index_info; 166 char *index_info;
167 char *logo; 167 char *logo;
168 char *logo_link; 168 char *logo_link;
169 char *module_link; 169 char *module_link;
170 char *project_list; 170 char *project_list;
171 char *readme; 171 char *readme;
172 char *robots; 172 char *robots;
173 char *root_title; 173 char *root_title;
174 char *root_desc; 174 char *root_desc;
175 char *root_readme; 175 char *root_readme;
176 char *script_name; 176 char *script_name;
177 char *section; 177 char *section;
178 char *virtual_root; 178 char *virtual_root;
179 char *strict_export;
179 int cache_size; 180 int cache_size;
180 int cache_dynamic_ttl; 181 int cache_dynamic_ttl;
181 int cache_max_create_time; 182 int cache_max_create_time;
182 int cache_repo_ttl; 183 int cache_repo_ttl;
183 int cache_root_ttl; 184 int cache_root_ttl;
184 int cache_scanrc_ttl; 185 int cache_scanrc_ttl;
185 int cache_static_ttl; 186 int cache_static_ttl;
186 int embedded; 187 int embedded;
187 int enable_filter_overrides; 188 int enable_filter_overrides;
188 int enable_gitweb_owner; 189 int enable_gitweb_owner;
189 int enable_index_links; 190 int enable_index_links;
190 int enable_log_filecount; 191 int enable_log_filecount;
191 int enable_log_linecount; 192 int enable_log_linecount;
192 int enable_remote_branches; 193 int enable_remote_branches;
193 int enable_subject_links; 194 int enable_subject_links;
194 int enable_tree_linenumbers; 195 int enable_tree_linenumbers;
195 int local_time; 196 int local_time;
196 int max_atom_items; 197 int max_atom_items;
197 int max_repo_count; 198 int max_repo_count;
198 int max_commit_count; 199 int max_commit_count;
199 int max_lock_attempts; 200 int max_lock_attempts;
200 int max_msg_len; 201 int max_msg_len;
201 int max_repodesc_len; 202 int max_repodesc_len;
202 int max_blob_size; 203 int max_blob_size;
203 int max_stats; 204 int max_stats;
204 int nocache; 205 int nocache;
205 int noplainemail; 206 int noplainemail;
206 int noheader; 207 int noheader;
207 int renamelimit; 208 int renamelimit;
208 int remove_suffix; 209 int remove_suffix;
209 int section_from_path; 210 int section_from_path;
210 int snapshots; 211 int snapshots;
211 int summary_branches; 212 int summary_branches;
212 int summary_log; 213 int summary_log;
213 int summary_tags; 214 int summary_tags;
214 int ssdiff; 215 int ssdiff;
215 struct string_list mimetypes; 216 struct string_list mimetypes;
216 struct cgit_filter *about_filter; 217 struct cgit_filter *about_filter;
217 struct cgit_filter *commit_filter; 218 struct cgit_filter *commit_filter;
218 struct cgit_filter *source_filter; 219 struct cgit_filter *source_filter;
219}; 220};
220 221
221struct cgit_page { 222struct cgit_page {
222 time_t modified; 223 time_t modified;
223 time_t expires; 224 time_t expires;
224 size_t size; 225 size_t size;
225 char *mimetype; 226 char *mimetype;
226 char *charset; 227 char *charset;
227 char *filename; 228 char *filename;
228 char *etag; 229 char *etag;
229 char *title; 230 char *title;
230 int status; 231 int status;
231 char *statusmsg; 232 char *statusmsg;
232}; 233};
233 234
234struct cgit_environment { 235struct cgit_environment {
235 char *cgit_config; 236 char *cgit_config;
236 char *http_host; 237 char *http_host;
237 char *https; 238 char *https;
238 char *no_http; 239 char *no_http;
239 char *path_info; 240 char *path_info;
240 char *query_string; 241 char *query_string;
241 char *request_method; 242 char *request_method;
242 char *script_name; 243 char *script_name;
243 char *server_name; 244 char *server_name;
244 char *server_port; 245 char *server_port;
245}; 246};
246 247
247struct cgit_context { 248struct cgit_context {
248 struct cgit_environment env; 249 struct cgit_environment env;
249 struct cgit_query qry; 250 struct cgit_query qry;
250 struct cgit_config cfg; 251 struct cgit_config cfg;
251 struct cgit_repo *repo; 252 struct cgit_repo *repo;
252 struct cgit_page page; 253 struct cgit_page page;
253}; 254};
254 255
255struct cgit_snapshot_format { 256struct cgit_snapshot_format {
256 const char *suffix; 257 const char *suffix;
257 const char *mimetype; 258 const char *mimetype;
258 write_archive_fn_t write_func; 259 write_archive_fn_t write_func;
259 int bit; 260 int bit;
260}; 261};
261 262
262extern const char *cgit_version; 263extern const char *cgit_version;
263 264
264extern struct cgit_repolist cgit_repolist; 265extern struct cgit_repolist cgit_repolist;
265extern struct cgit_context ctx; 266extern struct cgit_context ctx;
266extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 267extern const struct cgit_snapshot_format cgit_snapshot_formats[];
267 268
268extern struct cgit_repo *cgit_add_repo(const char *url); 269extern struct cgit_repo *cgit_add_repo(const char *url);
269extern struct cgit_repo *cgit_get_repoinfo(const char *url); 270extern struct cgit_repo *cgit_get_repoinfo(const char *url);
270extern void cgit_repo_config_cb(const char *name, const char *value); 271extern void cgit_repo_config_cb(const char *name, const char *value);
271 272
272extern int chk_zero(int result, char *msg); 273extern int chk_zero(int result, char *msg);
273extern int chk_positive(int result, char *msg); 274extern int chk_positive(int result, char *msg);
274extern int chk_non_negative(int result, char *msg); 275extern int chk_non_negative(int result, char *msg);
275 276
276extern char *trim_end(const char *str, char c); 277extern char *trim_end(const char *str, char c);
277extern char *strlpart(char *txt, int maxlen); 278extern char *strlpart(char *txt, int maxlen);
278extern char *strrpart(char *txt, int maxlen); 279extern char *strrpart(char *txt, int maxlen);
279 280
280extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 281extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
281extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 282extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
282 int flags, void *cb_data); 283 int flags, void *cb_data);
283 284
284extern void *cgit_free_commitinfo(struct commitinfo *info); 285extern void *cgit_free_commitinfo(struct commitinfo *info);
285 286
286extern int cgit_diff_files(const unsigned char *old_sha1, 287extern int cgit_diff_files(const unsigned char *old_sha1,
287 const unsigned char *new_sha1, 288 const unsigned char *new_sha1,
288 unsigned long *old_size, unsigned long *new_size, 289 unsigned long *old_size, unsigned long *new_size,
289 int *binary, int context, int ignorews, 290 int *binary, int context, int ignorews,
290 linediff_fn fn); 291 linediff_fn fn);
291 292
292extern void cgit_diff_tree(const unsigned char *old_sha1, 293extern void cgit_diff_tree(const unsigned char *old_sha1,
293 const unsigned char *new_sha1, 294 const unsigned char *new_sha1,
294 filepair_fn fn, const char *prefix, int ignorews); 295 filepair_fn fn, const char *prefix, int ignorews);
295 296
296extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 297extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
297 298
298__attribute__((format (printf,1,2))) 299__attribute__((format (printf,1,2)))
299extern char *fmt(const char *format,...); 300extern char *fmt(const char *format,...);
300 301
301extern struct commitinfo *cgit_parse_commit(struct commit *commit); 302extern struct commitinfo *cgit_parse_commit(struct commit *commit);
302extern struct taginfo *cgit_parse_tag(struct tag *tag); 303extern struct taginfo *cgit_parse_tag(struct tag *tag);
303extern void cgit_parse_url(const char *url); 304extern void cgit_parse_url(const char *url);
304 305
305extern const char *cgit_repobasename(const char *reponame); 306extern const char *cgit_repobasename(const char *reponame);
306 307
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index ce78d41..75b6584 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -192,256 +192,263 @@ max-message-length::
192max-repo-count:: 192max-repo-count::
193 Specifies the number of entries to list per page on therepository 193 Specifies the number of entries to list per page on therepository
194 index page. Default value: "50". 194 index page. Default value: "50".
195 195
196max-repodesc-length:: 196max-repodesc-length::
197 Specifies the maximum number of repo description characters to display 197 Specifies the maximum number of repo description characters to display
198 on the repository index page. Default value: "80". 198 on the repository index page. Default value: "80".
199 199
200max-blob-size:: 200max-blob-size::
201 Specifies the maximum size of a blob to display HTML for in KBytes. 201 Specifies the maximum size of a blob to display HTML for in KBytes.
202 Default value: "0" (limit disabled). 202 Default value: "0" (limit disabled).
203 203
204max-stats:: 204max-stats::
205 Set the default maximum statistics period. Valid values are "week", 205 Set the default maximum statistics period. Valid values are "week",
206 "month", "quarter" and "year". If unspecified, statistics are 206 "month", "quarter" and "year". If unspecified, statistics are
207 disabled. Default value: none. See also: "repo.max-stats". 207 disabled. Default value: none. See also: "repo.max-stats".
208 208
209mimetype.<ext>:: 209mimetype.<ext>::
210 Set the mimetype for the specified filename extension. This is used 210 Set the mimetype for the specified filename extension. This is used
211 by the `plain` command when returning blob content. 211 by the `plain` command when returning blob content.
212 212
213module-link:: 213module-link::
214 Text which will be used as the formatstring for a hyperlink when a 214 Text which will be used as the formatstring for a hyperlink when a
215 submodule is printed in a directory listing. The arguments for the 215 submodule is printed in a directory listing. The arguments for the
216 formatstring are the path and SHA1 of the submodule commit. Default 216 formatstring are the path and SHA1 of the submodule commit. Default
217 value: "./?repo=%s&page=commit&id=%s" 217 value: "./?repo=%s&page=commit&id=%s"
218 218
219nocache:: 219nocache::
220 If set to the value "1" caching will be disabled. This settings is 220 If set to the value "1" caching will be disabled. This settings is
221 deprecated, and will not be honored starting with cgit-1.0. Default 221 deprecated, and will not be honored starting with cgit-1.0. Default
222 value: "0". 222 value: "0".
223 223
224noplainemail:: 224noplainemail::
225 If set to "1" showing full author email adresses will be disabled. 225 If set to "1" showing full author email adresses will be disabled.
226 Default value: "0". 226 Default value: "0".
227 227
228noheader:: 228noheader::
229 Flag which, when set to "1", will make cgit omit the standard header 229 Flag which, when set to "1", will make cgit omit the standard header
230 on all pages. Default value: none. See also: "embedded". 230 on all pages. Default value: none. See also: "embedded".
231 231
232project-list:: 232project-list::
233 A list of subdirectories inside of scan-path, relative to it, that 233 A list of subdirectories inside of scan-path, relative to it, that
234 should loaded as git repositories. This must be defined prior to 234 should loaded as git repositories. This must be defined prior to
235 scan-path. Default value: none. See also: scan-path. 235 scan-path. Default value: none. See also: scan-path.
236 236
237readme:: 237readme::
238 Text which will be used as default value for "repo.readme". Default 238 Text which will be used as default value for "repo.readme". Default
239 value: none. 239 value: none.
240 240
241remove-suffix:: 241remove-suffix::
242 If set to "1" and scan-path is enabled, if any repositories are found 242 If set to "1" and scan-path is enabled, if any repositories are found
243 with a suffix of ".git", this suffix will be removed for the url and 243 with a suffix of ".git", this suffix will be removed for the url and
244 name. Default value: "0". See also: scan-path. 244 name. Default value: "0". See also: scan-path.
245 245
246renamelimit:: 246renamelimit::
247 Maximum number of files to consider when detecting renames. The value 247 Maximum number of files to consider when detecting renames. The value
248 "-1" uses the compiletime value in git (for further info, look at 248 "-1" uses the compiletime value in git (for further info, look at
249 `man git-diff`). Default value: "-1". 249 `man git-diff`). Default value: "-1".
250 250
251repo.group:: 251repo.group::
252 Legacy alias for "section". This option is deprecated and will not be 252 Legacy alias for "section". This option is deprecated and will not be
253 supported in cgit-1.0. 253 supported in cgit-1.0.
254 254
255robots:: 255robots::
256 Text used as content for the "robots" meta-tag. Default value: 256 Text used as content for the "robots" meta-tag. Default value:
257 "index, nofollow". 257 "index, nofollow".
258 258
259root-desc:: 259root-desc::
260 Text printed below the heading on the repository index page. Default 260 Text printed below the heading on the repository index page. Default
261 value: "a fast webinterface for the git dscm". 261 value: "a fast webinterface for the git dscm".
262 262
263root-readme:: 263root-readme::
264 The content of the file specified with this option will be included 264 The content of the file specified with this option will be included
265 verbatim below the "about" link on the repository index page. Default 265 verbatim below the "about" link on the repository index page. Default
266 value: none. 266 value: none.
267 267
268root-title:: 268root-title::
269 Text printed as heading on the repository index page. Default value: 269 Text printed as heading on the repository index page. Default value:
270 "Git Repository Browser". 270 "Git Repository Browser".
271 271
272scan-path:: 272scan-path::
273 A path which will be scanned for repositories. If caching is enabled, 273 A path which will be scanned for repositories. If caching is enabled,
274 the result will be cached as a cgitrc include-file in the cache 274 the result will be cached as a cgitrc include-file in the cache
275 directory. If project-list has been defined prior to scan-path, 275 directory. If project-list has been defined prior to scan-path,
276 scan-path loads only the directories listed in the file pointed to by 276 scan-path loads only the directories listed in the file pointed to by
277 project-list. Default value: none. See also: cache-scanrc-ttl, 277 project-list. Default value: none. See also: cache-scanrc-ttl,
278 project-list. 278 project-list.
279 279
280section:: 280section::
281 The name of the current repository section - all repositories defined 281 The name of the current repository section - all repositories defined
282 after this option will inherit the current section name. Default value: 282 after this option will inherit the current section name. Default value:
283 none. 283 none.
284 284
285section-from-path:: 285section-from-path::
286 A number which, if specified before scan-path, specifies how many 286 A number which, if specified before scan-path, specifies how many
287 path elements from each repo path to use as a default section name. 287 path elements from each repo path to use as a default section name.
288 If negative, cgit will discard the specified number of path elements 288 If negative, cgit will discard the specified number of path elements
289 above the repo directory. Default value: 0. 289 above the repo directory. Default value: 0.
290 290
291side-by-side-diffs:: 291side-by-side-diffs::
292 If set to "1" shows side-by-side diffs instead of unidiffs per 292 If set to "1" shows side-by-side diffs instead of unidiffs per
293 default. Default value: "0". 293 default. Default value: "0".
294 294
295snapshots:: 295snapshots::
296 Text which specifies the default set of snapshot formats generated by 296 Text which specifies the default set of snapshot formats generated by
297 cgit. The value is a space-separated list of zero or more of the 297 cgit. The value is a space-separated list of zero or more of the
298 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 298 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
299 299
300source-filter:: 300source-filter::
301 Specifies a command which will be invoked to format plaintext blobs 301 Specifies a command which will be invoked to format plaintext blobs
302 in the tree view. The command will get the blob content on its STDIN 302 in the tree view. The command will get the blob content on its STDIN
303 and the name of the blob as its only command line argument. The STDOUT 303 and the name of the blob as its only command line argument. The STDOUT
304 from the command will be included verbatim as the blob contents, i.e. 304 from the command will be included verbatim as the blob contents, i.e.
305 this can be used to implement e.g. syntax highlighting. Default value: 305 this can be used to implement e.g. syntax highlighting. Default value:
306 none. 306 none.
307 307
308summary-branches:: 308summary-branches::
309 Specifies the number of branches to display in the repository "summary" 309 Specifies the number of branches to display in the repository "summary"
310 view. Default value: "10". 310 view. Default value: "10".
311 311
312summary-log:: 312summary-log::
313 Specifies the number of log entries to display in the repository 313 Specifies the number of log entries to display in the repository
314 "summary" view. Default value: "10". 314 "summary" view. Default value: "10".
315 315
316summary-tags:: 316summary-tags::
317 Specifies the number of tags to display in the repository "summary" 317 Specifies the number of tags to display in the repository "summary"
318 view. Default value: "10". 318 view. Default value: "10".
319 319
320strict-export::
321 Filename which, if specified, needs to be present within the repository
322 for cgit to allow access to that repository. This can be used to emulate
323 gitweb's EXPORT_OK and STRICT_EXPORT functionality and limit cgit's
324 repositories to match those exported by git-daemon. This option MUST come
325 before 'scan-path'.
326
320virtual-root:: 327virtual-root::
321 Url which, if specified, will be used as root for all cgit links. It 328 Url which, if specified, will be used as root for all cgit links. It
322 will also cause cgit to generate 'virtual urls', i.e. urls like 329 will also cause cgit to generate 'virtual urls', i.e. urls like
323 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 330 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
324 value: none. 331 value: none.
325 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 332 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
326 same kind of virtual urls, so this option will probably be deprecated. 333 same kind of virtual urls, so this option will probably be deprecated.
327 334
328REPOSITORY SETTINGS 335REPOSITORY SETTINGS
329------------------- 336-------------------
330repo.about-filter:: 337repo.about-filter::
331 Override the default about-filter. Default value: none. See also: 338 Override the default about-filter. Default value: none. See also:
332 "enable-filter-overrides". 339 "enable-filter-overrides".
333 340
334repo.clone-url:: 341repo.clone-url::
335 A list of space-separated urls which can be used to clone this repo. 342 A list of space-separated urls which can be used to clone this repo.
336 Default value: none. 343 Default value: none.
337 344
338repo.commit-filter:: 345repo.commit-filter::
339 Override the default commit-filter. Default value: none. See also: 346 Override the default commit-filter. Default value: none. See also:
340 "enable-filter-overrides". 347 "enable-filter-overrides".
341 348
342repo.defbranch:: 349repo.defbranch::
343 The name of the default branch for this repository. If no such branch 350 The name of the default branch for this repository. If no such branch
344 exists in the repository, the first branch name (when sorted) is used 351 exists in the repository, the first branch name (when sorted) is used
345 as default instead. Default value: "master". 352 as default instead. Default value: "master".
346 353
347repo.desc:: 354repo.desc::
348 The value to show as repository description. Default value: none. 355 The value to show as repository description. Default value: none.
349 356
350repo.enable-log-filecount:: 357repo.enable-log-filecount::
351 A flag which can be used to disable the global setting 358 A flag which can be used to disable the global setting
352 `enable-log-filecount'. Default value: none. 359 `enable-log-filecount'. Default value: none.
353 360
354repo.enable-log-linecount:: 361repo.enable-log-linecount::
355 A flag which can be used to disable the global setting 362 A flag which can be used to disable the global setting
356 `enable-log-linecount'. Default value: none. 363 `enable-log-linecount'. Default value: none.
357 364
358repo.enable-remote-branches:: 365repo.enable-remote-branches::
359 Flag which, when set to "1", will make cgit display remote branches 366 Flag which, when set to "1", will make cgit display remote branches
360 in the summary and refs views. Default value: <enable-remote-branches>. 367 in the summary and refs views. Default value: <enable-remote-branches>.
361 368
362repo.enable-subject-links:: 369repo.enable-subject-links::
363 A flag which can be used to override the global setting 370 A flag which can be used to override the global setting
364 `enable-subject-links'. Default value: none. 371 `enable-subject-links'. Default value: none.
365 372
366repo.max-stats:: 373repo.max-stats::
367 Override the default maximum statistics period. Valid values are equal 374 Override the default maximum statistics period. Valid values are equal
368 to the values specified for the global "max-stats" setting. Default 375 to the values specified for the global "max-stats" setting. Default
369 value: none. 376 value: none.
370 377
371repo.name:: 378repo.name::
372 The value to show as repository name. Default value: <repo.url>. 379 The value to show as repository name. Default value: <repo.url>.
373 380
374repo.owner:: 381repo.owner::
375 A value used to identify the owner of the repository. Default value: 382 A value used to identify the owner of the repository. Default value:
376 none. 383 none.
377 384
378repo.path:: 385repo.path::
379 An absolute path to the repository directory. For non-bare repositories 386 An absolute path to the repository directory. For non-bare repositories
380 this is the .git-directory. Default value: none. 387 this is the .git-directory. Default value: none.
381 388
382repo.readme:: 389repo.readme::
383 A path (relative to <repo.path>) which specifies a file to include 390 A path (relative to <repo.path>) which specifies a file to include
384 verbatim as the "About" page for this repo. You may also specify a 391 verbatim as the "About" page for this repo. You may also specify a
385 git refspec by head or by hash by prepending the refspec followed by 392 git refspec by head or by hash by prepending the refspec followed by
386 a colon. For example, "master:docs/readme.mkd" Default value: <readme>. 393 a colon. For example, "master:docs/readme.mkd" Default value: <readme>.
387 394
388repo.snapshots:: 395repo.snapshots::
389 A mask of allowed snapshot-formats for this repo, restricted by the 396 A mask of allowed snapshot-formats for this repo, restricted by the
390 "snapshots" global setting. Default value: <snapshots>. 397 "snapshots" global setting. Default value: <snapshots>.
391 398
392repo.section:: 399repo.section::
393 Override the current section name for this repository. Default value: 400 Override the current section name for this repository. Default value:
394 none. 401 none.
395 402
396repo.source-filter:: 403repo.source-filter::
397 Override the default source-filter. Default value: none. See also: 404 Override the default source-filter. Default value: none. See also:
398 "enable-filter-overrides". 405 "enable-filter-overrides".
399 406
400repo.url:: 407repo.url::
401 The relative url used to access the repository. This must be the first 408 The relative url used to access the repository. This must be the first
402 setting specified for each repo. Default value: none. 409 setting specified for each repo. Default value: none.
403 410
404 411
405REPOSITORY-SPECIFIC CGITRC FILE 412REPOSITORY-SPECIFIC CGITRC FILE
406------------------------------- 413-------------------------------
407When the option "scan-path" is used to auto-discover git repositories, cgit 414When the option "scan-path" is used to auto-discover git repositories, cgit
408will try to parse the file "cgitrc" within any found repository. Such a 415will try to parse the file "cgitrc" within any found repository. Such a
409repo-specific config file may contain any of the repo-specific options 416repo-specific config file may contain any of the repo-specific options
410described above, except "repo.url" and "repo.path". Additionally, the "filter" 417described above, except "repo.url" and "repo.path". Additionally, the "filter"
411options are only acknowledged in repo-specific config files when 418options are only acknowledged in repo-specific config files when
412"enable-filter-overrides" is set to "1". 419"enable-filter-overrides" is set to "1".
413 420
414Note: the "repo." prefix is dropped from the option names in repo-specific 421Note: the "repo." prefix is dropped from the option names in repo-specific
415config files, e.g. "repo.desc" becomes "desc". 422config files, e.g. "repo.desc" becomes "desc".
416 423
417 424
418EXAMPLE CGITRC FILE 425EXAMPLE CGITRC FILE
419------------------- 426-------------------
420 427
421.... 428....
422# Enable caching of up to 1000 output entriess 429# Enable caching of up to 1000 output entriess
423cache-size=1000 430cache-size=1000
424 431
425 432
426# Specify some default clone prefixes 433# Specify some default clone prefixes
427clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git 434clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
428 435
429# Specify the css url 436# Specify the css url
430css=/css/cgit.css 437css=/css/cgit.css
431 438
432 439
433# Show extra links for each repository on the index page 440# Show extra links for each repository on the index page
434enable-index-links=1 441enable-index-links=1
435 442
436 443
437# Show number of affected files per commit on the log pages 444# Show number of affected files per commit on the log pages
438enable-log-filecount=1 445enable-log-filecount=1
439 446
440 447
441# Show number of added/removed lines per commit on the log pages 448# Show number of added/removed lines per commit on the log pages
442enable-log-linecount=1 449enable-log-linecount=1
443 450
444 451
445# Add a cgit favicon 452# Add a cgit favicon
446favicon=/favicon.ico 453favicon=/favicon.ico
447 454
diff --git a/scan-tree.c b/scan-tree.c
index b5b50f3..a0e09ce 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -1,211 +1,215 @@
1/* scan-tree.c 1/* scan-tree.c
2 * 2 *
3 * Copyright (C) 2008-2009 Lars Hjemli 3 * Copyright (C) 2008-2009 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com> 4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
5 * 5 *
6 * Licensed under GNU General Public License v2 6 * Licensed under GNU General Public License v2
7 * (see COPYING for full license text) 7 * (see COPYING for full license text)
8 */ 8 */
9 9
10#include "cgit.h" 10#include "cgit.h"
11#include "configfile.h" 11#include "configfile.h"
12#include "html.h" 12#include "html.h"
13 13
14#define MAX_PATH 4096 14#define MAX_PATH 4096
15 15
16/* 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 */
17static int is_git_dir(const char *path) 17static int is_git_dir(const char *path)
18{ 18{
19 struct stat st; 19 struct stat st;
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;
50char *owner; 50char *owner;
51 51
52static void repo_config(const char *name, const char *value) 52static void repo_config(const char *name, const char *value)
53{ 53{
54 config_fn(repo, name, value); 54 config_fn(repo, name, value);
55} 55}
56 56
57static int git_owner_config(const char *key, const char *value, void *cb) 57static int git_owner_config(const char *key, const char *value, void *cb)
58{ 58{
59 if (!strcmp(key, "gitweb.owner")) 59 if (!strcmp(key, "gitweb.owner"))
60 owner = xstrdup(value); 60 owner = xstrdup(value);
61 return 0; 61 return 0;
62} 62}
63 63
64static char *xstrrchr(char *s, char *from, int c) 64static char *xstrrchr(char *s, char *from, int c)
65{ 65{
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
85 if (ctx.cfg.strict_export && stat(fmt("%s/%s", path, ctx.cfg.strict_export), &st))
86 return;
87
84 if (!stat(fmt("%s/noweb", path), &st)) 88 if (!stat(fmt("%s/noweb", path), &st))
85 return; 89 return;
86 90
87 owner = NULL; 91 owner = NULL;
88 if (ctx.cfg.enable_gitweb_owner) 92 if (ctx.cfg.enable_gitweb_owner)
89 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);
90 if (base == path) 94 if (base == path)
91 rel = xstrdup(fmt("%s", path)); 95 rel = xstrdup(fmt("%s", path));
92 else 96 else
93 rel = xstrdup(fmt("%s", path + strlen(base) + 1)); 97 rel = xstrdup(fmt("%s", path + strlen(base) + 1));
94 98
95 if (!strcmp(rel + strlen(rel) - 5, "/.git")) 99 if (!strcmp(rel + strlen(rel) - 5, "/.git"))
96 rel[strlen(rel) - 5] = '\0'; 100 rel[strlen(rel) - 5] = '\0';
97 101
98 repo = cgit_add_repo(rel); 102 repo = cgit_add_repo(rel);
99 if (ctx.cfg.remove_suffix) 103 if (ctx.cfg.remove_suffix)
100 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) 104 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
101 *p = '\0'; 105 *p = '\0';
102 repo->name = repo->url; 106 repo->name = repo->url;
103 repo->path = xstrdup(path); 107 repo->path = xstrdup(path);
104 while (!owner) { 108 while (!owner) {
105 if ((pwd = getpwuid(st.st_uid)) == NULL) { 109 if ((pwd = getpwuid(st.st_uid)) == NULL) {
106 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 110 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
107 path, strerror(errno), errno); 111 path, strerror(errno), errno);
108 break; 112 break;
109 } 113 }
110 if (pwd->pw_gecos) 114 if (pwd->pw_gecos)
111 if ((p = strchr(pwd->pw_gecos, ','))) 115 if ((p = strchr(pwd->pw_gecos, ',')))
112 *p = '\0'; 116 *p = '\0';
113 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); 117 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
114 } 118 }
115 repo->owner = owner; 119 repo->owner = owner;
116 120
117 p = fmt("%s/description", path); 121 p = fmt("%s/description", path);
118 if (!stat(p, &st)) 122 if (!stat(p, &st))
119 readfile(p, &repo->desc, &size); 123 readfile(p, &repo->desc, &size);
120 124
121 if (!repo->readme) { 125 if (!repo->readme) {
122 p = fmt("%s/README.html", path); 126 p = fmt("%s/README.html", path);
123 if (!stat(p, &st)) 127 if (!stat(p, &st))
124 repo->readme = "README.html"; 128 repo->readme = "README.html";
125 } 129 }
126 if (ctx.cfg.section_from_path) { 130 if (ctx.cfg.section_from_path) {
127 n = ctx.cfg.section_from_path; 131 n = ctx.cfg.section_from_path;
128 if (n > 0) { 132 if (n > 0) {
129 slash = rel; 133 slash = rel;
130 while (slash && n && (slash = strchr(slash, '/'))) 134 while (slash && n && (slash = strchr(slash, '/')))
131 n--; 135 n--;
132 } else { 136 } else {
133 slash = rel + strlen(rel); 137 slash = rel + strlen(rel);
134 while (slash && n && (slash = xstrrchr(rel, slash, '/'))) 138 while (slash && n && (slash = xstrrchr(rel, slash, '/')))
135 n++; 139 n++;
136 } 140 }
137 if (slash && !n) { 141 if (slash && !n) {
138 *slash = '\0'; 142 *slash = '\0';
139 repo->section = xstrdup(rel); 143 repo->section = xstrdup(rel);
140 *slash = '/'; 144 *slash = '/';
141 if (!prefixcmp(repo->name, repo->section)) { 145 if (!prefixcmp(repo->name, repo->section)) {
142 repo->name += strlen(repo->section); 146 repo->name += strlen(repo->section);
143 if (*repo->name == '/') 147 if (*repo->name == '/')
144 repo->name++; 148 repo->name++;
145 } 149 }
146 } 150 }
147 } 151 }
148 152
149 p = fmt("%s/cgitrc", path); 153 p = fmt("%s/cgitrc", path);
150 if (!stat(p, &st)) { 154 if (!stat(p, &st)) {
151 config_fn = fn; 155 config_fn = fn;
152 parse_configfile(xstrdup(p), &repo_config); 156 parse_configfile(xstrdup(p), &repo_config);
153 } 157 }
154} 158}
155 159
156static 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)
157{ 161{
158 DIR *dir; 162 DIR *dir;
159 struct dirent *ent; 163 struct dirent *ent;
160 char *buf; 164 char *buf;
161 struct stat st; 165 struct stat st;
162 166
163 if (is_git_dir(path)) { 167 if (is_git_dir(path)) {
164 add_repo(base, path, fn); 168 add_repo(base, path, fn);
165 return; 169 return;
166 } 170 }
167 if (is_git_dir(fmt("%s/.git", path))) { 171 if (is_git_dir(fmt("%s/.git", path))) {
168 add_repo(base, fmt("%s/.git", path), fn); 172 add_repo(base, fmt("%s/.git", path), fn);
169 return; 173 return;
170 } 174 }
171 dir = opendir(path); 175 dir = opendir(path);
172 if (!dir) { 176 if (!dir) {
173 fprintf(stderr, "Error opening directory %s: %s (%d)\n", 177 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
174 path, strerror(errno), errno); 178 path, strerror(errno), errno);
175 return; 179 return;
176 } 180 }
177 while((ent = readdir(dir)) != NULL) { 181 while((ent = readdir(dir)) != NULL) {
178 if (ent->d_name[0] == '.') { 182 if (ent->d_name[0] == '.') {
179 if (ent->d_name[1] == '\0') 183 if (ent->d_name[1] == '\0')
180 continue; 184 continue;
181 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 185 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
182 continue; 186 continue;
183 } 187 }
184 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 188 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
185 if (!buf) { 189 if (!buf) {
186 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 190 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
187 path, strerror(errno), errno); 191 path, strerror(errno), errno);
188 exit(1); 192 exit(1);
189 } 193 }
190 sprintf(buf, "%s/%s", path, ent->d_name); 194 sprintf(buf, "%s/%s", path, ent->d_name);
191 if (stat(buf, &st)) { 195 if (stat(buf, &st)) {
192 fprintf(stderr, "Error checking path %s: %s (%d)\n", 196 fprintf(stderr, "Error checking path %s: %s (%d)\n",
193 buf, strerror(errno), errno); 197 buf, strerror(errno), errno);
194 free(buf); 198 free(buf);
195 continue; 199 continue;
196 } 200 }
197 if (S_ISDIR(st.st_mode)) 201 if (S_ISDIR(st.st_mode))
198 scan_path(base, buf, fn); 202 scan_path(base, buf, fn);
199 free(buf); 203 free(buf);
200 } 204 }
201 closedir(dir); 205 closedir(dir);
202} 206}
203 207
204#define lastc(s) s[strlen(s) - 1] 208#define lastc(s) s[strlen(s) - 1]
205 209
206void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) 210void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
207{ 211{
208 char line[MAX_PATH * 2], *z; 212 char line[MAX_PATH * 2], *z;
209 FILE *projects; 213 FILE *projects;
210 int err; 214 int err;
211 215