summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c3
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt4
-rw-r--r--cmd.c2
4 files changed, 9 insertions, 1 deletions
diff --git a/cgit.c b/cgit.c
index ed2be64..bf64c9e 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,680 +1,683 @@
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 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cache.h" 10#include "cache.h"
11#include "cmd.h" 11#include "cmd.h"
12#include "configfile.h" 12#include "configfile.h"
13#include "html.h" 13#include "html.h"
14#include "ui-shared.h" 14#include "ui-shared.h"
15#include "ui-stats.h" 15#include "ui-stats.h"
16#include "scan-tree.h" 16#include "scan-tree.h"
17 17
18const char *cgit_version = CGIT_VERSION; 18const char *cgit_version = CGIT_VERSION;
19 19
20void add_mimetype(const char *name, const char *value) 20void add_mimetype(const char *name, const char *value)
21{ 21{
22 struct string_list_item *item; 22 struct string_list_item *item;
23 23
24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes); 24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);
25 item->util = xstrdup(value); 25 item->util = xstrdup(value);
26} 26}
27 27
28struct cgit_filter *new_filter(const char *cmd, int extra_args) 28struct cgit_filter *new_filter(const char *cmd, int extra_args)
29{ 29{
30 struct cgit_filter *f; 30 struct cgit_filter *f;
31 31
32 if (!cmd || !cmd[0]) 32 if (!cmd || !cmd[0])
33 return NULL; 33 return NULL;
34 34
35 f = xmalloc(sizeof(struct cgit_filter)); 35 f = xmalloc(sizeof(struct cgit_filter));
36 f->cmd = xstrdup(cmd); 36 f->cmd = xstrdup(cmd);
37 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 37 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
38 f->argv[0] = f->cmd; 38 f->argv[0] = f->cmd;
39 f->argv[1] = NULL; 39 f->argv[1] = NULL;
40 return f; 40 return f;
41} 41}
42 42
43static void process_cached_repolist(const char *path); 43static void process_cached_repolist(const char *path);
44 44
45void repo_config(struct cgit_repo *repo, const char *name, const char *value) 45void repo_config(struct cgit_repo *repo, const char *name, const char *value)
46{ 46{
47 if (!strcmp(name, "name")) 47 if (!strcmp(name, "name"))
48 repo->name = xstrdup(value); 48 repo->name = xstrdup(value);
49 else if (!strcmp(name, "clone-url")) 49 else if (!strcmp(name, "clone-url"))
50 repo->clone_url = xstrdup(value); 50 repo->clone_url = xstrdup(value);
51 else if (!strcmp(name, "desc")) 51 else if (!strcmp(name, "desc"))
52 repo->desc = xstrdup(value); 52 repo->desc = xstrdup(value);
53 else if (!strcmp(name, "owner")) 53 else if (!strcmp(name, "owner"))
54 repo->owner = xstrdup(value); 54 repo->owner = xstrdup(value);
55 else if (!strcmp(name, "defbranch")) 55 else if (!strcmp(name, "defbranch"))
56 repo->defbranch = xstrdup(value); 56 repo->defbranch = xstrdup(value);
57 else if (!strcmp(name, "snapshots")) 57 else if (!strcmp(name, "snapshots"))
58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
59 else if (!strcmp(name, "enable-log-filecount")) 59 else if (!strcmp(name, "enable-log-filecount"))
60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
61 else if (!strcmp(name, "enable-log-linecount")) 61 else if (!strcmp(name, "enable-log-linecount"))
62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
63 else if (!strcmp(name, "enable-remote-branches")) 63 else if (!strcmp(name, "enable-remote-branches"))
64 repo->enable_remote_branches = atoi(value); 64 repo->enable_remote_branches = atoi(value);
65 else if (!strcmp(name, "max-stats")) 65 else if (!strcmp(name, "max-stats"))
66 repo->max_stats = cgit_find_stats_period(value, NULL); 66 repo->max_stats = cgit_find_stats_period(value, NULL);
67 else if (!strcmp(name, "module-link")) 67 else if (!strcmp(name, "module-link"))
68 repo->module_link= xstrdup(value); 68 repo->module_link= xstrdup(value);
69 else if (!strcmp(name, "section")) 69 else if (!strcmp(name, "section"))
70 repo->section = xstrdup(value); 70 repo->section = xstrdup(value);
71 else if (!strcmp(name, "readme") && value != NULL) { 71 else if (!strcmp(name, "readme") && value != NULL) {
72 if (*value == '/') 72 if (*value == '/')
73 repo->readme = xstrdup(value); 73 repo->readme = xstrdup(value);
74 else 74 else
75 repo->readme = xstrdup(fmt("%s/%s", repo->path, value)); 75 repo->readme = xstrdup(fmt("%s/%s", repo->path, 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, "root-title")) 96 else if (!strcmp(name, "root-title"))
97 ctx.cfg.root_title = xstrdup(value); 97 ctx.cfg.root_title = xstrdup(value);
98 else if (!strcmp(name, "root-desc")) 98 else if (!strcmp(name, "root-desc"))
99 ctx.cfg.root_desc = xstrdup(value); 99 ctx.cfg.root_desc = xstrdup(value);
100 else if (!strcmp(name, "root-readme")) 100 else if (!strcmp(name, "root-readme"))
101 ctx.cfg.root_readme = xstrdup(value); 101 ctx.cfg.root_readme = xstrdup(value);
102 else if (!strcmp(name, "css")) 102 else if (!strcmp(name, "css"))
103 ctx.cfg.css = xstrdup(value); 103 ctx.cfg.css = xstrdup(value);
104 else if (!strcmp(name, "favicon")) 104 else if (!strcmp(name, "favicon"))
105 ctx.cfg.favicon = xstrdup(value); 105 ctx.cfg.favicon = xstrdup(value);
106 else if (!strcmp(name, "footer")) 106 else if (!strcmp(name, "footer"))
107 ctx.cfg.footer = xstrdup(value); 107 ctx.cfg.footer = xstrdup(value);
108 else if (!strcmp(name, "head-include")) 108 else if (!strcmp(name, "head-include"))
109 ctx.cfg.head_include = xstrdup(value); 109 ctx.cfg.head_include = xstrdup(value);
110 else if (!strcmp(name, "header")) 110 else if (!strcmp(name, "header"))
111 ctx.cfg.header = xstrdup(value); 111 ctx.cfg.header = xstrdup(value);
112 else if (!strcmp(name, "logo")) 112 else if (!strcmp(name, "logo"))
113 ctx.cfg.logo = xstrdup(value); 113 ctx.cfg.logo = xstrdup(value);
114 else if (!strcmp(name, "index-header")) 114 else if (!strcmp(name, "index-header"))
115 ctx.cfg.index_header = xstrdup(value); 115 ctx.cfg.index_header = xstrdup(value);
116 else if (!strcmp(name, "index-info")) 116 else if (!strcmp(name, "index-info"))
117 ctx.cfg.index_info = xstrdup(value); 117 ctx.cfg.index_info = xstrdup(value);
118 else if (!strcmp(name, "logo-link")) 118 else if (!strcmp(name, "logo-link"))
119 ctx.cfg.logo_link = xstrdup(value); 119 ctx.cfg.logo_link = xstrdup(value);
120 else if (!strcmp(name, "module-link")) 120 else if (!strcmp(name, "module-link"))
121 ctx.cfg.module_link = xstrdup(value); 121 ctx.cfg.module_link = xstrdup(value);
122 else if (!strcmp(name, "virtual-root")) { 122 else if (!strcmp(name, "virtual-root")) {
123 ctx.cfg.virtual_root = trim_end(value, '/'); 123 ctx.cfg.virtual_root = trim_end(value, '/');
124 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 124 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
125 ctx.cfg.virtual_root = ""; 125 ctx.cfg.virtual_root = "";
126 } else if (!strcmp(name, "nocache")) 126 } else if (!strcmp(name, "nocache"))
127 ctx.cfg.nocache = atoi(value); 127 ctx.cfg.nocache = atoi(value);
128 else if (!strcmp(name, "noplainemail")) 128 else if (!strcmp(name, "noplainemail"))
129 ctx.cfg.noplainemail = atoi(value); 129 ctx.cfg.noplainemail = atoi(value);
130 else if (!strcmp(name, "noheader")) 130 else if (!strcmp(name, "noheader"))
131 ctx.cfg.noheader = atoi(value); 131 ctx.cfg.noheader = atoi(value);
132 else if (!strcmp(name, "snapshots")) 132 else if (!strcmp(name, "snapshots"))
133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
134 else if (!strcmp(name, "enable-filter-overrides")) 134 else if (!strcmp(name, "enable-filter-overrides"))
135 ctx.cfg.enable_filter_overrides = atoi(value); 135 ctx.cfg.enable_filter_overrides = atoi(value);
136 else if (!strcmp(name, "enable-index-links")) 136 else if (!strcmp(name, "enable-index-links"))
137 ctx.cfg.enable_index_links = atoi(value); 137 ctx.cfg.enable_index_links = atoi(value);
138 else if (!strcmp(name, "enable-log-filecount")) 138 else if (!strcmp(name, "enable-log-filecount"))
139 ctx.cfg.enable_log_filecount = atoi(value); 139 ctx.cfg.enable_log_filecount = atoi(value);
140 else if (!strcmp(name, "enable-log-linecount")) 140 else if (!strcmp(name, "enable-log-linecount"))
141 ctx.cfg.enable_log_linecount = atoi(value); 141 ctx.cfg.enable_log_linecount = atoi(value);
142 else if (!strcmp(name, "enable-remote-branches")) 142 else if (!strcmp(name, "enable-remote-branches"))
143 ctx.cfg.enable_remote_branches = atoi(value); 143 ctx.cfg.enable_remote_branches = atoi(value);
144 else if (!strcmp(name, "enable-tree-linenumbers")) 144 else if (!strcmp(name, "enable-tree-linenumbers"))
145 ctx.cfg.enable_tree_linenumbers = atoi(value); 145 ctx.cfg.enable_tree_linenumbers = atoi(value);
146 else if (!strcmp(name, "max-stats")) 146 else if (!strcmp(name, "max-stats"))
147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
148 else if (!strcmp(name, "cache-size")) 148 else if (!strcmp(name, "cache-size"))
149 ctx.cfg.cache_size = atoi(value); 149 ctx.cfg.cache_size = atoi(value);
150 else if (!strcmp(name, "cache-root")) 150 else if (!strcmp(name, "cache-root"))
151 ctx.cfg.cache_root = xstrdup(value); 151 ctx.cfg.cache_root = xstrdup(value);
152 else if (!strcmp(name, "cache-root-ttl")) 152 else if (!strcmp(name, "cache-root-ttl"))
153 ctx.cfg.cache_root_ttl = atoi(value); 153 ctx.cfg.cache_root_ttl = atoi(value);
154 else if (!strcmp(name, "cache-repo-ttl")) 154 else if (!strcmp(name, "cache-repo-ttl"))
155 ctx.cfg.cache_repo_ttl = atoi(value); 155 ctx.cfg.cache_repo_ttl = atoi(value);
156 else if (!strcmp(name, "cache-scanrc-ttl")) 156 else if (!strcmp(name, "cache-scanrc-ttl"))
157 ctx.cfg.cache_scanrc_ttl = atoi(value); 157 ctx.cfg.cache_scanrc_ttl = atoi(value);
158 else if (!strcmp(name, "cache-static-ttl")) 158 else if (!strcmp(name, "cache-static-ttl"))
159 ctx.cfg.cache_static_ttl = atoi(value); 159 ctx.cfg.cache_static_ttl = atoi(value);
160 else if (!strcmp(name, "cache-dynamic-ttl")) 160 else if (!strcmp(name, "cache-dynamic-ttl"))
161 ctx.cfg.cache_dynamic_ttl = atoi(value); 161 ctx.cfg.cache_dynamic_ttl = atoi(value);
162 else if (!strcmp(name, "about-filter")) 162 else if (!strcmp(name, "about-filter"))
163 ctx.cfg.about_filter = new_filter(value, 0); 163 ctx.cfg.about_filter = new_filter(value, 0);
164 else if (!strcmp(name, "commit-filter")) 164 else if (!strcmp(name, "commit-filter"))
165 ctx.cfg.commit_filter = new_filter(value, 0); 165 ctx.cfg.commit_filter = new_filter(value, 0);
166 else if (!strcmp(name, "embedded")) 166 else if (!strcmp(name, "embedded"))
167 ctx.cfg.embedded = atoi(value); 167 ctx.cfg.embedded = atoi(value);
168 else if (!strcmp(name, "max-atom-items"))
169 ctx.cfg.max_atom_items = atoi(value);
168 else if (!strcmp(name, "max-message-length")) 170 else if (!strcmp(name, "max-message-length"))
169 ctx.cfg.max_msg_len = atoi(value); 171 ctx.cfg.max_msg_len = atoi(value);
170 else if (!strcmp(name, "max-repodesc-length")) 172 else if (!strcmp(name, "max-repodesc-length"))
171 ctx.cfg.max_repodesc_len = atoi(value); 173 ctx.cfg.max_repodesc_len = atoi(value);
172 else if (!strcmp(name, "max-blob-size")) 174 else if (!strcmp(name, "max-blob-size"))
173 ctx.cfg.max_blob_size = atoi(value); 175 ctx.cfg.max_blob_size = atoi(value);
174 else if (!strcmp(name, "max-repo-count")) 176 else if (!strcmp(name, "max-repo-count"))
175 ctx.cfg.max_repo_count = atoi(value); 177 ctx.cfg.max_repo_count = atoi(value);
176 else if (!strcmp(name, "max-commit-count")) 178 else if (!strcmp(name, "max-commit-count"))
177 ctx.cfg.max_commit_count = atoi(value); 179 ctx.cfg.max_commit_count = atoi(value);
178 else if (!strcmp(name, "scan-path")) 180 else if (!strcmp(name, "scan-path"))
179 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 181 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
180 process_cached_repolist(value); 182 process_cached_repolist(value);
181 else 183 else
182 scan_tree(value, repo_config); 184 scan_tree(value, repo_config);
183 else if (!strcmp(name, "source-filter")) 185 else if (!strcmp(name, "source-filter"))
184 ctx.cfg.source_filter = new_filter(value, 1); 186 ctx.cfg.source_filter = new_filter(value, 1);
185 else if (!strcmp(name, "summary-log")) 187 else if (!strcmp(name, "summary-log"))
186 ctx.cfg.summary_log = atoi(value); 188 ctx.cfg.summary_log = atoi(value);
187 else if (!strcmp(name, "summary-branches")) 189 else if (!strcmp(name, "summary-branches"))
188 ctx.cfg.summary_branches = atoi(value); 190 ctx.cfg.summary_branches = atoi(value);
189 else if (!strcmp(name, "summary-tags")) 191 else if (!strcmp(name, "summary-tags"))
190 ctx.cfg.summary_tags = atoi(value); 192 ctx.cfg.summary_tags = atoi(value);
191 else if (!strcmp(name, "side-by-side-diffs")) 193 else if (!strcmp(name, "side-by-side-diffs"))
192 ctx.cfg.ssdiff = atoi(value); 194 ctx.cfg.ssdiff = atoi(value);
193 else if (!strcmp(name, "agefile")) 195 else if (!strcmp(name, "agefile"))
194 ctx.cfg.agefile = xstrdup(value); 196 ctx.cfg.agefile = xstrdup(value);
195 else if (!strcmp(name, "renamelimit")) 197 else if (!strcmp(name, "renamelimit"))
196 ctx.cfg.renamelimit = atoi(value); 198 ctx.cfg.renamelimit = atoi(value);
197 else if (!strcmp(name, "robots")) 199 else if (!strcmp(name, "robots"))
198 ctx.cfg.robots = xstrdup(value); 200 ctx.cfg.robots = xstrdup(value);
199 else if (!strcmp(name, "clone-prefix")) 201 else if (!strcmp(name, "clone-prefix"))
200 ctx.cfg.clone_prefix = xstrdup(value); 202 ctx.cfg.clone_prefix = xstrdup(value);
201 else if (!strcmp(name, "local-time")) 203 else if (!strcmp(name, "local-time"))
202 ctx.cfg.local_time = atoi(value); 204 ctx.cfg.local_time = atoi(value);
203 else if (!prefixcmp(name, "mimetype.")) 205 else if (!prefixcmp(name, "mimetype."))
204 add_mimetype(name + 9, value); 206 add_mimetype(name + 9, value);
205 else if (!strcmp(name, "include")) 207 else if (!strcmp(name, "include"))
206 parse_configfile(value, config_cb); 208 parse_configfile(value, config_cb);
207} 209}
208 210
209static void querystring_cb(const char *name, const char *value) 211static void querystring_cb(const char *name, const char *value)
210{ 212{
211 if (!value) 213 if (!value)
212 value = ""; 214 value = "";
213 215
214 if (!strcmp(name,"r")) { 216 if (!strcmp(name,"r")) {
215 ctx.qry.repo = xstrdup(value); 217 ctx.qry.repo = xstrdup(value);
216 ctx.repo = cgit_get_repoinfo(value); 218 ctx.repo = cgit_get_repoinfo(value);
217 } else if (!strcmp(name, "p")) { 219 } else if (!strcmp(name, "p")) {
218 ctx.qry.page = xstrdup(value); 220 ctx.qry.page = xstrdup(value);
219 } else if (!strcmp(name, "url")) { 221 } else if (!strcmp(name, "url")) {
220 if (*value == '/') 222 if (*value == '/')
221 value++; 223 value++;
222 ctx.qry.url = xstrdup(value); 224 ctx.qry.url = xstrdup(value);
223 cgit_parse_url(value); 225 cgit_parse_url(value);
224 } else if (!strcmp(name, "qt")) { 226 } else if (!strcmp(name, "qt")) {
225 ctx.qry.grep = xstrdup(value); 227 ctx.qry.grep = xstrdup(value);
226 } else if (!strcmp(name, "q")) { 228 } else if (!strcmp(name, "q")) {
227 ctx.qry.search = xstrdup(value); 229 ctx.qry.search = xstrdup(value);
228 } else if (!strcmp(name, "h")) { 230 } else if (!strcmp(name, "h")) {
229 ctx.qry.head = xstrdup(value); 231 ctx.qry.head = xstrdup(value);
230 ctx.qry.has_symref = 1; 232 ctx.qry.has_symref = 1;
231 } else if (!strcmp(name, "id")) { 233 } else if (!strcmp(name, "id")) {
232 ctx.qry.sha1 = xstrdup(value); 234 ctx.qry.sha1 = xstrdup(value);
233 ctx.qry.has_sha1 = 1; 235 ctx.qry.has_sha1 = 1;
234 } else if (!strcmp(name, "id2")) { 236 } else if (!strcmp(name, "id2")) {
235 ctx.qry.sha2 = xstrdup(value); 237 ctx.qry.sha2 = xstrdup(value);
236 ctx.qry.has_sha1 = 1; 238 ctx.qry.has_sha1 = 1;
237 } else if (!strcmp(name, "ofs")) { 239 } else if (!strcmp(name, "ofs")) {
238 ctx.qry.ofs = atoi(value); 240 ctx.qry.ofs = atoi(value);
239 } else if (!strcmp(name, "path")) { 241 } else if (!strcmp(name, "path")) {
240 ctx.qry.path = trim_end(value, '/'); 242 ctx.qry.path = trim_end(value, '/');
241 } else if (!strcmp(name, "name")) { 243 } else if (!strcmp(name, "name")) {
242 ctx.qry.name = xstrdup(value); 244 ctx.qry.name = xstrdup(value);
243 } else if (!strcmp(name, "mimetype")) { 245 } else if (!strcmp(name, "mimetype")) {
244 ctx.qry.mimetype = xstrdup(value); 246 ctx.qry.mimetype = xstrdup(value);
245 } else if (!strcmp(name, "s")){ 247 } else if (!strcmp(name, "s")){
246 ctx.qry.sort = xstrdup(value); 248 ctx.qry.sort = xstrdup(value);
247 } else if (!strcmp(name, "showmsg")) { 249 } else if (!strcmp(name, "showmsg")) {
248 ctx.qry.showmsg = atoi(value); 250 ctx.qry.showmsg = atoi(value);
249 } else if (!strcmp(name, "period")) { 251 } else if (!strcmp(name, "period")) {
250 ctx.qry.period = xstrdup(value); 252 ctx.qry.period = xstrdup(value);
251 } else if (!strcmp(name, "ss")) { 253 } else if (!strcmp(name, "ss")) {
252 ctx.qry.ssdiff = atoi(value); 254 ctx.qry.ssdiff = atoi(value);
253 } else if (!strcmp(name, "all")) { 255 } else if (!strcmp(name, "all")) {
254 ctx.qry.show_all = atoi(value); 256 ctx.qry.show_all = atoi(value);
255 } 257 }
256} 258}
257 259
258char *xstrdupn(const char *str) 260char *xstrdupn(const char *str)
259{ 261{
260 return (str ? xstrdup(str) : NULL); 262 return (str ? xstrdup(str) : NULL);
261} 263}
262 264
263static void prepare_context(struct cgit_context *ctx) 265static void prepare_context(struct cgit_context *ctx)
264{ 266{
265 memset(ctx, 0, sizeof(*ctx)); 267 memset(ctx, 0, sizeof(*ctx));
266 ctx->cfg.agefile = "info/web/last-modified"; 268 ctx->cfg.agefile = "info/web/last-modified";
267 ctx->cfg.nocache = 0; 269 ctx->cfg.nocache = 0;
268 ctx->cfg.cache_size = 0; 270 ctx->cfg.cache_size = 0;
269 ctx->cfg.cache_dynamic_ttl = 5; 271 ctx->cfg.cache_dynamic_ttl = 5;
270 ctx->cfg.cache_max_create_time = 5; 272 ctx->cfg.cache_max_create_time = 5;
271 ctx->cfg.cache_repo_ttl = 5; 273 ctx->cfg.cache_repo_ttl = 5;
272 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 274 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
273 ctx->cfg.cache_root_ttl = 5; 275 ctx->cfg.cache_root_ttl = 5;
274 ctx->cfg.cache_scanrc_ttl = 15; 276 ctx->cfg.cache_scanrc_ttl = 15;
275 ctx->cfg.cache_static_ttl = -1; 277 ctx->cfg.cache_static_ttl = -1;
276 ctx->cfg.css = "/cgit.css"; 278 ctx->cfg.css = "/cgit.css";
277 ctx->cfg.logo = "/cgit.png"; 279 ctx->cfg.logo = "/cgit.png";
278 ctx->cfg.local_time = 0; 280 ctx->cfg.local_time = 0;
279 ctx->cfg.enable_tree_linenumbers = 1; 281 ctx->cfg.enable_tree_linenumbers = 1;
280 ctx->cfg.max_repo_count = 50; 282 ctx->cfg.max_repo_count = 50;
281 ctx->cfg.max_commit_count = 50; 283 ctx->cfg.max_commit_count = 50;
282 ctx->cfg.max_lock_attempts = 5; 284 ctx->cfg.max_lock_attempts = 5;
283 ctx->cfg.max_msg_len = 80; 285 ctx->cfg.max_msg_len = 80;
284 ctx->cfg.max_repodesc_len = 80; 286 ctx->cfg.max_repodesc_len = 80;
285 ctx->cfg.max_blob_size = 0; 287 ctx->cfg.max_blob_size = 0;
286 ctx->cfg.max_stats = 0; 288 ctx->cfg.max_stats = 0;
287 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 289 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
288 ctx->cfg.renamelimit = -1; 290 ctx->cfg.renamelimit = -1;
289 ctx->cfg.robots = "index, nofollow"; 291 ctx->cfg.robots = "index, nofollow";
290 ctx->cfg.root_title = "Git repository browser"; 292 ctx->cfg.root_title = "Git repository browser";
291 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 293 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
292 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 294 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
293 ctx->cfg.section = ""; 295 ctx->cfg.section = "";
294 ctx->cfg.summary_branches = 10; 296 ctx->cfg.summary_branches = 10;
295 ctx->cfg.summary_log = 10; 297 ctx->cfg.summary_log = 10;
296 ctx->cfg.summary_tags = 10; 298 ctx->cfg.summary_tags = 10;
299 ctx->cfg.max_atom_items = 10;
297 ctx->cfg.ssdiff = 0; 300 ctx->cfg.ssdiff = 0;
298 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 301 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
299 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 302 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
300 ctx->env.https = xstrdupn(getenv("HTTPS")); 303 ctx->env.https = xstrdupn(getenv("HTTPS"));
301 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 304 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
302 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 305 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
303 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 306 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
304 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 307 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
305 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 308 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
306 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 309 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
307 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 310 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
308 ctx->page.mimetype = "text/html"; 311 ctx->page.mimetype = "text/html";
309 ctx->page.charset = PAGE_ENCODING; 312 ctx->page.charset = PAGE_ENCODING;
310 ctx->page.filename = NULL; 313 ctx->page.filename = NULL;
311 ctx->page.size = 0; 314 ctx->page.size = 0;
312 ctx->page.modified = time(NULL); 315 ctx->page.modified = time(NULL);
313 ctx->page.expires = ctx->page.modified; 316 ctx->page.expires = ctx->page.modified;
314 ctx->page.etag = NULL; 317 ctx->page.etag = NULL;
315 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 318 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
316 if (ctx->env.script_name) 319 if (ctx->env.script_name)
317 ctx->cfg.script_name = ctx->env.script_name; 320 ctx->cfg.script_name = ctx->env.script_name;
318 if (ctx->env.query_string) 321 if (ctx->env.query_string)
319 ctx->qry.raw = ctx->env.query_string; 322 ctx->qry.raw = ctx->env.query_string;
320 if (!ctx->env.cgit_config) 323 if (!ctx->env.cgit_config)
321 ctx->env.cgit_config = CGIT_CONFIG; 324 ctx->env.cgit_config = CGIT_CONFIG;
322} 325}
323 326
324struct refmatch { 327struct refmatch {
325 char *req_ref; 328 char *req_ref;
326 char *first_ref; 329 char *first_ref;
327 int match; 330 int match;
328}; 331};
329 332
330int find_current_ref(const char *refname, const unsigned char *sha1, 333int find_current_ref(const char *refname, const unsigned char *sha1,
331 int flags, void *cb_data) 334 int flags, void *cb_data)
332{ 335{
333 struct refmatch *info; 336 struct refmatch *info;
334 337
335 info = (struct refmatch *)cb_data; 338 info = (struct refmatch *)cb_data;
336 if (!strcmp(refname, info->req_ref)) 339 if (!strcmp(refname, info->req_ref))
337 info->match = 1; 340 info->match = 1;
338 if (!info->first_ref) 341 if (!info->first_ref)
339 info->first_ref = xstrdup(refname); 342 info->first_ref = xstrdup(refname);
340 return info->match; 343 return info->match;
341} 344}
342 345
343char *find_default_branch(struct cgit_repo *repo) 346char *find_default_branch(struct cgit_repo *repo)
344{ 347{
345 struct refmatch info; 348 struct refmatch info;
346 char *ref; 349 char *ref;
347 350
348 info.req_ref = repo->defbranch; 351 info.req_ref = repo->defbranch;
349 info.first_ref = NULL; 352 info.first_ref = NULL;
350 info.match = 0; 353 info.match = 0;
351 for_each_branch_ref(find_current_ref, &info); 354 for_each_branch_ref(find_current_ref, &info);
352 if (info.match) 355 if (info.match)
353 ref = info.req_ref; 356 ref = info.req_ref;
354 else 357 else
355 ref = info.first_ref; 358 ref = info.first_ref;
356 if (ref) 359 if (ref)
357 ref = xstrdup(ref); 360 ref = xstrdup(ref);
358 return ref; 361 return ref;
359} 362}
360 363
361static int prepare_repo_cmd(struct cgit_context *ctx) 364static int prepare_repo_cmd(struct cgit_context *ctx)
362{ 365{
363 char *tmp; 366 char *tmp;
364 unsigned char sha1[20]; 367 unsigned char sha1[20];
365 int nongit = 0; 368 int nongit = 0;
366 369
367 setenv("GIT_DIR", ctx->repo->path, 1); 370 setenv("GIT_DIR", ctx->repo->path, 1);
368 setup_git_directory_gently(&nongit); 371 setup_git_directory_gently(&nongit);
369 if (nongit) { 372 if (nongit) {
370 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 373 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
371 "config error"); 374 "config error");
372 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 375 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
373 ctx->repo = NULL; 376 ctx->repo = NULL;
374 cgit_print_http_headers(ctx); 377 cgit_print_http_headers(ctx);
375 cgit_print_docstart(ctx); 378 cgit_print_docstart(ctx);
376 cgit_print_pageheader(ctx); 379 cgit_print_pageheader(ctx);
377 cgit_print_error(tmp); 380 cgit_print_error(tmp);
378 cgit_print_docend(); 381 cgit_print_docend();
379 return 1; 382 return 1;
380 } 383 }
381 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 384 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
382 385
383 if (!ctx->qry.head) { 386 if (!ctx->qry.head) {
384 ctx->qry.nohead = 1; 387 ctx->qry.nohead = 1;
385 ctx->qry.head = find_default_branch(ctx->repo); 388 ctx->qry.head = find_default_branch(ctx->repo);
386 ctx->repo->defbranch = ctx->qry.head; 389 ctx->repo->defbranch = ctx->qry.head;
387 } 390 }
388 391
389 if (!ctx->qry.head) { 392 if (!ctx->qry.head) {
390 cgit_print_http_headers(ctx); 393 cgit_print_http_headers(ctx);
391 cgit_print_docstart(ctx); 394 cgit_print_docstart(ctx);
392 cgit_print_pageheader(ctx); 395 cgit_print_pageheader(ctx);
393 cgit_print_error("Repository seems to be empty"); 396 cgit_print_error("Repository seems to be empty");
394 cgit_print_docend(); 397 cgit_print_docend();
395 return 1; 398 return 1;
396 } 399 }
397 400
398 if (get_sha1(ctx->qry.head, sha1)) { 401 if (get_sha1(ctx->qry.head, sha1)) {
399 tmp = xstrdup(ctx->qry.head); 402 tmp = xstrdup(ctx->qry.head);
400 ctx->qry.head = ctx->repo->defbranch; 403 ctx->qry.head = ctx->repo->defbranch;
401 ctx->page.status = 404; 404 ctx->page.status = 404;
402 ctx->page.statusmsg = "not found"; 405 ctx->page.statusmsg = "not found";
403 cgit_print_http_headers(ctx); 406 cgit_print_http_headers(ctx);
404 cgit_print_docstart(ctx); 407 cgit_print_docstart(ctx);
405 cgit_print_pageheader(ctx); 408 cgit_print_pageheader(ctx);
406 cgit_print_error(fmt("Invalid branch: %s", tmp)); 409 cgit_print_error(fmt("Invalid branch: %s", tmp));
407 cgit_print_docend(); 410 cgit_print_docend();
408 return 1; 411 return 1;
409 } 412 }
410 return 0; 413 return 0;
411} 414}
412 415
413static void process_request(void *cbdata) 416static void process_request(void *cbdata)
414{ 417{
415 struct cgit_context *ctx = cbdata; 418 struct cgit_context *ctx = cbdata;
416 struct cgit_cmd *cmd; 419 struct cgit_cmd *cmd;
417 420
418 cmd = cgit_get_cmd(ctx); 421 cmd = cgit_get_cmd(ctx);
419 if (!cmd) { 422 if (!cmd) {
420 ctx->page.title = "cgit error"; 423 ctx->page.title = "cgit error";
421 cgit_print_http_headers(ctx); 424 cgit_print_http_headers(ctx);
422 cgit_print_docstart(ctx); 425 cgit_print_docstart(ctx);
423 cgit_print_pageheader(ctx); 426 cgit_print_pageheader(ctx);
424 cgit_print_error("Invalid request"); 427 cgit_print_error("Invalid request");
425 cgit_print_docend(); 428 cgit_print_docend();
426 return; 429 return;
427 } 430 }
428 431
429 if (cmd->want_repo && !ctx->repo) { 432 if (cmd->want_repo && !ctx->repo) {
430 cgit_print_http_headers(ctx); 433 cgit_print_http_headers(ctx);
431 cgit_print_docstart(ctx); 434 cgit_print_docstart(ctx);
432 cgit_print_pageheader(ctx); 435 cgit_print_pageheader(ctx);
433 cgit_print_error(fmt("No repository selected")); 436 cgit_print_error(fmt("No repository selected"));
434 cgit_print_docend(); 437 cgit_print_docend();
435 return; 438 return;
436 } 439 }
437 440
438 if (ctx->repo && prepare_repo_cmd(ctx)) 441 if (ctx->repo && prepare_repo_cmd(ctx))
439 return; 442 return;
440 443
441 if (cmd->want_layout) { 444 if (cmd->want_layout) {
442 cgit_print_http_headers(ctx); 445 cgit_print_http_headers(ctx);
443 cgit_print_docstart(ctx); 446 cgit_print_docstart(ctx);
444 cgit_print_pageheader(ctx); 447 cgit_print_pageheader(ctx);
445 } 448 }
446 449
447 cmd->fn(ctx); 450 cmd->fn(ctx);
448 451
449 if (cmd->want_layout) 452 if (cmd->want_layout)
450 cgit_print_docend(); 453 cgit_print_docend();
451} 454}
452 455
453int cmp_repos(const void *a, const void *b) 456int cmp_repos(const void *a, const void *b)
454{ 457{
455 const struct cgit_repo *ra = a, *rb = b; 458 const struct cgit_repo *ra = a, *rb = b;
456 return strcmp(ra->url, rb->url); 459 return strcmp(ra->url, rb->url);
457} 460}
458 461
459char *build_snapshot_setting(int bitmap) 462char *build_snapshot_setting(int bitmap)
460{ 463{
461 const struct cgit_snapshot_format *f; 464 const struct cgit_snapshot_format *f;
462 char *result = xstrdup(""); 465 char *result = xstrdup("");
463 char *tmp; 466 char *tmp;
464 int len; 467 int len;
465 468
466 for (f = cgit_snapshot_formats; f->suffix; f++) { 469 for (f = cgit_snapshot_formats; f->suffix; f++) {
467 if (f->bit & bitmap) { 470 if (f->bit & bitmap) {
468 tmp = result; 471 tmp = result;
469 result = xstrdup(fmt("%s%s ", tmp, f->suffix)); 472 result = xstrdup(fmt("%s%s ", tmp, f->suffix));
470 free(tmp); 473 free(tmp);
471 } 474 }
472 } 475 }
473 len = strlen(result); 476 len = strlen(result);
474 if (len) 477 if (len)
475 result[len - 1] = '\0'; 478 result[len - 1] = '\0';
476 return result; 479 return result;
477} 480}
478 481
479char *get_first_line(char *txt) 482char *get_first_line(char *txt)
480{ 483{
481 char *t = xstrdup(txt); 484 char *t = xstrdup(txt);
482 char *p = strchr(t, '\n'); 485 char *p = strchr(t, '\n');
483 if (p) 486 if (p)
484 *p = '\0'; 487 *p = '\0';
485 return t; 488 return t;
486} 489}
487 490
488void print_repo(FILE *f, struct cgit_repo *repo) 491void print_repo(FILE *f, struct cgit_repo *repo)
489{ 492{
490 fprintf(f, "repo.url=%s\n", repo->url); 493 fprintf(f, "repo.url=%s\n", repo->url);
491 fprintf(f, "repo.name=%s\n", repo->name); 494 fprintf(f, "repo.name=%s\n", repo->name);
492 fprintf(f, "repo.path=%s\n", repo->path); 495 fprintf(f, "repo.path=%s\n", repo->path);
493 if (repo->owner) 496 if (repo->owner)
494 fprintf(f, "repo.owner=%s\n", repo->owner); 497 fprintf(f, "repo.owner=%s\n", repo->owner);
495 if (repo->desc) { 498 if (repo->desc) {
496 char *tmp = get_first_line(repo->desc); 499 char *tmp = get_first_line(repo->desc);
497 fprintf(f, "repo.desc=%s\n", tmp); 500 fprintf(f, "repo.desc=%s\n", tmp);
498 free(tmp); 501 free(tmp);
499 } 502 }
500 if (repo->readme) 503 if (repo->readme)
501 fprintf(f, "repo.readme=%s\n", repo->readme); 504 fprintf(f, "repo.readme=%s\n", repo->readme);
502 if (repo->defbranch) 505 if (repo->defbranch)
503 fprintf(f, "repo.defbranch=%s\n", repo->defbranch); 506 fprintf(f, "repo.defbranch=%s\n", repo->defbranch);
504 if (repo->module_link) 507 if (repo->module_link)
505 fprintf(f, "repo.module-link=%s\n", repo->module_link); 508 fprintf(f, "repo.module-link=%s\n", repo->module_link);
506 if (repo->section) 509 if (repo->section)
507 fprintf(f, "repo.section=%s\n", repo->section); 510 fprintf(f, "repo.section=%s\n", repo->section);
508 if (repo->clone_url) 511 if (repo->clone_url)
509 fprintf(f, "repo.clone-url=%s\n", repo->clone_url); 512 fprintf(f, "repo.clone-url=%s\n", repo->clone_url);
510 fprintf(f, "repo.enable-log-filecount=%d\n", 513 fprintf(f, "repo.enable-log-filecount=%d\n",
511 repo->enable_log_filecount); 514 repo->enable_log_filecount);
512 fprintf(f, "repo.enable-log-linecount=%d\n", 515 fprintf(f, "repo.enable-log-linecount=%d\n",
513 repo->enable_log_linecount); 516 repo->enable_log_linecount);
514 if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter) 517 if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter)
515 fprintf(f, "repo.about-filter=%s\n", repo->about_filter->cmd); 518 fprintf(f, "repo.about-filter=%s\n", repo->about_filter->cmd);
516 if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter) 519 if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter)
517 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd); 520 fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd);
518 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter) 521 if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
519 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd); 522 fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd);
520 if (repo->snapshots != ctx.cfg.snapshots) { 523 if (repo->snapshots != ctx.cfg.snapshots) {
521 char *tmp = build_snapshot_setting(repo->snapshots); 524 char *tmp = build_snapshot_setting(repo->snapshots);
522 fprintf(f, "repo.snapshots=%s\n", tmp); 525 fprintf(f, "repo.snapshots=%s\n", tmp);
523 free(tmp); 526 free(tmp);
524 } 527 }
525 if (repo->max_stats != ctx.cfg.max_stats) 528 if (repo->max_stats != ctx.cfg.max_stats)
526 fprintf(f, "repo.max-stats=%s\n", 529 fprintf(f, "repo.max-stats=%s\n",
527 cgit_find_stats_periodname(repo->max_stats)); 530 cgit_find_stats_periodname(repo->max_stats));
528 fprintf(f, "\n"); 531 fprintf(f, "\n");
529} 532}
530 533
531void print_repolist(FILE *f, struct cgit_repolist *list, int start) 534void print_repolist(FILE *f, struct cgit_repolist *list, int start)
532{ 535{
533 int i; 536 int i;
534 537
535 for(i = start; i < list->count; i++) 538 for(i = start; i < list->count; i++)
536 print_repo(f, &list->repos[i]); 539 print_repo(f, &list->repos[i]);
537} 540}
538 541
539/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 542/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
540 * and return 0 on success. 543 * and return 0 on success.
541 */ 544 */
542static int generate_cached_repolist(const char *path, const char *cached_rc) 545static int generate_cached_repolist(const char *path, const char *cached_rc)
543{ 546{
544 char *locked_rc; 547 char *locked_rc;
545 int idx; 548 int idx;
546 FILE *f; 549 FILE *f;
547 550
548 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 551 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
549 f = fopen(locked_rc, "wx"); 552 f = fopen(locked_rc, "wx");
550 if (!f) { 553 if (!f) {
551 /* Inform about the error unless the lockfile already existed, 554 /* Inform about the error unless the lockfile already existed,
552 * since that only means we've got concurrent requests. 555 * since that only means we've got concurrent requests.
553 */ 556 */
554 if (errno != EEXIST) 557 if (errno != EEXIST)
555 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 558 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
556 locked_rc, strerror(errno), errno); 559 locked_rc, strerror(errno), errno);
557 return errno; 560 return errno;
558 } 561 }
559 idx = cgit_repolist.count; 562 idx = cgit_repolist.count;
560 scan_tree(path, repo_config); 563 scan_tree(path, repo_config);
561 print_repolist(f, &cgit_repolist, idx); 564 print_repolist(f, &cgit_repolist, idx);
562 if (rename(locked_rc, cached_rc)) 565 if (rename(locked_rc, cached_rc))
563 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 566 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
564 locked_rc, cached_rc, strerror(errno), errno); 567 locked_rc, cached_rc, strerror(errno), errno);
565 fclose(f); 568 fclose(f);
566 return 0; 569 return 0;
567} 570}
568 571
569static void process_cached_repolist(const char *path) 572static void process_cached_repolist(const char *path)
570{ 573{
571 struct stat st; 574 struct stat st;
572 char *cached_rc; 575 char *cached_rc;
573 time_t age; 576 time_t age;
574 577
575 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 578 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root,
576 hash_str(path))); 579 hash_str(path)));
577 580
578 if (stat(cached_rc, &st)) { 581 if (stat(cached_rc, &st)) {
579 /* Nothing is cached, we need to scan without forking. And 582 /* Nothing is cached, we need to scan without forking. And
580 * if we fail to generate a cached repolist, we need to 583 * if we fail to generate a cached repolist, we need to
581 * invoke scan_tree manually. 584 * invoke scan_tree manually.
582 */ 585 */
583 if (generate_cached_repolist(path, cached_rc)) 586 if (generate_cached_repolist(path, cached_rc))
584 scan_tree(path, repo_config); 587 scan_tree(path, repo_config);
585 return; 588 return;
586 } 589 }
587 590
588 parse_configfile(cached_rc, config_cb); 591 parse_configfile(cached_rc, config_cb);
589 592
590 /* If the cached configfile hasn't expired, lets exit now */ 593 /* If the cached configfile hasn't expired, lets exit now */
591 age = time(NULL) - st.st_mtime; 594 age = time(NULL) - st.st_mtime;
592 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 595 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
593 return; 596 return;
594 597
595 /* The cached repolist has been parsed, but it was old. So lets 598 /* The cached repolist has been parsed, but it was old. So lets
596 * rescan the specified path and generate a new cached repolist 599 * rescan the specified path and generate a new cached repolist
597 * in a child-process to avoid latency for the current request. 600 * in a child-process to avoid latency for the current request.
598 */ 601 */
599 if (fork()) 602 if (fork())
600 return; 603 return;
601 604
602 exit(generate_cached_repolist(path, cached_rc)); 605 exit(generate_cached_repolist(path, cached_rc));
603} 606}
604 607
605static void cgit_parse_args(int argc, const char **argv) 608static void cgit_parse_args(int argc, const char **argv)
606{ 609{
607 int i; 610 int i;
608 int scan = 0; 611 int scan = 0;
609 612
610 for (i = 1; i < argc; i++) { 613 for (i = 1; i < argc; i++) {
611 if (!strncmp(argv[i], "--cache=", 8)) { 614 if (!strncmp(argv[i], "--cache=", 8)) {
612 ctx.cfg.cache_root = xstrdup(argv[i]+8); 615 ctx.cfg.cache_root = xstrdup(argv[i]+8);
613 } 616 }
614 if (!strcmp(argv[i], "--nocache")) { 617 if (!strcmp(argv[i], "--nocache")) {
615 ctx.cfg.nocache = 1; 618 ctx.cfg.nocache = 1;
616 } 619 }
617 if (!strcmp(argv[i], "--nohttp")) { 620 if (!strcmp(argv[i], "--nohttp")) {
618 ctx.env.no_http = "1"; 621 ctx.env.no_http = "1";
619 } 622 }
620 if (!strncmp(argv[i], "--query=", 8)) { 623 if (!strncmp(argv[i], "--query=", 8)) {
621 ctx.qry.raw = xstrdup(argv[i]+8); 624 ctx.qry.raw = xstrdup(argv[i]+8);
622 } 625 }
623 if (!strncmp(argv[i], "--repo=", 7)) { 626 if (!strncmp(argv[i], "--repo=", 7)) {
624 ctx.qry.repo = xstrdup(argv[i]+7); 627 ctx.qry.repo = xstrdup(argv[i]+7);
625 } 628 }
626 if (!strncmp(argv[i], "--page=", 7)) { 629 if (!strncmp(argv[i], "--page=", 7)) {
627 ctx.qry.page = xstrdup(argv[i]+7); 630 ctx.qry.page = xstrdup(argv[i]+7);
628 } 631 }
629 if (!strncmp(argv[i], "--head=", 7)) { 632 if (!strncmp(argv[i], "--head=", 7)) {
630 ctx.qry.head = xstrdup(argv[i]+7); 633 ctx.qry.head = xstrdup(argv[i]+7);
631 ctx.qry.has_symref = 1; 634 ctx.qry.has_symref = 1;
632 } 635 }
633 if (!strncmp(argv[i], "--sha1=", 7)) { 636 if (!strncmp(argv[i], "--sha1=", 7)) {
634 ctx.qry.sha1 = xstrdup(argv[i]+7); 637 ctx.qry.sha1 = xstrdup(argv[i]+7);
635 ctx.qry.has_sha1 = 1; 638 ctx.qry.has_sha1 = 1;
636 } 639 }
637 if (!strncmp(argv[i], "--ofs=", 6)) { 640 if (!strncmp(argv[i], "--ofs=", 6)) {
638 ctx.qry.ofs = atoi(argv[i]+6); 641 ctx.qry.ofs = atoi(argv[i]+6);
639 } 642 }
640 if (!strncmp(argv[i], "--scan-tree=", 12) || 643 if (!strncmp(argv[i], "--scan-tree=", 12) ||
641 !strncmp(argv[i], "--scan-path=", 12)) { 644 !strncmp(argv[i], "--scan-path=", 12)) {
642 /* HACK: the global snapshot bitmask defines the 645 /* HACK: the global snapshot bitmask defines the
643 * set of allowed snapshot formats, but the config 646 * set of allowed snapshot formats, but the config
644 * file hasn't been parsed yet so the mask is 647 * file hasn't been parsed yet so the mask is
645 * currently 0. By setting all bits high before 648 * currently 0. By setting all bits high before
646 * scanning we make sure that any in-repo cgitrc 649 * scanning we make sure that any in-repo cgitrc
647 * snapshot setting is respected by scan_tree(). 650 * snapshot setting is respected by scan_tree().
648 * BTW: we assume that there'll never be more than 651 * BTW: we assume that there'll never be more than
649 * 255 different snapshot formats supported by cgit... 652 * 255 different snapshot formats supported by cgit...
650 */ 653 */
651 ctx.cfg.snapshots = 0xFF; 654 ctx.cfg.snapshots = 0xFF;
652 scan++; 655 scan++;
653 scan_tree(argv[i] + 12, repo_config); 656 scan_tree(argv[i] + 12, repo_config);
654 } 657 }
655 } 658 }
656 if (scan) { 659 if (scan) {
657 qsort(cgit_repolist.repos, cgit_repolist.count, 660 qsort(cgit_repolist.repos, cgit_repolist.count,
658 sizeof(struct cgit_repo), cmp_repos); 661 sizeof(struct cgit_repo), cmp_repos);
659 print_repolist(stdout, &cgit_repolist, 0); 662 print_repolist(stdout, &cgit_repolist, 0);
660 exit(0); 663 exit(0);
661 } 664 }
662} 665}
663 666
664static int calc_ttl() 667static int calc_ttl()
665{ 668{
666 if (!ctx.repo) 669 if (!ctx.repo)
667 return ctx.cfg.cache_root_ttl; 670 return ctx.cfg.cache_root_ttl;
668 671
669 if (!ctx.qry.page) 672 if (!ctx.qry.page)
670 return ctx.cfg.cache_repo_ttl; 673 return ctx.cfg.cache_repo_ttl;
671 674
672 if (ctx.qry.has_symref) 675 if (ctx.qry.has_symref)
673 return ctx.cfg.cache_dynamic_ttl; 676 return ctx.cfg.cache_dynamic_ttl;
674 677
675 if (ctx.qry.has_sha1) 678 if (ctx.qry.has_sha1)
676 return ctx.cfg.cache_static_ttl; 679 return ctx.cfg.cache_static_ttl;
677 680
678 return ctx.cfg.cache_repo_ttl; 681 return ctx.cfg.cache_repo_ttl;
679} 682}
680 683
diff --git a/cgit.h b/cgit.h
index 478aebb..7fafb58 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,300 +1,301 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <string-list.h> 18#include <string-list.h>
19#include <xdiff-interface.h> 19#include <xdiff-interface.h>
20#include <xdiff/xdiff.h> 20#include <xdiff/xdiff.h>
21#include <utf8.h> 21#include <utf8.h>
22 22
23 23
24/* 24/*
25 * Dateformats used on misc. pages 25 * Dateformats used on misc. pages
26 */ 26 */
27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
28#define FMT_SHORTDATE "%Y-%m-%d" 28#define FMT_SHORTDATE "%Y-%m-%d"
29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
30 30
31 31
32/* 32/*
33 * Limits used for relative dates 33 * Limits used for relative dates
34 */ 34 */
35#define TM_MIN 60 35#define TM_MIN 60
36#define TM_HOUR (TM_MIN * 60) 36#define TM_HOUR (TM_MIN * 60)
37#define TM_DAY (TM_HOUR * 24) 37#define TM_DAY (TM_HOUR * 24)
38#define TM_WEEK (TM_DAY * 7) 38#define TM_WEEK (TM_DAY * 7)
39#define TM_YEAR (TM_DAY * 365) 39#define TM_YEAR (TM_DAY * 365)
40#define TM_MONTH (TM_YEAR / 12.0) 40#define TM_MONTH (TM_YEAR / 12.0)
41 41
42 42
43/* 43/*
44 * Default encoding 44 * Default encoding
45 */ 45 */
46#define PAGE_ENCODING "UTF-8" 46#define PAGE_ENCODING "UTF-8"
47 47
48typedef void (*configfn)(const char *name, const char *value); 48typedef void (*configfn)(const char *name, const char *value);
49typedef void (*filepair_fn)(struct diff_filepair *pair); 49typedef void (*filepair_fn)(struct diff_filepair *pair);
50typedef void (*linediff_fn)(char *line, int len); 50typedef void (*linediff_fn)(char *line, int len);
51 51
52struct cgit_filter { 52struct cgit_filter {
53 char *cmd; 53 char *cmd;
54 char **argv; 54 char **argv;
55 int old_stdout; 55 int old_stdout;
56 int pipe_fh[2]; 56 int pipe_fh[2];
57 int pid; 57 int pid;
58 int exitstatus; 58 int exitstatus;
59}; 59};
60 60
61struct cgit_repo { 61struct cgit_repo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *module_link; 68 char *module_link;
69 char *readme; 69 char *readme;
70 char *section; 70 char *section;
71 char *clone_url; 71 char *clone_url;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75 int enable_remote_branches; 75 int enable_remote_branches;
76 int max_stats; 76 int max_stats;
77 time_t mtime; 77 time_t mtime;
78 struct cgit_filter *about_filter; 78 struct cgit_filter *about_filter;
79 struct cgit_filter *commit_filter; 79 struct cgit_filter *commit_filter;
80 struct cgit_filter *source_filter; 80 struct cgit_filter *source_filter;
81}; 81};
82 82
83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
84 const char *value); 84 const char *value);
85 85
86struct cgit_repolist { 86struct cgit_repolist {
87 int length; 87 int length;
88 int count; 88 int count;
89 struct cgit_repo *repos; 89 struct cgit_repo *repos;
90}; 90};
91 91
92struct commitinfo { 92struct commitinfo {
93 struct commit *commit; 93 struct commit *commit;
94 char *author; 94 char *author;
95 char *author_email; 95 char *author_email;
96 unsigned long author_date; 96 unsigned long author_date;
97 char *committer; 97 char *committer;
98 char *committer_email; 98 char *committer_email;
99 unsigned long committer_date; 99 unsigned long committer_date;
100 char *subject; 100 char *subject;
101 char *msg; 101 char *msg;
102 char *msg_encoding; 102 char *msg_encoding;
103}; 103};
104 104
105struct taginfo { 105struct taginfo {
106 char *tagger; 106 char *tagger;
107 char *tagger_email; 107 char *tagger_email;
108 unsigned long tagger_date; 108 unsigned long tagger_date;
109 char *msg; 109 char *msg;
110}; 110};
111 111
112struct refinfo { 112struct refinfo {
113 const char *refname; 113 const char *refname;
114 struct object *object; 114 struct object *object;
115 union { 115 union {
116 struct taginfo *tag; 116 struct taginfo *tag;
117 struct commitinfo *commit; 117 struct commitinfo *commit;
118 }; 118 };
119}; 119};
120 120
121struct reflist { 121struct reflist {
122 struct refinfo **refs; 122 struct refinfo **refs;
123 int alloc; 123 int alloc;
124 int count; 124 int count;
125}; 125};
126 126
127struct cgit_query { 127struct cgit_query {
128 int has_symref; 128 int has_symref;
129 int has_sha1; 129 int has_sha1;
130 char *raw; 130 char *raw;
131 char *repo; 131 char *repo;
132 char *page; 132 char *page;
133 char *search; 133 char *search;
134 char *grep; 134 char *grep;
135 char *head; 135 char *head;
136 char *sha1; 136 char *sha1;
137 char *sha2; 137 char *sha2;
138 char *path; 138 char *path;
139 char *name; 139 char *name;
140 char *mimetype; 140 char *mimetype;
141 char *url; 141 char *url;
142 char *period; 142 char *period;
143 int ofs; 143 int ofs;
144 int nohead; 144 int nohead;
145 char *sort; 145 char *sort;
146 int showmsg; 146 int showmsg;
147 int ssdiff; 147 int ssdiff;
148 int show_all; 148 int show_all;
149}; 149};
150 150
151struct cgit_config { 151struct cgit_config {
152 char *agefile; 152 char *agefile;
153 char *cache_root; 153 char *cache_root;
154 char *clone_prefix; 154 char *clone_prefix;
155 char *css; 155 char *css;
156 char *favicon; 156 char *favicon;
157 char *footer; 157 char *footer;
158 char *head_include; 158 char *head_include;
159 char *header; 159 char *header;
160 char *index_header; 160 char *index_header;
161 char *index_info; 161 char *index_info;
162 char *logo; 162 char *logo;
163 char *logo_link; 163 char *logo_link;
164 char *module_link; 164 char *module_link;
165 char *robots; 165 char *robots;
166 char *root_title; 166 char *root_title;
167 char *root_desc; 167 char *root_desc;
168 char *root_readme; 168 char *root_readme;
169 char *script_name; 169 char *script_name;
170 char *section; 170 char *section;
171 char *virtual_root; 171 char *virtual_root;
172 int cache_size; 172 int cache_size;
173 int cache_dynamic_ttl; 173 int cache_dynamic_ttl;
174 int cache_max_create_time; 174 int cache_max_create_time;
175 int cache_repo_ttl; 175 int cache_repo_ttl;
176 int cache_root_ttl; 176 int cache_root_ttl;
177 int cache_scanrc_ttl; 177 int cache_scanrc_ttl;
178 int cache_static_ttl; 178 int cache_static_ttl;
179 int embedded; 179 int embedded;
180 int enable_filter_overrides; 180 int enable_filter_overrides;
181 int enable_index_links; 181 int enable_index_links;
182 int enable_log_filecount; 182 int enable_log_filecount;
183 int enable_log_linecount; 183 int enable_log_linecount;
184 int enable_remote_branches; 184 int enable_remote_branches;
185 int enable_tree_linenumbers; 185 int enable_tree_linenumbers;
186 int local_time; 186 int local_time;
187 int max_atom_items;
187 int max_repo_count; 188 int max_repo_count;
188 int max_commit_count; 189 int max_commit_count;
189 int max_lock_attempts; 190 int max_lock_attempts;
190 int max_msg_len; 191 int max_msg_len;
191 int max_repodesc_len; 192 int max_repodesc_len;
192 int max_blob_size; 193 int max_blob_size;
193 int max_stats; 194 int max_stats;
194 int nocache; 195 int nocache;
195 int noplainemail; 196 int noplainemail;
196 int noheader; 197 int noheader;
197 int renamelimit; 198 int renamelimit;
198 int snapshots; 199 int snapshots;
199 int summary_branches; 200 int summary_branches;
200 int summary_log; 201 int summary_log;
201 int summary_tags; 202 int summary_tags;
202 int ssdiff; 203 int ssdiff;
203 struct string_list mimetypes; 204 struct string_list mimetypes;
204 struct cgit_filter *about_filter; 205 struct cgit_filter *about_filter;
205 struct cgit_filter *commit_filter; 206 struct cgit_filter *commit_filter;
206 struct cgit_filter *source_filter; 207 struct cgit_filter *source_filter;
207}; 208};
208 209
209struct cgit_page { 210struct cgit_page {
210 time_t modified; 211 time_t modified;
211 time_t expires; 212 time_t expires;
212 size_t size; 213 size_t size;
213 char *mimetype; 214 char *mimetype;
214 char *charset; 215 char *charset;
215 char *filename; 216 char *filename;
216 char *etag; 217 char *etag;
217 char *title; 218 char *title;
218 int status; 219 int status;
219 char *statusmsg; 220 char *statusmsg;
220}; 221};
221 222
222struct cgit_environment { 223struct cgit_environment {
223 char *cgit_config; 224 char *cgit_config;
224 char *http_host; 225 char *http_host;
225 char *https; 226 char *https;
226 char *no_http; 227 char *no_http;
227 char *path_info; 228 char *path_info;
228 char *query_string; 229 char *query_string;
229 char *request_method; 230 char *request_method;
230 char *script_name; 231 char *script_name;
231 char *server_name; 232 char *server_name;
232 char *server_port; 233 char *server_port;
233}; 234};
234 235
235struct cgit_context { 236struct cgit_context {
236 struct cgit_environment env; 237 struct cgit_environment env;
237 struct cgit_query qry; 238 struct cgit_query qry;
238 struct cgit_config cfg; 239 struct cgit_config cfg;
239 struct cgit_repo *repo; 240 struct cgit_repo *repo;
240 struct cgit_page page; 241 struct cgit_page page;
241}; 242};
242 243
243struct cgit_snapshot_format { 244struct cgit_snapshot_format {
244 const char *suffix; 245 const char *suffix;
245 const char *mimetype; 246 const char *mimetype;
246 write_archive_fn_t write_func; 247 write_archive_fn_t write_func;
247 int bit; 248 int bit;
248}; 249};
249 250
250extern const char *cgit_version; 251extern const char *cgit_version;
251 252
252extern struct cgit_repolist cgit_repolist; 253extern struct cgit_repolist cgit_repolist;
253extern struct cgit_context ctx; 254extern struct cgit_context ctx;
254extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 255extern const struct cgit_snapshot_format cgit_snapshot_formats[];
255 256
256extern struct cgit_repo *cgit_add_repo(const char *url); 257extern struct cgit_repo *cgit_add_repo(const char *url);
257extern struct cgit_repo *cgit_get_repoinfo(const char *url); 258extern struct cgit_repo *cgit_get_repoinfo(const char *url);
258extern void cgit_repo_config_cb(const char *name, const char *value); 259extern void cgit_repo_config_cb(const char *name, const char *value);
259 260
260extern int chk_zero(int result, char *msg); 261extern int chk_zero(int result, char *msg);
261extern int chk_positive(int result, char *msg); 262extern int chk_positive(int result, char *msg);
262extern int chk_non_negative(int result, char *msg); 263extern int chk_non_negative(int result, char *msg);
263 264
264extern char *trim_end(const char *str, char c); 265extern char *trim_end(const char *str, char c);
265extern char *strlpart(char *txt, int maxlen); 266extern char *strlpart(char *txt, int maxlen);
266extern char *strrpart(char *txt, int maxlen); 267extern char *strrpart(char *txt, int maxlen);
267 268
268extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 269extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
269extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 270extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
270 int flags, void *cb_data); 271 int flags, void *cb_data);
271 272
272extern void *cgit_free_commitinfo(struct commitinfo *info); 273extern void *cgit_free_commitinfo(struct commitinfo *info);
273 274
274extern int cgit_diff_files(const unsigned char *old_sha1, 275extern int cgit_diff_files(const unsigned char *old_sha1,
275 const unsigned char *new_sha1, 276 const unsigned char *new_sha1,
276 unsigned long *old_size, unsigned long *new_size, 277 unsigned long *old_size, unsigned long *new_size,
277 int *binary, linediff_fn fn); 278 int *binary, linediff_fn fn);
278 279
279extern void cgit_diff_tree(const unsigned char *old_sha1, 280extern void cgit_diff_tree(const unsigned char *old_sha1,
280 const unsigned char *new_sha1, 281 const unsigned char *new_sha1,
281 filepair_fn fn, const char *prefix); 282 filepair_fn fn, const char *prefix);
282 283
283extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 284extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
284 285
285extern char *fmt(const char *format,...); 286extern char *fmt(const char *format,...);
286 287
287extern struct commitinfo *cgit_parse_commit(struct commit *commit); 288extern struct commitinfo *cgit_parse_commit(struct commit *commit);
288extern struct taginfo *cgit_parse_tag(struct tag *tag); 289extern struct taginfo *cgit_parse_tag(struct tag *tag);
289extern void cgit_parse_url(const char *url); 290extern void cgit_parse_url(const char *url);
290 291
291extern const char *cgit_repobasename(const char *reponame); 292extern const char *cgit_repobasename(const char *reponame);
292 293
293extern int cgit_parse_snapshots_mask(const char *str); 294extern int cgit_parse_snapshots_mask(const char *str);
294 295
295extern int cgit_open_filter(struct cgit_filter *filter); 296extern int cgit_open_filter(struct cgit_filter *filter);
296extern int cgit_close_filter(struct cgit_filter *filter); 297extern int cgit_close_filter(struct cgit_filter *filter);
297 298
298extern int readfile(const char *path, char **buf, size_t *size); 299extern int readfile(const char *path, char **buf, size_t *size);
299 300
300#endif /* CGIT_H */ 301#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index d74d9e7..6cdfd03 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,518 +1,522 @@
1:man source: cgit 1:man source: cgit
2:man manual: cgit 2:man manual: cgit
3 3
4CGITRC(5) 4CGITRC(5)
5======== 5========
6 6
7 7
8NAME 8NAME
9---- 9----
10cgitrc - runtime configuration for cgit 10cgitrc - runtime configuration for cgit
11 11
12 12
13SYNOPSIS 13SYNOPSIS
14-------- 14--------
15Cgitrc contains all runtime settings for cgit, including the list of git 15Cgitrc contains all runtime settings for cgit, including the list of git
16repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank 16repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank
17lines, and lines starting with '#', are ignored. 17lines, and lines starting with '#', are ignored.
18 18
19 19
20LOCATION 20LOCATION
21-------- 21--------
22The default location of cgitrc, defined at compile time, is /etc/cgitrc. At 22The default location of cgitrc, defined at compile time, is /etc/cgitrc. At
23runtime, cgit will consult the environment variable CGIT_CONFIG and, if 23runtime, cgit will consult the environment variable CGIT_CONFIG and, if
24defined, use its value instead. 24defined, use its value instead.
25 25
26 26
27GLOBAL SETTINGS 27GLOBAL SETTINGS
28--------------- 28---------------
29about-filter:: 29about-filter::
30 Specifies a command which will be invoked to format the content of 30 Specifies a command which will be invoked to format the content of
31 about pages (both top-level and for each repository). The command will 31 about pages (both top-level and for each repository). The command will
32 get the content of the about-file on its STDIN, and the STDOUT from the 32 get the content of the about-file on its STDIN, and the STDOUT from the
33 command will be included verbatim on the about page. Default value: 33 command will be included verbatim on the about page. Default value:
34 none. 34 none.
35 35
36agefile:: 36agefile::
37 Specifies a path, relative to each repository path, which can be used 37 Specifies a path, relative to each repository path, which can be used
38 to specify the date and time of the youngest commit in the repository. 38 to specify the date and time of the youngest commit in the repository.
39 The first line in the file is used as input to the "parse_date" 39 The first line in the file is used as input to the "parse_date"
40 function in libgit. Recommended timestamp-format is "yyyy-mm-dd 40 function in libgit. Recommended timestamp-format is "yyyy-mm-dd
41 hh:mm:ss". Default value: "info/web/last-modified". 41 hh:mm:ss". Default value: "info/web/last-modified".
42 42
43cache-root:: 43cache-root::
44 Path used to store the cgit cache entries. Default value: 44 Path used to store the cgit cache entries. Default value:
45 "/var/cache/cgit". 45 "/var/cache/cgit".
46 46
47cache-dynamic-ttl:: 47cache-dynamic-ttl::
48 Number which specifies the time-to-live, in minutes, for the cached 48 Number which specifies the time-to-live, in minutes, for the cached
49 version of repository pages accessed without a fixed SHA1. Default 49 version of repository pages accessed without a fixed SHA1. Default
50 value: "5". 50 value: "5".
51 51
52cache-repo-ttl:: 52cache-repo-ttl::
53 Number which specifies the time-to-live, in minutes, for the cached 53 Number which specifies the time-to-live, in minutes, for the cached
54 version of the repository summary page. Default value: "5". 54 version of the repository summary page. Default value: "5".
55 55
56cache-root-ttl:: 56cache-root-ttl::
57 Number which specifies the time-to-live, in minutes, for the cached 57 Number which specifies the time-to-live, in minutes, for the cached
58 version of the repository index page. Default value: "5". 58 version of the repository index page. Default value: "5".
59 59
60cache-scanrc-ttl:: 60cache-scanrc-ttl::
61 Number which specifies the time-to-live, in minutes, for the result 61 Number which specifies the time-to-live, in minutes, for the result
62 of scanning a path for git repositories. Default value: "15". 62 of scanning a path for git repositories. Default value: "15".
63 63
64cache-size:: 64cache-size::
65 The maximum number of entries in the cgit cache. Default value: "0" 65 The maximum number of entries in the cgit cache. Default value: "0"
66 (i.e. caching is disabled). 66 (i.e. caching is disabled).
67 67
68cache-static-ttl:: 68cache-static-ttl::
69 Number which specifies the time-to-live, in minutes, for the cached 69 Number which specifies the time-to-live, in minutes, for the cached
70 version of repository pages accessed with a fixed SHA1. Default value: 70 version of repository pages accessed with a fixed SHA1. Default value:
71 "5". 71 "5".
72 72
73clone-prefix:: 73clone-prefix::
74 Space-separated list of common prefixes which, when combined with a 74 Space-separated list of common prefixes which, when combined with a
75 repository url, generates valid clone urls for the repository. This 75 repository url, generates valid clone urls for the repository. This
76 setting is only used if `repo.clone-url` is unspecified. Default value: 76 setting is only used if `repo.clone-url` is unspecified. Default value:
77 none. 77 none.
78 78
79commit-filter:: 79commit-filter::
80 Specifies a command which will be invoked to format commit messages. 80 Specifies a command which will be invoked to format commit messages.
81 The command will get the message on its STDIN, and the STDOUT from the 81 The command will get the message on its STDIN, and the STDOUT from the
82 command will be included verbatim as the commit message, i.e. this can 82 command will be included verbatim as the commit message, i.e. this can
83 be used to implement bugtracker integration. Default value: none. 83 be used to implement bugtracker integration. Default value: none.
84 84
85css:: 85css::
86 Url which specifies the css document to include in all cgit pages. 86 Url which specifies the css document to include in all cgit pages.
87 Default value: "/cgit.css". 87 Default value: "/cgit.css".
88 88
89embedded:: 89embedded::
90 Flag which, when set to "1", will make cgit generate a html fragment 90 Flag which, when set to "1", will make cgit generate a html fragment
91 suitable for embedding in other html pages. Default value: none. See 91 suitable for embedding in other html pages. Default value: none. See
92 also: "noheader". 92 also: "noheader".
93 93
94enable-filter-overrides:: 94enable-filter-overrides::
95 Flag which, when set to "1", allows all filter settings to be 95 Flag which, when set to "1", allows all filter settings to be
96 overridden in repository-specific cgitrc files. Default value: none. 96 overridden in repository-specific cgitrc files. Default value: none.
97 97
98enable-index-links:: 98enable-index-links::
99 Flag which, when set to "1", will make cgit generate extra links for 99 Flag which, when set to "1", will make cgit generate extra links for
100 each repo in the repository index (specifically, to the "summary", 100 each repo in the repository index (specifically, to the "summary",
101 "commit" and "tree" pages). Default value: "0". 101 "commit" and "tree" pages). Default value: "0".
102 102
103enable-log-filecount:: 103enable-log-filecount::
104 Flag which, when set to "1", will make cgit print the number of 104 Flag which, when set to "1", will make cgit print the number of
105 modified files for each commit on the repository log page. Default 105 modified files for each commit on the repository log page. Default
106 value: "0". 106 value: "0".
107 107
108enable-log-linecount:: 108enable-log-linecount::
109 Flag which, when set to "1", will make cgit print the number of added 109 Flag which, when set to "1", will make cgit print the number of added
110 and removed lines for each commit on the repository log page. Default 110 and removed lines for each commit on the repository log page. Default
111 value: "0". 111 value: "0".
112 112
113enable-remote-branches:: 113enable-remote-branches::
114 Flag which, when set to "1", will make cgit display remote branches 114 Flag which, when set to "1", will make cgit display remote branches
115 in the summary and refs views. Default value: "0". See also: 115 in the summary and refs views. Default value: "0". See also:
116 "repo.enable-remote-branches". 116 "repo.enable-remote-branches".
117 117
118enable-tree-linenumbers:: 118enable-tree-linenumbers::
119 Flag which, when set to "1", will make cgit generate linenumber links 119 Flag which, when set to "1", will make cgit generate linenumber links
120 for plaintext blobs printed in the tree view. Default value: "1". 120 for plaintext blobs printed in the tree view. Default value: "1".
121 121
122favicon:: 122favicon::
123 Url used as link to a shortcut icon for cgit. If specified, it is 123 Url used as link to a shortcut icon for cgit. If specified, it is
124 suggested to use the value "/favicon.ico" since certain browsers will 124 suggested to use the value "/favicon.ico" since certain browsers will
125 ignore other values. Default value: none. 125 ignore other values. Default value: none.
126 126
127footer:: 127footer::
128 The content of the file specified with this option will be included 128 The content of the file specified with this option will be included
129 verbatim at the bottom of all pages (i.e. it replaces the standard 129 verbatim at the bottom of all pages (i.e. it replaces the standard
130 "generated by..." message. Default value: none. 130 "generated by..." message. Default value: none.
131 131
132head-include:: 132head-include::
133 The content of the file specified with this option will be included 133 The content of the file specified with this option will be included
134 verbatim in the html HEAD section on all pages. Default value: none. 134 verbatim in the html HEAD section on all pages. Default value: none.
135 135
136header:: 136header::
137 The content of the file specified with this option will be included 137 The content of the file specified with this option will be included
138 verbatim at the top of all pages. Default value: none. 138 verbatim at the top of all pages. Default value: none.
139 139
140include:: 140include::
141 Name of a configfile to include before the rest of the current config- 141 Name of a configfile to include before the rest of the current config-
142 file is parsed. Default value: none. 142 file is parsed. Default value: none.
143 143
144index-header:: 144index-header::
145 The content of the file specified with this option will be included 145 The content of the file specified with this option will be included
146 verbatim above the repository index. This setting is deprecated, and 146 verbatim above the repository index. This setting is deprecated, and
147 will not be supported by cgit-1.0 (use root-readme instead). Default 147 will not be supported by cgit-1.0 (use root-readme instead). Default
148 value: none. 148 value: none.
149 149
150index-info:: 150index-info::
151 The content of the file specified with this option will be included 151 The content of the file specified with this option will be included
152 verbatim below the heading on the repository index page. This setting 152 verbatim below the heading on the repository index page. This setting
153 is deprecated, and will not be supported by cgit-1.0 (use root-desc 153 is deprecated, and will not be supported by cgit-1.0 (use root-desc
154 instead). Default value: none. 154 instead). Default value: none.
155 155
156local-time:: 156local-time::
157 Flag which, if set to "1", makes cgit print commit and tag times in the 157 Flag which, if set to "1", makes cgit print commit and tag times in the
158 servers timezone. Default value: "0". 158 servers timezone. Default value: "0".
159 159
160logo:: 160logo::
161 Url which specifies the source of an image which will be used as a logo 161 Url which specifies the source of an image which will be used as a logo
162 on all cgit pages. Default value: "/cgit.png". 162 on all cgit pages. Default value: "/cgit.png".
163 163
164logo-link:: 164logo-link::
165 Url loaded when clicking on the cgit logo image. If unspecified the 165 Url loaded when clicking on the cgit logo image. If unspecified the
166 calculated url of the repository index page will be used. Default 166 calculated url of the repository index page will be used. Default
167 value: none. 167 value: none.
168 168
169max-atom-items::
170 Specifies the number of items to display in atom feeds view. Default
171 value: "10".
172
169max-commit-count:: 173max-commit-count::
170 Specifies the number of entries to list per page in "log" view. Default 174 Specifies the number of entries to list per page in "log" view. Default
171 value: "50". 175 value: "50".
172 176
173max-message-length:: 177max-message-length::
174 Specifies the maximum number of commit message characters to display in 178 Specifies the maximum number of commit message characters to display in
175 "log" view. Default value: "80". 179 "log" view. Default value: "80".
176 180
177max-repo-count:: 181max-repo-count::
178 Specifies the number of entries to list per page on therepository 182 Specifies the number of entries to list per page on therepository
179 index page. Default value: "50". 183 index page. Default value: "50".
180 184
181max-repodesc-length:: 185max-repodesc-length::
182 Specifies the maximum number of repo description characters to display 186 Specifies the maximum number of repo description characters to display
183 on the repository index page. Default value: "80". 187 on the repository index page. Default value: "80".
184 188
185max-blob-size:: 189max-blob-size::
186 Specifies the maximum size of a blob to display HTML for in KBytes. 190 Specifies the maximum size of a blob to display HTML for in KBytes.
187 Default value: "0" (limit disabled). 191 Default value: "0" (limit disabled).
188 192
189max-stats:: 193max-stats::
190 Set the default maximum statistics period. Valid values are "week", 194 Set the default maximum statistics period. Valid values are "week",
191 "month", "quarter" and "year". If unspecified, statistics are 195 "month", "quarter" and "year". If unspecified, statistics are
192 disabled. Default value: none. See also: "repo.max-stats". 196 disabled. Default value: none. See also: "repo.max-stats".
193 197
194mimetype.<ext>:: 198mimetype.<ext>::
195 Set the mimetype for the specified filename extension. This is used 199 Set the mimetype for the specified filename extension. This is used
196 by the `plain` command when returning blob content. 200 by the `plain` command when returning blob content.
197 201
198module-link:: 202module-link::
199 Text which will be used as the formatstring for a hyperlink when a 203 Text which will be used as the formatstring for a hyperlink when a
200 submodule is printed in a directory listing. The arguments for the 204 submodule is printed in a directory listing. The arguments for the
201 formatstring are the path and SHA1 of the submodule commit. Default 205 formatstring are the path and SHA1 of the submodule commit. Default
202 value: "./?repo=%s&page=commit&id=%s" 206 value: "./?repo=%s&page=commit&id=%s"
203 207
204nocache:: 208nocache::
205 If set to the value "1" caching will be disabled. This settings is 209 If set to the value "1" caching will be disabled. This settings is
206 deprecated, and will not be honored starting with cgit-1.0. Default 210 deprecated, and will not be honored starting with cgit-1.0. Default
207 value: "0". 211 value: "0".
208 212
209noplainemail:: 213noplainemail::
210 If set to "1" showing full author email adresses will be disabled. 214 If set to "1" showing full author email adresses will be disabled.
211 Default value: "0". 215 Default value: "0".
212 216
213noheader:: 217noheader::
214 Flag which, when set to "1", will make cgit omit the standard header 218 Flag which, when set to "1", will make cgit omit the standard header
215 on all pages. Default value: none. See also: "embedded". 219 on all pages. Default value: none. See also: "embedded".
216 220
217renamelimit:: 221renamelimit::
218 Maximum number of files to consider when detecting renames. The value 222 Maximum number of files to consider when detecting renames. The value
219 "-1" uses the compiletime value in git (for further info, look at 223 "-1" uses the compiletime value in git (for further info, look at
220 `man git-diff`). Default value: "-1". 224 `man git-diff`). Default value: "-1".
221 225
222repo.group:: 226repo.group::
223 Legacy alias for "section". This option is deprecated and will not be 227 Legacy alias for "section". This option is deprecated and will not be
224 supported in cgit-1.0. 228 supported in cgit-1.0.
225 229
226robots:: 230robots::
227 Text used as content for the "robots" meta-tag. Default value: 231 Text used as content for the "robots" meta-tag. Default value:
228 "index, nofollow". 232 "index, nofollow".
229 233
230root-desc:: 234root-desc::
231 Text printed below the heading on the repository index page. Default 235 Text printed below the heading on the repository index page. Default
232 value: "a fast webinterface for the git dscm". 236 value: "a fast webinterface for the git dscm".
233 237
234root-readme:: 238root-readme::
235 The content of the file specified with this option will be included 239 The content of the file specified with this option will be included
236 verbatim below the "about" link on the repository index page. Default 240 verbatim below the "about" link on the repository index page. Default
237 value: none. 241 value: none.
238 242
239root-title:: 243root-title::
240 Text printed as heading on the repository index page. Default value: 244 Text printed as heading on the repository index page. Default value:
241 "Git Repository Browser". 245 "Git Repository Browser".
242 246
243scan-path:: 247scan-path::
244 A path which will be scanned for repositories. If caching is enabled, 248 A path which will be scanned for repositories. If caching is enabled,
245 the result will be cached as a cgitrc include-file in the cache 249 the result will be cached as a cgitrc include-file in the cache
246 directory. Default value: none. See also: cache-scanrc-ttl. 250 directory. Default value: none. See also: cache-scanrc-ttl.
247 251
248section:: 252section::
249 The name of the current repository section - all repositories defined 253 The name of the current repository section - all repositories defined
250 after this option will inherit the current section name. Default value: 254 after this option will inherit the current section name. Default value:
251 none. 255 none.
252 256
253side-by-side-diffs:: 257side-by-side-diffs::
254 If set to "1" shows side-by-side diffs instead of unidiffs per 258 If set to "1" shows side-by-side diffs instead of unidiffs per
255 default. Default value: "0". 259 default. Default value: "0".
256 260
257snapshots:: 261snapshots::
258 Text which specifies the default set of snapshot formats generated by 262 Text which specifies the default set of snapshot formats generated by
259 cgit. The value is a space-separated list of zero or more of the 263 cgit. The value is a space-separated list of zero or more of the
260 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 264 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
261 265
262source-filter:: 266source-filter::
263 Specifies a command which will be invoked to format plaintext blobs 267 Specifies a command which will be invoked to format plaintext blobs
264 in the tree view. The command will get the blob content on its STDIN 268 in the tree view. The command will get the blob content on its STDIN
265 and the name of the blob as its only command line argument. The STDOUT 269 and the name of the blob as its only command line argument. The STDOUT
266 from the command will be included verbatim as the blob contents, i.e. 270 from the command will be included verbatim as the blob contents, i.e.
267 this can be used to implement e.g. syntax highlighting. Default value: 271 this can be used to implement e.g. syntax highlighting. Default value:
268 none. 272 none.
269 273
270summary-branches:: 274summary-branches::
271 Specifies the number of branches to display in the repository "summary" 275 Specifies the number of branches to display in the repository "summary"
272 view. Default value: "10". 276 view. Default value: "10".
273 277
274summary-log:: 278summary-log::
275 Specifies the number of log entries to display in the repository 279 Specifies the number of log entries to display in the repository
276 "summary" view. Default value: "10". 280 "summary" view. Default value: "10".
277 281
278summary-tags:: 282summary-tags::
279 Specifies the number of tags to display in the repository "summary" 283 Specifies the number of tags to display in the repository "summary"
280 view. Default value: "10". 284 view. Default value: "10".
281 285
282virtual-root:: 286virtual-root::
283 Url which, if specified, will be used as root for all cgit links. It 287 Url which, if specified, will be used as root for all cgit links. It
284 will also cause cgit to generate 'virtual urls', i.e. urls like 288 will also cause cgit to generate 'virtual urls', i.e. urls like
285 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 289 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
286 value: none. 290 value: none.
287 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 291 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
288 same kind of virtual urls, so this option will probably be deprecated. 292 same kind of virtual urls, so this option will probably be deprecated.
289 293
290REPOSITORY SETTINGS 294REPOSITORY SETTINGS
291------------------- 295-------------------
292repo.about-filter:: 296repo.about-filter::
293 Override the default about-filter. Default value: none. See also: 297 Override the default about-filter. Default value: none. See also:
294 "enable-filter-overrides". 298 "enable-filter-overrides".
295 299
296repo.clone-url:: 300repo.clone-url::
297 A list of space-separated urls which can be used to clone this repo. 301 A list of space-separated urls which can be used to clone this repo.
298 Default value: none. 302 Default value: none.
299 303
300repo.commit-filter:: 304repo.commit-filter::
301 Override the default commit-filter. Default value: none. See also: 305 Override the default commit-filter. Default value: none. See also:
302 "enable-filter-overrides". 306 "enable-filter-overrides".
303 307
304repo.defbranch:: 308repo.defbranch::
305 The name of the default branch for this repository. If no such branch 309 The name of the default branch for this repository. If no such branch
306 exists in the repository, the first branch name (when sorted) is used 310 exists in the repository, the first branch name (when sorted) is used
307 as default instead. Default value: "master". 311 as default instead. Default value: "master".
308 312
309repo.desc:: 313repo.desc::
310 The value to show as repository description. Default value: none. 314 The value to show as repository description. Default value: none.
311 315
312repo.enable-log-filecount:: 316repo.enable-log-filecount::
313 A flag which can be used to disable the global setting 317 A flag which can be used to disable the global setting
314 `enable-log-filecount'. Default value: none. 318 `enable-log-filecount'. Default value: none.
315 319
316repo.enable-log-linecount:: 320repo.enable-log-linecount::
317 A flag which can be used to disable the global setting 321 A flag which can be used to disable the global setting
318 `enable-log-linecount'. Default value: none. 322 `enable-log-linecount'. Default value: none.
319 323
320repo.enable-remote-branches:: 324repo.enable-remote-branches::
321 Flag which, when set to "1", will make cgit display remote branches 325 Flag which, when set to "1", will make cgit display remote branches
322 in the summary and refs views. Default value: <enable-remote-branches>. 326 in the summary and refs views. Default value: <enable-remote-branches>.
323 327
324repo.max-stats:: 328repo.max-stats::
325 Override the default maximum statistics period. Valid values are equal 329 Override the default maximum statistics period. Valid values are equal
326 to the values specified for the global "max-stats" setting. Default 330 to the values specified for the global "max-stats" setting. Default
327 value: none. 331 value: none.
328 332
329repo.name:: 333repo.name::
330 The value to show as repository name. Default value: <repo.url>. 334 The value to show as repository name. Default value: <repo.url>.
331 335
332repo.owner:: 336repo.owner::
333 A value used to identify the owner of the repository. Default value: 337 A value used to identify the owner of the repository. Default value:
334 none. 338 none.
335 339
336repo.path:: 340repo.path::
337 An absolute path to the repository directory. For non-bare repositories 341 An absolute path to the repository directory. For non-bare repositories
338 this is the .git-directory. Default value: none. 342 this is the .git-directory. Default value: none.
339 343
340repo.readme:: 344repo.readme::
341 A path (relative to <repo.path>) which specifies a file to include 345 A path (relative to <repo.path>) which specifies a file to include
342 verbatim as the "About" page for this repo. Default value: none. 346 verbatim as the "About" page for this repo. Default value: none.
343 347
344repo.snapshots:: 348repo.snapshots::
345 A mask of allowed snapshot-formats for this repo, restricted by the 349 A mask of allowed snapshot-formats for this repo, restricted by the
346 "snapshots" global setting. Default value: <snapshots>. 350 "snapshots" global setting. Default value: <snapshots>.
347 351
348repo.section:: 352repo.section::
349 Override the current section name for this repository. Default value: 353 Override the current section name for this repository. Default value:
350 none. 354 none.
351 355
352repo.source-filter:: 356repo.source-filter::
353 Override the default source-filter. Default value: none. See also: 357 Override the default source-filter. Default value: none. See also:
354 "enable-filter-overrides". 358 "enable-filter-overrides".
355 359
356repo.url:: 360repo.url::
357 The relative url used to access the repository. This must be the first 361 The relative url used to access the repository. This must be the first
358 setting specified for each repo. Default value: none. 362 setting specified for each repo. Default value: none.
359 363
360 364
361REPOSITORY-SPECIFIC CGITRC FILE 365REPOSITORY-SPECIFIC CGITRC FILE
362------------------------------- 366-------------------------------
363When the option "scan-path" is used to auto-discover git repositories, cgit 367When the option "scan-path" is used to auto-discover git repositories, cgit
364will try to parse the file "cgitrc" within any found repository. Such a 368will try to parse the file "cgitrc" within any found repository. Such a
365repo-specific config file may contain any of the repo-specific options 369repo-specific config file may contain any of the repo-specific options
366described above, except "repo.url" and "repo.path". Additionally, the "filter" 370described above, except "repo.url" and "repo.path". Additionally, the "filter"
367options are only acknowledged in repo-specific config files when 371options are only acknowledged in repo-specific config files when
368"enable-filter-overrides" is set to "1". 372"enable-filter-overrides" is set to "1".
369 373
370Note: the "repo." prefix is dropped from the option names in repo-specific 374Note: the "repo." prefix is dropped from the option names in repo-specific
371config files, e.g. "repo.desc" becomes "desc". 375config files, e.g. "repo.desc" becomes "desc".
372 376
373 377
374EXAMPLE CGITRC FILE 378EXAMPLE CGITRC FILE
375------------------- 379-------------------
376 380
377.... 381....
378# Enable caching of up to 1000 output entriess 382# Enable caching of up to 1000 output entriess
379cache-size=1000 383cache-size=1000
380 384
381 385
382# Specify some default clone prefixes 386# Specify some default clone prefixes
383clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git 387clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
384 388
385# Specify the css url 389# Specify the css url
386css=/css/cgit.css 390css=/css/cgit.css
387 391
388 392
389# Show extra links for each repository on the index page 393# Show extra links for each repository on the index page
390enable-index-links=1 394enable-index-links=1
391 395
392 396
393# Show number of affected files per commit on the log pages 397# Show number of affected files per commit on the log pages
394enable-log-filecount=1 398enable-log-filecount=1
395 399
396 400
397# Show number of added/removed lines per commit on the log pages 401# Show number of added/removed lines per commit on the log pages
398enable-log-linecount=1 402enable-log-linecount=1
399 403
400 404
401# Add a cgit favicon 405# Add a cgit favicon
402favicon=/favicon.ico 406favicon=/favicon.ico
403 407
404 408
405# Use a custom logo 409# Use a custom logo
406logo=/img/mylogo.png 410logo=/img/mylogo.png
407 411
408 412
409# Enable statistics per week, month and quarter 413# Enable statistics per week, month and quarter
410max-stats=quarter 414max-stats=quarter
411 415
412 416
413# Set the title and heading of the repository index page 417# Set the title and heading of the repository index page
414root-title=foobar.com git repositories 418root-title=foobar.com git repositories
415 419
416 420
417# Set a subheading for the repository index page 421# Set a subheading for the repository index page
418root-desc=tracking the foobar development 422root-desc=tracking the foobar development
419 423
420 424
421# Include some more info about foobar.com on the index page 425# Include some more info about foobar.com on the index page
422root-readme=/var/www/htdocs/about.html 426root-readme=/var/www/htdocs/about.html
423 427
424 428
425# Allow download of tar.gz, tar.bz2 and zip-files 429# Allow download of tar.gz, tar.bz2 and zip-files
426snapshots=tar.gz tar.bz2 zip 430snapshots=tar.gz tar.bz2 zip
427 431
428 432
429## 433##
430## List of common mimetypes 434## List of common mimetypes
431## 435##
432 436
433mimetype.git=image/git 437mimetype.git=image/git
434mimetype.html=text/html 438mimetype.html=text/html
435mimetype.jpg=image/jpeg 439mimetype.jpg=image/jpeg
436mimetype.jpeg=image/jpeg 440mimetype.jpeg=image/jpeg
437mimetype.pdf=application/pdf 441mimetype.pdf=application/pdf
438mimetype.png=image/png 442mimetype.png=image/png
439mimetype.svg=image/svg+xml 443mimetype.svg=image/svg+xml
440 444
441 445
442## 446##
443## List of repositories. 447## List of repositories.
444## PS: Any repositories listed when section is unset will not be 448## PS: Any repositories listed when section is unset will not be
445## displayed under a section heading 449## displayed under a section heading
446## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') 450## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
447## and included like this: 451## and included like this:
448## include=/etc/cgitrepos 452## include=/etc/cgitrepos
449## 453##
450 454
451 455
452repo.url=foo 456repo.url=foo
453repo.path=/pub/git/foo.git 457repo.path=/pub/git/foo.git
454repo.desc=the master foo repository 458repo.desc=the master foo repository
455repo.owner=fooman@foobar.com 459repo.owner=fooman@foobar.com
456repo.readme=info/web/about.html 460repo.readme=info/web/about.html
457 461
458 462
459repo.url=bar 463repo.url=bar
460repo.path=/pub/git/bar.git 464repo.path=/pub/git/bar.git
461repo.desc=the bars for your foo 465repo.desc=the bars for your foo
462repo.owner=barman@foobar.com 466repo.owner=barman@foobar.com
463repo.readme=info/web/about.html 467repo.readme=info/web/about.html
464 468
465 469
466# The next repositories will be displayed under the 'extras' heading 470# The next repositories will be displayed under the 'extras' heading
467section=extras 471section=extras
468 472
469 473
470repo.url=baz 474repo.url=baz
471repo.path=/pub/git/baz.git 475repo.path=/pub/git/baz.git
472repo.desc=a set of extensions for bar users 476repo.desc=a set of extensions for bar users
473 477
474repo.url=wiz 478repo.url=wiz
475repo.path=/pub/git/wiz.git 479repo.path=/pub/git/wiz.git
476repo.desc=the wizard of foo 480repo.desc=the wizard of foo
477 481
478 482
479# Add some mirrored repositories 483# Add some mirrored repositories
480section=mirrors 484section=mirrors
481 485
482 486
483repo.url=git 487repo.url=git
484repo.path=/pub/git/git.git 488repo.path=/pub/git/git.git
485repo.desc=the dscm 489repo.desc=the dscm
486 490
487 491
488repo.url=linux 492repo.url=linux
489repo.path=/pub/git/linux.git 493repo.path=/pub/git/linux.git
490repo.desc=the kernel 494repo.desc=the kernel
491 495
492# Disable adhoc downloads of this repo 496# Disable adhoc downloads of this repo
493repo.snapshots=0 497repo.snapshots=0
494 498
495# Disable line-counts for this repo 499# Disable line-counts for this repo
496repo.enable-log-linecount=0 500repo.enable-log-linecount=0
497 501
498# Restrict the max statistics period for this repo 502# Restrict the max statistics period for this repo
499repo.max-stats=month 503repo.max-stats=month
500.... 504....
501 505
502 506
503BUGS 507BUGS
504---- 508----
505Comments currently cannot appear on the same line as a setting; the comment 509Comments currently cannot appear on the same line as a setting; the comment
506will be included as part of the value. E.g. this line: 510will be included as part of the value. E.g. this line:
507 511
508 robots=index # allow indexing 512 robots=index # allow indexing
509 513
510will generate the following html element: 514will generate the following html element:
511 515
512 <meta name='robots' content='index # allow indexing'/> 516 <meta name='robots' content='index # allow indexing'/>
513 517
514 518
515 519
516AUTHOR 520AUTHOR
517------ 521------
518Lars Hjemli <hjemli@gmail.com> 522Lars Hjemli <hjemli@gmail.com>
diff --git a/cmd.c b/cmd.c
index 766f903..ad784fc 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,171 +1,171 @@
1/* cmd.c: the cgit command dispatcher 1/* cmd.c: the cgit command dispatcher
2 * 2 *
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cmd.h" 10#include "cmd.h"
11#include "cache.h" 11#include "cache.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13#include "ui-atom.h" 13#include "ui-atom.h"
14#include "ui-blob.h" 14#include "ui-blob.h"
15#include "ui-clone.h" 15#include "ui-clone.h"
16#include "ui-commit.h" 16#include "ui-commit.h"
17#include "ui-diff.h" 17#include "ui-diff.h"
18#include "ui-log.h" 18#include "ui-log.h"
19#include "ui-patch.h" 19#include "ui-patch.h"
20#include "ui-plain.h" 20#include "ui-plain.h"
21#include "ui-refs.h" 21#include "ui-refs.h"
22#include "ui-repolist.h" 22#include "ui-repolist.h"
23#include "ui-snapshot.h" 23#include "ui-snapshot.h"
24#include "ui-stats.h" 24#include "ui-stats.h"
25#include "ui-summary.h" 25#include "ui-summary.h"
26#include "ui-tag.h" 26#include "ui-tag.h"
27#include "ui-tree.h" 27#include "ui-tree.h"
28 28
29static void HEAD_fn(struct cgit_context *ctx) 29static void HEAD_fn(struct cgit_context *ctx)
30{ 30{
31 cgit_clone_head(ctx); 31 cgit_clone_head(ctx);
32} 32}
33 33
34static void atom_fn(struct cgit_context *ctx) 34static void atom_fn(struct cgit_context *ctx)
35{ 35{
36 cgit_print_atom(ctx->qry.head, ctx->qry.path, 10); 36 cgit_print_atom(ctx->qry.head, ctx->qry.path, ctx->cfg.max_atom_items);
37} 37}
38 38
39static void about_fn(struct cgit_context *ctx) 39static void about_fn(struct cgit_context *ctx)
40{ 40{
41 if (ctx->repo) 41 if (ctx->repo)
42 cgit_print_repo_readme(ctx->qry.path); 42 cgit_print_repo_readme(ctx->qry.path);
43 else 43 else
44 cgit_print_site_readme(); 44 cgit_print_site_readme();
45} 45}
46 46
47static void blob_fn(struct cgit_context *ctx) 47static void blob_fn(struct cgit_context *ctx)
48{ 48{
49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head); 49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head);
50} 50}
51 51
52static void commit_fn(struct cgit_context *ctx) 52static void commit_fn(struct cgit_context *ctx)
53{ 53{
54 cgit_print_commit(ctx->qry.sha1); 54 cgit_print_commit(ctx->qry.sha1);
55} 55}
56 56
57static void diff_fn(struct cgit_context *ctx) 57static void diff_fn(struct cgit_context *ctx)
58{ 58{
59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); 59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
60} 60}
61 61
62static void info_fn(struct cgit_context *ctx) 62static void info_fn(struct cgit_context *ctx)
63{ 63{
64 cgit_clone_info(ctx); 64 cgit_clone_info(ctx);
65} 65}
66 66
67static void log_fn(struct cgit_context *ctx) 67static void log_fn(struct cgit_context *ctx)
68{ 68{
69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, 69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); 70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
71} 71}
72 72
73static void ls_cache_fn(struct cgit_context *ctx) 73static void ls_cache_fn(struct cgit_context *ctx)
74{ 74{
75 ctx->page.mimetype = "text/plain"; 75 ctx->page.mimetype = "text/plain";
76 ctx->page.filename = "ls-cache.txt"; 76 ctx->page.filename = "ls-cache.txt";
77 cgit_print_http_headers(ctx); 77 cgit_print_http_headers(ctx);
78 cache_ls(ctx->cfg.cache_root); 78 cache_ls(ctx->cfg.cache_root);
79} 79}
80 80
81static void objects_fn(struct cgit_context *ctx) 81static void objects_fn(struct cgit_context *ctx)
82{ 82{
83 cgit_clone_objects(ctx); 83 cgit_clone_objects(ctx);
84} 84}
85 85
86static void repolist_fn(struct cgit_context *ctx) 86static void repolist_fn(struct cgit_context *ctx)
87{ 87{
88 cgit_print_repolist(); 88 cgit_print_repolist();
89} 89}
90 90
91static void patch_fn(struct cgit_context *ctx) 91static void patch_fn(struct cgit_context *ctx)
92{ 92{
93 cgit_print_patch(ctx->qry.sha1); 93 cgit_print_patch(ctx->qry.sha1);
94} 94}
95 95
96static void plain_fn(struct cgit_context *ctx) 96static void plain_fn(struct cgit_context *ctx)
97{ 97{
98 cgit_print_plain(ctx); 98 cgit_print_plain(ctx);
99} 99}
100 100
101static void refs_fn(struct cgit_context *ctx) 101static void refs_fn(struct cgit_context *ctx)
102{ 102{
103 cgit_print_refs(); 103 cgit_print_refs();
104} 104}
105 105
106static void snapshot_fn(struct cgit_context *ctx) 106static void snapshot_fn(struct cgit_context *ctx)
107{ 107{
108 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, ctx->qry.path, 108 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, ctx->qry.path,
109 ctx->repo->snapshots, ctx->qry.nohead); 109 ctx->repo->snapshots, ctx->qry.nohead);
110} 110}
111 111
112static void stats_fn(struct cgit_context *ctx) 112static void stats_fn(struct cgit_context *ctx)
113{ 113{
114 cgit_show_stats(ctx); 114 cgit_show_stats(ctx);
115} 115}
116 116
117static void summary_fn(struct cgit_context *ctx) 117static void summary_fn(struct cgit_context *ctx)
118{ 118{
119 cgit_print_summary(); 119 cgit_print_summary();
120} 120}
121 121
122static void tag_fn(struct cgit_context *ctx) 122static void tag_fn(struct cgit_context *ctx)
123{ 123{
124 cgit_print_tag(ctx->qry.sha1); 124 cgit_print_tag(ctx->qry.sha1);
125} 125}
126 126
127static void tree_fn(struct cgit_context *ctx) 127static void tree_fn(struct cgit_context *ctx)
128{ 128{
129 cgit_print_tree(ctx->qry.sha1, ctx->qry.path); 129 cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
130} 130}
131 131
132#define def_cmd(name, want_repo, want_layout) \ 132#define def_cmd(name, want_repo, want_layout) \
133 {#name, name##_fn, want_repo, want_layout} 133 {#name, name##_fn, want_repo, want_layout}
134 134
135struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx) 135struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
136{ 136{
137 static struct cgit_cmd cmds[] = { 137 static struct cgit_cmd cmds[] = {
138 def_cmd(HEAD, 1, 0), 138 def_cmd(HEAD, 1, 0),
139 def_cmd(atom, 1, 0), 139 def_cmd(atom, 1, 0),
140 def_cmd(about, 0, 1), 140 def_cmd(about, 0, 1),
141 def_cmd(blob, 1, 0), 141 def_cmd(blob, 1, 0),
142 def_cmd(commit, 1, 1), 142 def_cmd(commit, 1, 1),
143 def_cmd(diff, 1, 1), 143 def_cmd(diff, 1, 1),
144 def_cmd(info, 1, 0), 144 def_cmd(info, 1, 0),
145 def_cmd(log, 1, 1), 145 def_cmd(log, 1, 1),
146 def_cmd(ls_cache, 0, 0), 146 def_cmd(ls_cache, 0, 0),
147 def_cmd(objects, 1, 0), 147 def_cmd(objects, 1, 0),
148 def_cmd(patch, 1, 0), 148 def_cmd(patch, 1, 0),
149 def_cmd(plain, 1, 0), 149 def_cmd(plain, 1, 0),
150 def_cmd(refs, 1, 1), 150 def_cmd(refs, 1, 1),
151 def_cmd(repolist, 0, 0), 151 def_cmd(repolist, 0, 0),
152 def_cmd(snapshot, 1, 0), 152 def_cmd(snapshot, 1, 0),
153 def_cmd(stats, 1, 1), 153 def_cmd(stats, 1, 1),
154 def_cmd(summary, 1, 1), 154 def_cmd(summary, 1, 1),
155 def_cmd(tag, 1, 1), 155 def_cmd(tag, 1, 1),
156 def_cmd(tree, 1, 1), 156 def_cmd(tree, 1, 1),
157 }; 157 };
158 int i; 158 int i;
159 159
160 if (ctx->qry.page == NULL) { 160 if (ctx->qry.page == NULL) {
161 if (ctx->repo) 161 if (ctx->repo)
162 ctx->qry.page = "summary"; 162 ctx->qry.page = "summary";
163 else 163 else
164 ctx->qry.page = "repolist"; 164 ctx->qry.page = "repolist";
165 } 165 }
166 166
167 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) 167 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
168 if (!strcmp(ctx->qry.page, cmds[i].name)) 168 if (!strcmp(ctx->qry.page, cmds[i].name))
169 return &cmds[i]; 169 return &cmds[i];
170 return NULL; 170 return NULL;
171} 171}