summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt6
-rw-r--r--scan-tree.c42
4 files changed, 45 insertions, 6 deletions
diff --git a/cgit.c b/cgit.c
index d6146e2..5666875 100644
--- a/cgit.c
+++ b/cgit.c
@@ -70,256 +70,258 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
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 char *colon; 75 char *colon;
76 if (*value == '/' || ((colon = strchr(value, ':')) != NULL && colon != value && *(colon + 1) != '\0')) 76 if (*value == '/' || ((colon = strchr(value, ':')) != NULL && colon != value && *(colon + 1) != '\0'))
77 repo->readme = xstrdup(value); 77 repo->readme = xstrdup(value);
78 else 78 else
79 repo->readme = xstrdup(fmt("%s/%s", repo->path, value)); 79 repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
80 } else if (ctx.cfg.enable_filter_overrides) { 80 } else if (ctx.cfg.enable_filter_overrides) {
81 if (!strcmp(name, "about-filter")) 81 if (!strcmp(name, "about-filter"))
82 repo->about_filter = new_filter(value, 0); 82 repo->about_filter = new_filter(value, 0);
83 else if (!strcmp(name, "commit-filter")) 83 else if (!strcmp(name, "commit-filter"))
84 repo->commit_filter = new_filter(value, 0); 84 repo->commit_filter = new_filter(value, 0);
85 else if (!strcmp(name, "source-filter")) 85 else if (!strcmp(name, "source-filter"))
86 repo->source_filter = new_filter(value, 1); 86 repo->source_filter = new_filter(value, 1);
87 } 87 }
88} 88}
89 89
90void config_cb(const char *name, const char *value) 90void config_cb(const char *name, const char *value)
91{ 91{
92 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 92 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
93 ctx.cfg.section = xstrdup(value); 93 ctx.cfg.section = xstrdup(value);
94 else if (!strcmp(name, "repo.url")) 94 else if (!strcmp(name, "repo.url"))
95 ctx.repo = cgit_add_repo(value); 95 ctx.repo = cgit_add_repo(value);
96 else if (ctx.repo && !strcmp(name, "repo.path")) 96 else if (ctx.repo && !strcmp(name, "repo.path"))
97 ctx.repo->path = trim_end(value, '/'); 97 ctx.repo->path = trim_end(value, '/');
98 else if (ctx.repo && !prefixcmp(name, "repo.")) 98 else if (ctx.repo && !prefixcmp(name, "repo."))
99 repo_config(ctx.repo, name + 5, value); 99 repo_config(ctx.repo, name + 5, value);
100 else if (!strcmp(name, "root-title")) 100 else if (!strcmp(name, "root-title"))
101 ctx.cfg.root_title = xstrdup(value); 101 ctx.cfg.root_title = xstrdup(value);
102 else if (!strcmp(name, "root-desc")) 102 else if (!strcmp(name, "root-desc"))
103 ctx.cfg.root_desc = xstrdup(value); 103 ctx.cfg.root_desc = xstrdup(value);
104 else if (!strcmp(name, "root-readme")) 104 else if (!strcmp(name, "root-readme"))
105 ctx.cfg.root_readme = xstrdup(value); 105 ctx.cfg.root_readme = xstrdup(value);
106 else if (!strcmp(name, "css")) 106 else if (!strcmp(name, "css"))
107 ctx.cfg.css = xstrdup(value); 107 ctx.cfg.css = xstrdup(value);
108 else if (!strcmp(name, "favicon")) 108 else if (!strcmp(name, "favicon"))
109 ctx.cfg.favicon = xstrdup(value); 109 ctx.cfg.favicon = xstrdup(value);
110 else if (!strcmp(name, "footer")) 110 else if (!strcmp(name, "footer"))
111 ctx.cfg.footer = xstrdup(value); 111 ctx.cfg.footer = xstrdup(value);
112 else if (!strcmp(name, "head-include")) 112 else if (!strcmp(name, "head-include"))
113 ctx.cfg.head_include = xstrdup(value); 113 ctx.cfg.head_include = xstrdup(value);
114 else if (!strcmp(name, "header")) 114 else if (!strcmp(name, "header"))
115 ctx.cfg.header = xstrdup(value); 115 ctx.cfg.header = xstrdup(value);
116 else if (!strcmp(name, "logo")) 116 else if (!strcmp(name, "logo"))
117 ctx.cfg.logo = xstrdup(value); 117 ctx.cfg.logo = xstrdup(value);
118 else if (!strcmp(name, "index-header")) 118 else if (!strcmp(name, "index-header"))
119 ctx.cfg.index_header = xstrdup(value); 119 ctx.cfg.index_header = xstrdup(value);
120 else if (!strcmp(name, "index-info")) 120 else if (!strcmp(name, "index-info"))
121 ctx.cfg.index_info = xstrdup(value); 121 ctx.cfg.index_info = xstrdup(value);
122 else if (!strcmp(name, "logo-link")) 122 else if (!strcmp(name, "logo-link"))
123 ctx.cfg.logo_link = xstrdup(value); 123 ctx.cfg.logo_link = xstrdup(value);
124 else if (!strcmp(name, "module-link")) 124 else if (!strcmp(name, "module-link"))
125 ctx.cfg.module_link = xstrdup(value); 125 ctx.cfg.module_link = xstrdup(value);
126 else if (!strcmp(name, "virtual-root")) { 126 else if (!strcmp(name, "virtual-root")) {
127 ctx.cfg.virtual_root = trim_end(value, '/'); 127 ctx.cfg.virtual_root = trim_end(value, '/');
128 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 128 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
129 ctx.cfg.virtual_root = ""; 129 ctx.cfg.virtual_root = "";
130 } else if (!strcmp(name, "nocache")) 130 } else if (!strcmp(name, "nocache"))
131 ctx.cfg.nocache = atoi(value); 131 ctx.cfg.nocache = atoi(value);
132 else if (!strcmp(name, "noplainemail")) 132 else if (!strcmp(name, "noplainemail"))
133 ctx.cfg.noplainemail = atoi(value); 133 ctx.cfg.noplainemail = atoi(value);
134 else if (!strcmp(name, "noheader")) 134 else if (!strcmp(name, "noheader"))
135 ctx.cfg.noheader = atoi(value); 135 ctx.cfg.noheader = atoi(value);
136 else if (!strcmp(name, "snapshots")) 136 else if (!strcmp(name, "snapshots"))
137 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 137 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
138 else if (!strcmp(name, "enable-filter-overrides")) 138 else if (!strcmp(name, "enable-filter-overrides"))
139 ctx.cfg.enable_filter_overrides = atoi(value); 139 ctx.cfg.enable_filter_overrides = atoi(value);
140 else if (!strcmp(name, "enable-gitweb-owner")) 140 else if (!strcmp(name, "enable-gitweb-owner"))
141 ctx.cfg.enable_gitweb_owner = atoi(value); 141 ctx.cfg.enable_gitweb_owner = atoi(value);
142 else if (!strcmp(name, "enable-index-links")) 142 else if (!strcmp(name, "enable-index-links"))
143 ctx.cfg.enable_index_links = atoi(value); 143 ctx.cfg.enable_index_links = atoi(value);
144 else if (!strcmp(name, "enable-log-filecount")) 144 else if (!strcmp(name, "enable-log-filecount"))
145 ctx.cfg.enable_log_filecount = atoi(value); 145 ctx.cfg.enable_log_filecount = atoi(value);
146 else if (!strcmp(name, "enable-log-linecount")) 146 else if (!strcmp(name, "enable-log-linecount"))
147 ctx.cfg.enable_log_linecount = atoi(value); 147 ctx.cfg.enable_log_linecount = atoi(value);
148 else if (!strcmp(name, "enable-remote-branches")) 148 else if (!strcmp(name, "enable-remote-branches"))
149 ctx.cfg.enable_remote_branches = atoi(value); 149 ctx.cfg.enable_remote_branches = atoi(value);
150 else if (!strcmp(name, "enable-subject-links")) 150 else if (!strcmp(name, "enable-subject-links"))
151 ctx.cfg.enable_subject_links = atoi(value); 151 ctx.cfg.enable_subject_links = atoi(value);
152 else if (!strcmp(name, "enable-tree-linenumbers")) 152 else if (!strcmp(name, "enable-tree-linenumbers"))
153 ctx.cfg.enable_tree_linenumbers = atoi(value); 153 ctx.cfg.enable_tree_linenumbers = atoi(value);
154 else if (!strcmp(name, "max-stats")) 154 else if (!strcmp(name, "max-stats"))
155 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 155 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
156 else if (!strcmp(name, "cache-size")) 156 else if (!strcmp(name, "cache-size"))
157 ctx.cfg.cache_size = atoi(value); 157 ctx.cfg.cache_size = atoi(value);
158 else if (!strcmp(name, "cache-root")) 158 else if (!strcmp(name, "cache-root"))
159 ctx.cfg.cache_root = xstrdup(expand_macros(value)); 159 ctx.cfg.cache_root = xstrdup(expand_macros(value));
160 else if (!strcmp(name, "cache-root-ttl")) 160 else if (!strcmp(name, "cache-root-ttl"))
161 ctx.cfg.cache_root_ttl = atoi(value); 161 ctx.cfg.cache_root_ttl = atoi(value);
162 else if (!strcmp(name, "cache-repo-ttl")) 162 else if (!strcmp(name, "cache-repo-ttl"))
163 ctx.cfg.cache_repo_ttl = atoi(value); 163 ctx.cfg.cache_repo_ttl = atoi(value);
164 else if (!strcmp(name, "cache-scanrc-ttl")) 164 else if (!strcmp(name, "cache-scanrc-ttl"))
165 ctx.cfg.cache_scanrc_ttl = atoi(value); 165 ctx.cfg.cache_scanrc_ttl = atoi(value);
166 else if (!strcmp(name, "cache-static-ttl")) 166 else if (!strcmp(name, "cache-static-ttl"))
167 ctx.cfg.cache_static_ttl = atoi(value); 167 ctx.cfg.cache_static_ttl = atoi(value);
168 else if (!strcmp(name, "cache-dynamic-ttl")) 168 else if (!strcmp(name, "cache-dynamic-ttl"))
169 ctx.cfg.cache_dynamic_ttl = atoi(value); 169 ctx.cfg.cache_dynamic_ttl = atoi(value);
170 else if (!strcmp(name, "about-filter")) 170 else if (!strcmp(name, "about-filter"))
171 ctx.cfg.about_filter = new_filter(value, 0); 171 ctx.cfg.about_filter = new_filter(value, 0);
172 else if (!strcmp(name, "commit-filter")) 172 else if (!strcmp(name, "commit-filter"))
173 ctx.cfg.commit_filter = new_filter(value, 0); 173 ctx.cfg.commit_filter = new_filter(value, 0);
174 else if (!strcmp(name, "embedded")) 174 else if (!strcmp(name, "embedded"))
175 ctx.cfg.embedded = atoi(value); 175 ctx.cfg.embedded = atoi(value);
176 else if (!strcmp(name, "max-atom-items")) 176 else if (!strcmp(name, "max-atom-items"))
177 ctx.cfg.max_atom_items = atoi(value); 177 ctx.cfg.max_atom_items = atoi(value);
178 else if (!strcmp(name, "max-message-length")) 178 else if (!strcmp(name, "max-message-length"))
179 ctx.cfg.max_msg_len = atoi(value); 179 ctx.cfg.max_msg_len = atoi(value);
180 else if (!strcmp(name, "max-repodesc-length")) 180 else if (!strcmp(name, "max-repodesc-length"))
181 ctx.cfg.max_repodesc_len = atoi(value); 181 ctx.cfg.max_repodesc_len = atoi(value);
182 else if (!strcmp(name, "max-blob-size")) 182 else if (!strcmp(name, "max-blob-size"))
183 ctx.cfg.max_blob_size = atoi(value); 183 ctx.cfg.max_blob_size = atoi(value);
184 else if (!strcmp(name, "max-repo-count")) 184 else if (!strcmp(name, "max-repo-count"))
185 ctx.cfg.max_repo_count = atoi(value); 185 ctx.cfg.max_repo_count = atoi(value);
186 else if (!strcmp(name, "max-commit-count")) 186 else if (!strcmp(name, "max-commit-count"))
187 ctx.cfg.max_commit_count = atoi(value); 187 ctx.cfg.max_commit_count = atoi(value);
188 else if (!strcmp(name, "project-list")) 188 else if (!strcmp(name, "project-list"))
189 ctx.cfg.project_list = xstrdup(expand_macros(value)); 189 ctx.cfg.project_list = xstrdup(expand_macros(value));
190 else if (!strcmp(name, "scan-path")) 190 else if (!strcmp(name, "scan-path"))
191 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 191 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
192 process_cached_repolist(expand_macros(value)); 192 process_cached_repolist(expand_macros(value));
193 else if (ctx.cfg.project_list) 193 else if (ctx.cfg.project_list)
194 scan_projects(expand_macros(value), 194 scan_projects(expand_macros(value),
195 ctx.cfg.project_list, repo_config); 195 ctx.cfg.project_list, repo_config);
196 else 196 else
197 scan_tree(expand_macros(value), repo_config); 197 scan_tree(expand_macros(value), repo_config);
198 else if (!strcmp(name, "section-from-path"))
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")) {
252 ctx.qry.sha2 = xstrdup(value); 254 ctx.qry.sha2 = xstrdup(value);
253 ctx.qry.has_sha1 = 1; 255 ctx.qry.has_sha1 = 1;
254 } else if (!strcmp(name, "ofs")) { 256 } else if (!strcmp(name, "ofs")) {
255 ctx.qry.ofs = atoi(value); 257 ctx.qry.ofs = atoi(value);
256 } else if (!strcmp(name, "path")) { 258 } else if (!strcmp(name, "path")) {
257 ctx.qry.path = trim_end(value, '/'); 259 ctx.qry.path = trim_end(value, '/');
258 } else if (!strcmp(name, "name")) { 260 } else if (!strcmp(name, "name")) {
259 ctx.qry.name = xstrdup(value); 261 ctx.qry.name = xstrdup(value);
260 } else if (!strcmp(name, "mimetype")) { 262 } else if (!strcmp(name, "mimetype")) {
261 ctx.qry.mimetype = xstrdup(value); 263 ctx.qry.mimetype = xstrdup(value);
262 } else if (!strcmp(name, "s")){ 264 } else if (!strcmp(name, "s")){
263 ctx.qry.sort = xstrdup(value); 265 ctx.qry.sort = xstrdup(value);
264 } else if (!strcmp(name, "showmsg")) { 266 } else if (!strcmp(name, "showmsg")) {
265 ctx.qry.showmsg = atoi(value); 267 ctx.qry.showmsg = atoi(value);
266 } else if (!strcmp(name, "period")) { 268 } else if (!strcmp(name, "period")) {
267 ctx.qry.period = xstrdup(value); 269 ctx.qry.period = xstrdup(value);
268 } else if (!strcmp(name, "ss")) { 270 } else if (!strcmp(name, "ss")) {
269 ctx.qry.ssdiff = atoi(value); 271 ctx.qry.ssdiff = atoi(value);
270 } else if (!strcmp(name, "all")) { 272 } else if (!strcmp(name, "all")) {
271 ctx.qry.show_all = atoi(value); 273 ctx.qry.show_all = atoi(value);
272 } else if (!strcmp(name, "context")) { 274 } else if (!strcmp(name, "context")) {
273 ctx.qry.context = atoi(value); 275 ctx.qry.context = atoi(value);
274 } else if (!strcmp(name, "ignorews")) { 276 } else if (!strcmp(name, "ignorews")) {
275 ctx.qry.ignorews = atoi(value); 277 ctx.qry.ignorews = atoi(value);
276 } 278 }
277} 279}
278 280
279char *xstrdupn(const char *str) 281char *xstrdupn(const char *str)
280{ 282{
281 return (str ? xstrdup(str) : NULL); 283 return (str ? xstrdup(str) : NULL);
282} 284}
283 285
284static void prepare_context(struct cgit_context *ctx) 286static void prepare_context(struct cgit_context *ctx)
285{ 287{
286 memset(ctx, 0, sizeof(*ctx)); 288 memset(ctx, 0, sizeof(*ctx));
287 ctx->cfg.agefile = "info/web/last-modified"; 289 ctx->cfg.agefile = "info/web/last-modified";
288 ctx->cfg.nocache = 0; 290 ctx->cfg.nocache = 0;
289 ctx->cfg.cache_size = 0; 291 ctx->cfg.cache_size = 0;
290 ctx->cfg.cache_dynamic_ttl = 5; 292 ctx->cfg.cache_dynamic_ttl = 5;
291 ctx->cfg.cache_max_create_time = 5; 293 ctx->cfg.cache_max_create_time = 5;
292 ctx->cfg.cache_repo_ttl = 5; 294 ctx->cfg.cache_repo_ttl = 5;
293 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 295 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
294 ctx->cfg.cache_root_ttl = 5; 296 ctx->cfg.cache_root_ttl = 5;
295 ctx->cfg.cache_scanrc_ttl = 15; 297 ctx->cfg.cache_scanrc_ttl = 15;
296 ctx->cfg.cache_static_ttl = -1; 298 ctx->cfg.cache_static_ttl = -1;
297 ctx->cfg.css = "/cgit.css"; 299 ctx->cfg.css = "/cgit.css";
298 ctx->cfg.logo = "/cgit.png"; 300 ctx->cfg.logo = "/cgit.png";
299 ctx->cfg.local_time = 0; 301 ctx->cfg.local_time = 0;
300 ctx->cfg.enable_gitweb_owner = 1; 302 ctx->cfg.enable_gitweb_owner = 1;
301 ctx->cfg.enable_tree_linenumbers = 1; 303 ctx->cfg.enable_tree_linenumbers = 1;
302 ctx->cfg.max_repo_count = 50; 304 ctx->cfg.max_repo_count = 50;
303 ctx->cfg.max_commit_count = 50; 305 ctx->cfg.max_commit_count = 50;
304 ctx->cfg.max_lock_attempts = 5; 306 ctx->cfg.max_lock_attempts = 5;
305 ctx->cfg.max_msg_len = 80; 307 ctx->cfg.max_msg_len = 80;
306 ctx->cfg.max_repodesc_len = 80; 308 ctx->cfg.max_repodesc_len = 80;
307 ctx->cfg.max_blob_size = 0; 309 ctx->cfg.max_blob_size = 0;
308 ctx->cfg.max_stats = 0; 310 ctx->cfg.max_stats = 0;
309 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 311 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
310 ctx->cfg.project_list = NULL; 312 ctx->cfg.project_list = NULL;
311 ctx->cfg.renamelimit = -1; 313 ctx->cfg.renamelimit = -1;
312 ctx->cfg.remove_suffix = 0; 314 ctx->cfg.remove_suffix = 0;
313 ctx->cfg.robots = "index, nofollow"; 315 ctx->cfg.robots = "index, nofollow";
314 ctx->cfg.root_title = "Git repository browser"; 316 ctx->cfg.root_title = "Git repository browser";
315 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 317 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
316 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 318 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
317 ctx->cfg.section = ""; 319 ctx->cfg.section = "";
318 ctx->cfg.summary_branches = 10; 320 ctx->cfg.summary_branches = 10;
319 ctx->cfg.summary_log = 10; 321 ctx->cfg.summary_log = 10;
320 ctx->cfg.summary_tags = 10; 322 ctx->cfg.summary_tags = 10;
321 ctx->cfg.max_atom_items = 10; 323 ctx->cfg.max_atom_items = 10;
322 ctx->cfg.ssdiff = 0; 324 ctx->cfg.ssdiff = 0;
323 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 325 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
324 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 326 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
325 ctx->env.https = xstrdupn(getenv("HTTPS")); 327 ctx->env.https = xstrdupn(getenv("HTTPS"));
diff --git a/cgit.h b/cgit.h
index 4090cd4..9b269cc 100644
--- a/cgit.h
+++ b/cgit.h
@@ -80,234 +80,235 @@ struct cgit_repo {
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 *robots; 171 char *robots;
172 char *root_title; 172 char *root_title;
173 char *root_desc; 173 char *root_desc;
174 char *root_readme; 174 char *root_readme;
175 char *script_name; 175 char *script_name;
176 char *section; 176 char *section;
177 char *virtual_root; 177 char *virtual_root;
178 int cache_size; 178 int cache_size;
179 int cache_dynamic_ttl; 179 int cache_dynamic_ttl;
180 int cache_max_create_time; 180 int cache_max_create_time;
181 int cache_repo_ttl; 181 int cache_repo_ttl;
182 int cache_root_ttl; 182 int cache_root_ttl;
183 int cache_scanrc_ttl; 183 int cache_scanrc_ttl;
184 int cache_static_ttl; 184 int cache_static_ttl;
185 int embedded; 185 int embedded;
186 int enable_filter_overrides; 186 int enable_filter_overrides;
187 int enable_gitweb_owner; 187 int enable_gitweb_owner;
188 int enable_index_links; 188 int enable_index_links;
189 int enable_log_filecount; 189 int enable_log_filecount;
190 int enable_log_linecount; 190 int enable_log_linecount;
191 int enable_remote_branches; 191 int enable_remote_branches;
192 int enable_subject_links; 192 int enable_subject_links;
193 int enable_tree_linenumbers; 193 int enable_tree_linenumbers;
194 int local_time; 194 int local_time;
195 int max_atom_items; 195 int max_atom_items;
196 int max_repo_count; 196 int max_repo_count;
197 int max_commit_count; 197 int max_commit_count;
198 int max_lock_attempts; 198 int max_lock_attempts;
199 int max_msg_len; 199 int max_msg_len;
200 int max_repodesc_len; 200 int max_repodesc_len;
201 int max_blob_size; 201 int max_blob_size;
202 int max_stats; 202 int max_stats;
203 int nocache; 203 int nocache;
204 int noplainemail; 204 int noplainemail;
205 int noheader; 205 int noheader;
206 int renamelimit; 206 int renamelimit;
207 int remove_suffix; 207 int remove_suffix;
208 int section_from_path;
208 int snapshots; 209 int snapshots;
209 int summary_branches; 210 int summary_branches;
210 int summary_log; 211 int summary_log;
211 int summary_tags; 212 int summary_tags;
212 int ssdiff; 213 int ssdiff;
213 struct string_list mimetypes; 214 struct string_list mimetypes;
214 struct cgit_filter *about_filter; 215 struct cgit_filter *about_filter;
215 struct cgit_filter *commit_filter; 216 struct cgit_filter *commit_filter;
216 struct cgit_filter *source_filter; 217 struct cgit_filter *source_filter;
217}; 218};
218 219
219struct cgit_page { 220struct cgit_page {
220 time_t modified; 221 time_t modified;
221 time_t expires; 222 time_t expires;
222 size_t size; 223 size_t size;
223 char *mimetype; 224 char *mimetype;
224 char *charset; 225 char *charset;
225 char *filename; 226 char *filename;
226 char *etag; 227 char *etag;
227 char *title; 228 char *title;
228 int status; 229 int status;
229 char *statusmsg; 230 char *statusmsg;
230}; 231};
231 232
232struct cgit_environment { 233struct cgit_environment {
233 char *cgit_config; 234 char *cgit_config;
234 char *http_host; 235 char *http_host;
235 char *https; 236 char *https;
236 char *no_http; 237 char *no_http;
237 char *path_info; 238 char *path_info;
238 char *query_string; 239 char *query_string;
239 char *request_method; 240 char *request_method;
240 char *script_name; 241 char *script_name;
241 char *server_name; 242 char *server_name;
242 char *server_port; 243 char *server_port;
243}; 244};
244 245
245struct cgit_context { 246struct cgit_context {
246 struct cgit_environment env; 247 struct cgit_environment env;
247 struct cgit_query qry; 248 struct cgit_query qry;
248 struct cgit_config cfg; 249 struct cgit_config cfg;
249 struct cgit_repo *repo; 250 struct cgit_repo *repo;
250 struct cgit_page page; 251 struct cgit_page page;
251}; 252};
252 253
253struct cgit_snapshot_format { 254struct cgit_snapshot_format {
254 const char *suffix; 255 const char *suffix;
255 const char *mimetype; 256 const char *mimetype;
256 write_archive_fn_t write_func; 257 write_archive_fn_t write_func;
257 int bit; 258 int bit;
258}; 259};
259 260
260extern const char *cgit_version; 261extern const char *cgit_version;
261 262
262extern struct cgit_repolist cgit_repolist; 263extern struct cgit_repolist cgit_repolist;
263extern struct cgit_context ctx; 264extern struct cgit_context ctx;
264extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 265extern const struct cgit_snapshot_format cgit_snapshot_formats[];
265 266
266extern struct cgit_repo *cgit_add_repo(const char *url); 267extern struct cgit_repo *cgit_add_repo(const char *url);
267extern struct cgit_repo *cgit_get_repoinfo(const char *url); 268extern struct cgit_repo *cgit_get_repoinfo(const char *url);
268extern void cgit_repo_config_cb(const char *name, const char *value); 269extern void cgit_repo_config_cb(const char *name, const char *value);
269 270
270extern int chk_zero(int result, char *msg); 271extern int chk_zero(int result, char *msg);
271extern int chk_positive(int result, char *msg); 272extern int chk_positive(int result, char *msg);
272extern int chk_non_negative(int result, char *msg); 273extern int chk_non_negative(int result, char *msg);
273 274
274extern char *trim_end(const char *str, char c); 275extern char *trim_end(const char *str, char c);
275extern char *strlpart(char *txt, int maxlen); 276extern char *strlpart(char *txt, int maxlen);
276extern char *strrpart(char *txt, int maxlen); 277extern char *strrpart(char *txt, int maxlen);
277 278
278extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 279extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
279extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 280extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
280 int flags, void *cb_data); 281 int flags, void *cb_data);
281 282
282extern void *cgit_free_commitinfo(struct commitinfo *info); 283extern void *cgit_free_commitinfo(struct commitinfo *info);
283 284
284extern int cgit_diff_files(const unsigned char *old_sha1, 285extern int cgit_diff_files(const unsigned char *old_sha1,
285 const unsigned char *new_sha1, 286 const unsigned char *new_sha1,
286 unsigned long *old_size, unsigned long *new_size, 287 unsigned long *old_size, unsigned long *new_size,
287 int *binary, int context, int ignorews, 288 int *binary, int context, int ignorews,
288 linediff_fn fn); 289 linediff_fn fn);
289 290
290extern void cgit_diff_tree(const unsigned char *old_sha1, 291extern void cgit_diff_tree(const unsigned char *old_sha1,
291 const unsigned char *new_sha1, 292 const unsigned char *new_sha1,
292 filepair_fn fn, const char *prefix, int ignorews); 293 filepair_fn fn, const char *prefix, int ignorews);
293 294
294extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 295extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
295 296
296extern char *fmt(const char *format,...); 297extern char *fmt(const char *format,...);
297 298
298extern struct commitinfo *cgit_parse_commit(struct commit *commit); 299extern struct commitinfo *cgit_parse_commit(struct commit *commit);
299extern struct taginfo *cgit_parse_tag(struct tag *tag); 300extern struct taginfo *cgit_parse_tag(struct tag *tag);
300extern void cgit_parse_url(const char *url); 301extern void cgit_parse_url(const char *url);
301 302
302extern const char *cgit_repobasename(const char *reponame); 303extern const char *cgit_repobasename(const char *reponame);
303 304
304extern int cgit_parse_snapshots_mask(const char *str); 305extern int cgit_parse_snapshots_mask(const char *str);
305 306
306extern int cgit_open_filter(struct cgit_filter *filter); 307extern int cgit_open_filter(struct cgit_filter *filter);
307extern int cgit_close_filter(struct cgit_filter *filter); 308extern int cgit_close_filter(struct cgit_filter *filter);
308 309
309extern int readfile(const char *path, char **buf, size_t *size); 310extern int readfile(const char *path, char **buf, size_t *size);
310 311
311extern char *expand_macros(const char *txt); 312extern char *expand_macros(const char *txt);
312 313
313#endif /* CGIT_H */ 314#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index c643fae..95782df 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -153,256 +153,262 @@ include::
153 file is parsed. Default value: none. 153 file is parsed. Default value: none.
154 154
155index-header:: 155index-header::
156 The content of the file specified with this option will be included 156 The content of the file specified with this option will be included
157 verbatim above the repository index. This setting is deprecated, and 157 verbatim above the repository index. This setting is deprecated, and
158 will not be supported by cgit-1.0 (use root-readme instead). Default 158 will not be supported by cgit-1.0 (use root-readme instead). Default
159 value: none. 159 value: none.
160 160
161index-info:: 161index-info::
162 The content of the file specified with this option will be included 162 The content of the file specified with this option will be included
163 verbatim below the heading on the repository index page. This setting 163 verbatim below the heading on the repository index page. This setting
164 is deprecated, and will not be supported by cgit-1.0 (use root-desc 164 is deprecated, and will not be supported by cgit-1.0 (use root-desc
165 instead). Default value: none. 165 instead). Default value: none.
166 166
167local-time:: 167local-time::
168 Flag which, if set to "1", makes cgit print commit and tag times in the 168 Flag which, if set to "1", makes cgit print commit and tag times in the
169 servers timezone. Default value: "0". 169 servers timezone. Default value: "0".
170 170
171logo:: 171logo::
172 Url which specifies the source of an image which will be used as a logo 172 Url which specifies the source of an image which will be used as a logo
173 on all cgit pages. Default value: "/cgit.png". 173 on all cgit pages. Default value: "/cgit.png".
174 174
175logo-link:: 175logo-link::
176 Url loaded when clicking on the cgit logo image. If unspecified the 176 Url loaded when clicking on the cgit logo image. If unspecified the
177 calculated url of the repository index page will be used. Default 177 calculated url of the repository index page will be used. Default
178 value: none. 178 value: none.
179 179
180max-atom-items:: 180max-atom-items::
181 Specifies the number of items to display in atom feeds view. Default 181 Specifies the number of items to display in atom feeds view. Default
182 value: "10". 182 value: "10".
183 183
184max-commit-count:: 184max-commit-count::
185 Specifies the number of entries to list per page in "log" view. Default 185 Specifies the number of entries to list per page in "log" view. Default
186 value: "50". 186 value: "50".
187 187
188max-message-length:: 188max-message-length::
189 Specifies the maximum number of commit message characters to display in 189 Specifies the maximum number of commit message characters to display in
190 "log" view. Default value: "80". 190 "log" view. Default value: "80".
191 191
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
237remove-suffix:: 237remove-suffix::
238 If set to "1" and scan-path is enabled, if any repositories are found 238 If set to "1" and scan-path is enabled, if any repositories are found
239 with a suffix of ".git", this suffix will be removed for the url and 239 with a suffix of ".git", this suffix will be removed for the url and
240 name. Default value: "0". See also: scan-path. 240 name. Default value: "0". See also: scan-path.
241 241
242renamelimit:: 242renamelimit::
243 Maximum number of files to consider when detecting renames. The value 243 Maximum number of files to consider when detecting renames. The value
244 "-1" uses the compiletime value in git (for further info, look at 244 "-1" uses the compiletime value in git (for further info, look at
245 `man git-diff`). Default value: "-1". 245 `man git-diff`). Default value: "-1".
246 246
247repo.group:: 247repo.group::
248 Legacy alias for "section". This option is deprecated and will not be 248 Legacy alias for "section". This option is deprecated and will not be
249 supported in cgit-1.0. 249 supported in cgit-1.0.
250 250
251robots:: 251robots::
252 Text used as content for the "robots" meta-tag. Default value: 252 Text used as content for the "robots" meta-tag. Default value:
253 "index, nofollow". 253 "index, nofollow".
254 254
255root-desc:: 255root-desc::
256 Text printed below the heading on the repository index page. Default 256 Text printed below the heading on the repository index page. Default
257 value: "a fast webinterface for the git dscm". 257 value: "a fast webinterface for the git dscm".
258 258
259root-readme:: 259root-readme::
260 The content of the file specified with this option will be included 260 The content of the file specified with this option will be included
261 verbatim below the "about" link on the repository index page. Default 261 verbatim below the "about" link on the repository index page. Default
262 value: none. 262 value: none.
263 263
264root-title:: 264root-title::
265 Text printed as heading on the repository index page. Default value: 265 Text printed as heading on the repository index page. Default value:
266 "Git Repository Browser". 266 "Git Repository Browser".
267 267
268scan-path:: 268scan-path::
269 A path which will be scanned for repositories. If caching is enabled, 269 A path which will be scanned for repositories. If caching is enabled,
270 the result will be cached as a cgitrc include-file in the cache 270 the result will be cached as a cgitrc include-file in the cache
271 directory. If project-list has been defined prior to scan-path, 271 directory. If project-list has been defined prior to scan-path,
272 scan-path loads only the directories listed in the file pointed to by 272 scan-path loads only the directories listed in the file pointed to by
273 project-list. Default value: none. See also: cache-scanrc-ttl, 273 project-list. Default value: none. See also: cache-scanrc-ttl,
274 project-list. 274 project-list.
275 275
276section:: 276section::
277 The name of the current repository section - all repositories defined 277 The name of the current repository section - all repositories defined
278 after this option will inherit the current section name. Default value: 278 after this option will inherit the current section name. Default value:
279 none. 279 none.
280 280
281section-from-path::
282 A number which, if specified before scan-path, specifies how many
283 path elements from each repo path to use as a default section name.
284 If negative, cgit will discard the specified number of path elements
285 above the repo directory. Default value: 0.
286
281side-by-side-diffs:: 287side-by-side-diffs::
282 If set to "1" shows side-by-side diffs instead of unidiffs per 288 If set to "1" shows side-by-side diffs instead of unidiffs per
283 default. Default value: "0". 289 default. Default value: "0".
284 290
285snapshots:: 291snapshots::
286 Text which specifies the default set of snapshot formats generated by 292 Text which specifies the default set of snapshot formats generated by
287 cgit. The value is a space-separated list of zero or more of the 293 cgit. The value is a space-separated list of zero or more of the
288 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 294 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
289 295
290source-filter:: 296source-filter::
291 Specifies a command which will be invoked to format plaintext blobs 297 Specifies a command which will be invoked to format plaintext blobs
292 in the tree view. The command will get the blob content on its STDIN 298 in the tree view. The command will get the blob content on its STDIN
293 and the name of the blob as its only command line argument. The STDOUT 299 and the name of the blob as its only command line argument. The STDOUT
294 from the command will be included verbatim as the blob contents, i.e. 300 from the command will be included verbatim as the blob contents, i.e.
295 this can be used to implement e.g. syntax highlighting. Default value: 301 this can be used to implement e.g. syntax highlighting. Default value:
296 none. 302 none.
297 303
298summary-branches:: 304summary-branches::
299 Specifies the number of branches to display in the repository "summary" 305 Specifies the number of branches to display in the repository "summary"
300 view. Default value: "10". 306 view. Default value: "10".
301 307
302summary-log:: 308summary-log::
303 Specifies the number of log entries to display in the repository 309 Specifies the number of log entries to display in the repository
304 "summary" view. Default value: "10". 310 "summary" view. Default value: "10".
305 311
306summary-tags:: 312summary-tags::
307 Specifies the number of tags to display in the repository "summary" 313 Specifies the number of tags to display in the repository "summary"
308 view. Default value: "10". 314 view. Default value: "10".
309 315
310virtual-root:: 316virtual-root::
311 Url which, if specified, will be used as root for all cgit links. It 317 Url which, if specified, will be used as root for all cgit links. It
312 will also cause cgit to generate 'virtual urls', i.e. urls like 318 will also cause cgit to generate 'virtual urls', i.e. urls like
313 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 319 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
314 value: none. 320 value: none.
315 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 321 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
316 same kind of virtual urls, so this option will probably be deprecated. 322 same kind of virtual urls, so this option will probably be deprecated.
317 323
318REPOSITORY SETTINGS 324REPOSITORY SETTINGS
319------------------- 325-------------------
320repo.about-filter:: 326repo.about-filter::
321 Override the default about-filter. Default value: none. See also: 327 Override the default about-filter. Default value: none. See also:
322 "enable-filter-overrides". 328 "enable-filter-overrides".
323 329
324repo.clone-url:: 330repo.clone-url::
325 A list of space-separated urls which can be used to clone this repo. 331 A list of space-separated urls which can be used to clone this repo.
326 Default value: none. 332 Default value: none.
327 333
328repo.commit-filter:: 334repo.commit-filter::
329 Override the default commit-filter. Default value: none. See also: 335 Override the default commit-filter. Default value: none. See also:
330 "enable-filter-overrides". 336 "enable-filter-overrides".
331 337
332repo.defbranch:: 338repo.defbranch::
333 The name of the default branch for this repository. If no such branch 339 The name of the default branch for this repository. If no such branch
334 exists in the repository, the first branch name (when sorted) is used 340 exists in the repository, the first branch name (when sorted) is used
335 as default instead. Default value: "master". 341 as default instead. Default value: "master".
336 342
337repo.desc:: 343repo.desc::
338 The value to show as repository description. Default value: none. 344 The value to show as repository description. Default value: none.
339 345
340repo.enable-log-filecount:: 346repo.enable-log-filecount::
341 A flag which can be used to disable the global setting 347 A flag which can be used to disable the global setting
342 `enable-log-filecount'. Default value: none. 348 `enable-log-filecount'. Default value: none.
343 349
344repo.enable-log-linecount:: 350repo.enable-log-linecount::
345 A flag which can be used to disable the global setting 351 A flag which can be used to disable the global setting
346 `enable-log-linecount'. Default value: none. 352 `enable-log-linecount'. Default value: none.
347 353
348repo.enable-remote-branches:: 354repo.enable-remote-branches::
349 Flag which, when set to "1", will make cgit display remote branches 355 Flag which, when set to "1", will make cgit display remote branches
350 in the summary and refs views. Default value: <enable-remote-branches>. 356 in the summary and refs views. Default value: <enable-remote-branches>.
351 357
352repo.enable-subject-links:: 358repo.enable-subject-links::
353 A flag which can be used to override the global setting 359 A flag which can be used to override the global setting
354 `enable-subject-links'. Default value: none. 360 `enable-subject-links'. Default value: none.
355 361
356repo.max-stats:: 362repo.max-stats::
357 Override the default maximum statistics period. Valid values are equal 363 Override the default maximum statistics period. Valid values are equal
358 to the values specified for the global "max-stats" setting. Default 364 to the values specified for the global "max-stats" setting. Default
359 value: none. 365 value: none.
360 366
361repo.name:: 367repo.name::
362 The value to show as repository name. Default value: <repo.url>. 368 The value to show as repository name. Default value: <repo.url>.
363 369
364repo.owner:: 370repo.owner::
365 A value used to identify the owner of the repository. Default value: 371 A value used to identify the owner of the repository. Default value:
366 none. 372 none.
367 373
368repo.path:: 374repo.path::
369 An absolute path to the repository directory. For non-bare repositories 375 An absolute path to the repository directory. For non-bare repositories
370 this is the .git-directory. Default value: none. 376 this is the .git-directory. Default value: none.
371 377
372repo.readme:: 378repo.readme::
373 A path (relative to <repo.path>) which specifies a file to include 379 A path (relative to <repo.path>) which specifies a file to include
374 verbatim as the "About" page for this repo. You may also specify a 380 verbatim as the "About" page for this repo. You may also specify a
375 git refspec by head or by hash by prepending the refspec followed by 381 git refspec by head or by hash by prepending the refspec followed by
376 a colon. For example, "master:docs/readme.mkd" Default value: none. 382 a colon. For example, "master:docs/readme.mkd" Default value: none.
377 383
378repo.snapshots:: 384repo.snapshots::
379 A mask of allowed snapshot-formats for this repo, restricted by the 385 A mask of allowed snapshot-formats for this repo, restricted by the
380 "snapshots" global setting. Default value: <snapshots>. 386 "snapshots" global setting. Default value: <snapshots>.
381 387
382repo.section:: 388repo.section::
383 Override the current section name for this repository. Default value: 389 Override the current section name for this repository. Default value:
384 none. 390 none.
385 391
386repo.source-filter:: 392repo.source-filter::
387 Override the default source-filter. Default value: none. See also: 393 Override the default source-filter. Default value: none. See also:
388 "enable-filter-overrides". 394 "enable-filter-overrides".
389 395
390repo.url:: 396repo.url::
391 The relative url used to access the repository. This must be the first 397 The relative url used to access the repository. This must be the first
392 setting specified for each repo. Default value: none. 398 setting specified for each repo. Default value: none.
393 399
394 400
395REPOSITORY-SPECIFIC CGITRC FILE 401REPOSITORY-SPECIFIC CGITRC FILE
396------------------------------- 402-------------------------------
397When the option "scan-path" is used to auto-discover git repositories, cgit 403When the option "scan-path" is used to auto-discover git repositories, cgit
398will try to parse the file "cgitrc" within any found repository. Such a 404will try to parse the file "cgitrc" within any found repository. Such a
399repo-specific config file may contain any of the repo-specific options 405repo-specific config file may contain any of the repo-specific options
400described above, except "repo.url" and "repo.path". Additionally, the "filter" 406described above, except "repo.url" and "repo.path". Additionally, the "filter"
401options are only acknowledged in repo-specific config files when 407options are only acknowledged in repo-specific config files when
402"enable-filter-overrides" is set to "1". 408"enable-filter-overrides" is set to "1".
403 409
404Note: the "repo." prefix is dropped from the option names in repo-specific 410Note: the "repo." prefix is dropped from the option names in repo-specific
405config files, e.g. "repo.desc" becomes "desc". 411config files, e.g. "repo.desc" becomes "desc".
406 412
407 413
408EXAMPLE CGITRC FILE 414EXAMPLE CGITRC FILE
diff --git a/scan-tree.c b/scan-tree.c
index e987824..6ba9193 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -1,203 +1,233 @@
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)
65{
66 while (from >= s && *from != c)
67 from--;
68 return from < s ? NULL : from;
69}
70
64static 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)
65{ 72{
66 struct stat st; 73 struct stat st;
67 struct passwd *pwd; 74 struct passwd *pwd;
68 char *p; 75 char *rel, *p, *slash;
76 int n;
69 size_t size; 77 size_t size;
70 78
71 if (stat(path, &st)) { 79 if (stat(path, &st)) {
72 fprintf(stderr, "Error accessing %s: %s (%d)\n", 80 fprintf(stderr, "Error accessing %s: %s (%d)\n",
73 path, strerror(errno), errno); 81 path, strerror(errno), errno);
74 return; 82 return;
75 } 83 }
76 if (!stat(fmt("%s/noweb", path), &st)) 84 if (!stat(fmt("%s/noweb", path), &st))
77 return; 85 return;
78 86
79 owner = NULL; 87 owner = NULL;
80 if (ctx.cfg.enable_gitweb_owner) 88 if (ctx.cfg.enable_gitweb_owner)
81 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL); 89 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
82 if (base == path) 90 if (base == path)
83 p = fmt("%s", path); 91 rel = xstrdup(fmt("%s", path));
84 else 92 else
85 p = fmt("%s", path + strlen(base) + 1); 93 rel = xstrdup(fmt("%s", path + strlen(base) + 1));
86 94
87 if (!strcmp(p + strlen(p) - 5, "/.git")) 95 if (!strcmp(rel + strlen(rel) - 5, "/.git"))
88 p[strlen(p) - 5] = '\0'; 96 rel[strlen(rel) - 5] = '\0';
89 97
90 repo = cgit_add_repo(xstrdup(p)); 98 repo = cgit_add_repo(rel);
91 if (ctx.cfg.remove_suffix) 99 if (ctx.cfg.remove_suffix)
92 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) 100 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
93 *p = '\0'; 101 *p = '\0';
94 repo->name = repo->url; 102 repo->name = repo->url;
95 repo->path = xstrdup(path); 103 repo->path = xstrdup(path);
96 while (!owner) { 104 while (!owner) {
97 if ((pwd = getpwuid(st.st_uid)) == NULL) { 105 if ((pwd = getpwuid(st.st_uid)) == NULL) {
98 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 106 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
99 path, strerror(errno), errno); 107 path, strerror(errno), errno);
100 break; 108 break;
101 } 109 }
102 if (pwd->pw_gecos) 110 if (pwd->pw_gecos)
103 if ((p = strchr(pwd->pw_gecos, ','))) 111 if ((p = strchr(pwd->pw_gecos, ',')))
104 *p = '\0'; 112 *p = '\0';
105 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); 113 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
106 } 114 }
107 repo->owner = owner; 115 repo->owner = owner;
108 116
109 p = fmt("%s/description", path); 117 p = fmt("%s/description", path);
110 if (!stat(p, &st)) 118 if (!stat(p, &st))
111 readfile(p, &repo->desc, &size); 119 readfile(p, &repo->desc, &size);
112 120
113 p = fmt("%s/README.html", path); 121 p = fmt("%s/README.html", path);
114 if (!stat(p, &st)) 122 if (!stat(p, &st))
115 repo->readme = "README.html"; 123 repo->readme = "README.html";
124 if (ctx.cfg.section_from_path) {
125 n = ctx.cfg.section_from_path;
126 if (n > 0) {
127 slash = rel;
128 while (slash && n && (slash = strchr(slash, '/')))
129 n--;
130 } else {
131 slash = rel + strlen(rel);
132 while (slash && n && (slash = xstrrchr(rel, slash, '/')))
133 n++;
134 }
135 if (slash && !n) {
136 *slash = '\0';
137 repo->section = xstrdup(rel);
138 *slash = '/';
139 if (!prefixcmp(repo->name, repo->section)) {
140 repo->name += strlen(repo->section);
141 if (*repo->name == '/')
142 repo->name++;
143 }
144 }
145 }
116 146
117 p = fmt("%s/cgitrc", path); 147 p = fmt("%s/cgitrc", path);
118 if (!stat(p, &st)) { 148 if (!stat(p, &st)) {
119 config_fn = fn; 149 config_fn = fn;
120 parse_configfile(xstrdup(p), &repo_config); 150 parse_configfile(xstrdup(p), &repo_config);
121 } 151 }
122} 152}
123 153
124static void scan_path(const char *base, const char *path, repo_config_fn fn) 154static void scan_path(const char *base, const char *path, repo_config_fn fn)
125{ 155{
126 DIR *dir; 156 DIR *dir;
127 struct dirent *ent; 157 struct dirent *ent;
128 char *buf; 158 char *buf;
129 struct stat st; 159 struct stat st;
130 160
131 if (is_git_dir(path)) { 161 if (is_git_dir(path)) {
132 add_repo(base, path, fn); 162 add_repo(base, path, fn);
133 return; 163 return;
134 } 164 }
135 if (is_git_dir(fmt("%s/.git", path))) { 165 if (is_git_dir(fmt("%s/.git", path))) {
136 add_repo(base, fmt("%s/.git", path), fn); 166 add_repo(base, fmt("%s/.git", path), fn);
137 return; 167 return;
138 } 168 }
139 dir = opendir(path); 169 dir = opendir(path);
140 if (!dir) { 170 if (!dir) {
141 fprintf(stderr, "Error opening directory %s: %s (%d)\n", 171 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
142 path, strerror(errno), errno); 172 path, strerror(errno), errno);
143 return; 173 return;
144 } 174 }
145 while((ent = readdir(dir)) != NULL) { 175 while((ent = readdir(dir)) != NULL) {
146 if (ent->d_name[0] == '.') { 176 if (ent->d_name[0] == '.') {
147 if (ent->d_name[1] == '\0') 177 if (ent->d_name[1] == '\0')
148 continue; 178 continue;
149 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 179 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
150 continue; 180 continue;
151 } 181 }
152 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 182 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
153 if (!buf) { 183 if (!buf) {
154 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 184 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
155 path, strerror(errno), errno); 185 path, strerror(errno), errno);
156 exit(1); 186 exit(1);
157 } 187 }
158 sprintf(buf, "%s/%s", path, ent->d_name); 188 sprintf(buf, "%s/%s", path, ent->d_name);
159 if (stat(buf, &st)) { 189 if (stat(buf, &st)) {
160 fprintf(stderr, "Error checking path %s: %s (%d)\n", 190 fprintf(stderr, "Error checking path %s: %s (%d)\n",
161 buf, strerror(errno), errno); 191 buf, strerror(errno), errno);
162 free(buf); 192 free(buf);
163 continue; 193 continue;
164 } 194 }
165 if (S_ISDIR(st.st_mode)) 195 if (S_ISDIR(st.st_mode))
166 scan_path(base, buf, fn); 196 scan_path(base, buf, fn);
167 free(buf); 197 free(buf);
168 } 198 }
169 closedir(dir); 199 closedir(dir);
170} 200}
171 201
172#define lastc(s) s[strlen(s) - 1] 202#define lastc(s) s[strlen(s) - 1]
173 203
174void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) 204void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
175{ 205{
176 char line[MAX_PATH * 2], *z; 206 char line[MAX_PATH * 2], *z;
177 FILE *projects; 207 FILE *projects;
178 int err; 208 int err;
179 209
180 projects = fopen(projectsfile, "r"); 210 projects = fopen(projectsfile, "r");
181 if (!projects) { 211 if (!projects) {
182 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n", 212 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
183 projectsfile, strerror(errno), errno); 213 projectsfile, strerror(errno), errno);
184 } 214 }
185 while (fgets(line, sizeof(line), projects) != NULL) { 215 while (fgets(line, sizeof(line), projects) != NULL) {
186 for (z = &lastc(line); 216 for (z = &lastc(line);
187 strlen(line) && strchr("\n\r", *z); 217 strlen(line) && strchr("\n\r", *z);
188 z = &lastc(line)) 218 z = &lastc(line))
189 *z = '\0'; 219 *z = '\0';
190 if (strlen(line)) 220 if (strlen(line))
191 scan_path(path, fmt("%s/%s", path, line), fn); 221 scan_path(path, fmt("%s/%s", path, line), fn);
192 } 222 }
193 if ((err = ferror(projects))) { 223 if ((err = ferror(projects))) {
194 fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n", 224 fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
195 projectsfile, strerror(err), err); 225 projectsfile, strerror(err), err);
196 } 226 }
197 fclose(projects); 227 fclose(projects);
198} 228}
199 229
200void scan_tree(const char *path, repo_config_fn fn) 230void scan_tree(const char *path, repo_config_fn fn)
201{ 231{
202 scan_path(path, path, fn); 232 scan_path(path, path, fn);
203} 233}