summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2009-08-23 20:58:39 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2009-08-24 08:22:58 (UTC)
commite7af002d5c405c82652f739d08ced3908d1f57e7 (patch) (unidiff)
tree1e31e5dd8d33ca4a8392c4d6207b2ff6bf8a0d76
parent50d5af3adcdd90424b70e9472af24356ed50aa9b (diff)
downloadcgit-e7af002d5c405c82652f739d08ced3908d1f57e7.zip
cgit-e7af002d5c405c82652f739d08ced3908d1f57e7.tar.gz
cgit-e7af002d5c405c82652f739d08ced3908d1f57e7.tar.bz2
Introduce 'section' as canonical spelling for 'repo.group'
The 'repo.' prefix should be reserved for repo-specific options, but the option 'repo.group' must still be honored to stay backwards compatible. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c4
-rw-r--r--cgit.css2
-rw-r--r--cgit.h4
-rw-r--r--cgitrc.5.txt9
-rw-r--r--shared.c2
-rw-r--r--ui-repolist.c18
6 files changed, 22 insertions, 17 deletions
diff --git a/cgit.c b/cgit.c
index a792fe4..013a0fe 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,531 +1,531 @@
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 config_cb(const char *name, const char *value) 45void config_cb(const char *name, const char *value)
46{ 46{
47 if (!strcmp(name, "root-title")) 47 if (!strcmp(name, "root-title"))
48 ctx.cfg.root_title = xstrdup(value); 48 ctx.cfg.root_title = xstrdup(value);
49 else if (!strcmp(name, "root-desc")) 49 else if (!strcmp(name, "root-desc"))
50 ctx.cfg.root_desc = xstrdup(value); 50 ctx.cfg.root_desc = xstrdup(value);
51 else if (!strcmp(name, "root-readme")) 51 else if (!strcmp(name, "root-readme"))
52 ctx.cfg.root_readme = xstrdup(value); 52 ctx.cfg.root_readme = xstrdup(value);
53 else if (!strcmp(name, "css")) 53 else if (!strcmp(name, "css"))
54 ctx.cfg.css = xstrdup(value); 54 ctx.cfg.css = xstrdup(value);
55 else if (!strcmp(name, "favicon")) 55 else if (!strcmp(name, "favicon"))
56 ctx.cfg.favicon = xstrdup(value); 56 ctx.cfg.favicon = xstrdup(value);
57 else if (!strcmp(name, "footer")) 57 else if (!strcmp(name, "footer"))
58 ctx.cfg.footer = xstrdup(value); 58 ctx.cfg.footer = xstrdup(value);
59 else if (!strcmp(name, "head-include")) 59 else if (!strcmp(name, "head-include"))
60 ctx.cfg.head_include = xstrdup(value); 60 ctx.cfg.head_include = xstrdup(value);
61 else if (!strcmp(name, "header")) 61 else if (!strcmp(name, "header"))
62 ctx.cfg.header = xstrdup(value); 62 ctx.cfg.header = xstrdup(value);
63 else if (!strcmp(name, "logo")) 63 else if (!strcmp(name, "logo"))
64 ctx.cfg.logo = xstrdup(value); 64 ctx.cfg.logo = xstrdup(value);
65 else if (!strcmp(name, "index-header")) 65 else if (!strcmp(name, "index-header"))
66 ctx.cfg.index_header = xstrdup(value); 66 ctx.cfg.index_header = xstrdup(value);
67 else if (!strcmp(name, "index-info")) 67 else if (!strcmp(name, "index-info"))
68 ctx.cfg.index_info = xstrdup(value); 68 ctx.cfg.index_info = xstrdup(value);
69 else if (!strcmp(name, "logo-link")) 69 else if (!strcmp(name, "logo-link"))
70 ctx.cfg.logo_link = xstrdup(value); 70 ctx.cfg.logo_link = xstrdup(value);
71 else if (!strcmp(name, "module-link")) 71 else if (!strcmp(name, "module-link"))
72 ctx.cfg.module_link = xstrdup(value); 72 ctx.cfg.module_link = xstrdup(value);
73 else if (!strcmp(name, "virtual-root")) { 73 else if (!strcmp(name, "virtual-root")) {
74 ctx.cfg.virtual_root = trim_end(value, '/'); 74 ctx.cfg.virtual_root = trim_end(value, '/');
75 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 75 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
76 ctx.cfg.virtual_root = ""; 76 ctx.cfg.virtual_root = "";
77 } else if (!strcmp(name, "nocache")) 77 } else if (!strcmp(name, "nocache"))
78 ctx.cfg.nocache = atoi(value); 78 ctx.cfg.nocache = atoi(value);
79 else if (!strcmp(name, "noplainemail")) 79 else if (!strcmp(name, "noplainemail"))
80 ctx.cfg.noplainemail = atoi(value); 80 ctx.cfg.noplainemail = atoi(value);
81 else if (!strcmp(name, "noheader")) 81 else if (!strcmp(name, "noheader"))
82 ctx.cfg.noheader = atoi(value); 82 ctx.cfg.noheader = atoi(value);
83 else if (!strcmp(name, "snapshots")) 83 else if (!strcmp(name, "snapshots"))
84 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 84 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
85 else if (!strcmp(name, "enable-index-links")) 85 else if (!strcmp(name, "enable-index-links"))
86 ctx.cfg.enable_index_links = atoi(value); 86 ctx.cfg.enable_index_links = atoi(value);
87 else if (!strcmp(name, "enable-log-filecount")) 87 else if (!strcmp(name, "enable-log-filecount"))
88 ctx.cfg.enable_log_filecount = atoi(value); 88 ctx.cfg.enable_log_filecount = atoi(value);
89 else if (!strcmp(name, "enable-log-linecount")) 89 else if (!strcmp(name, "enable-log-linecount"))
90 ctx.cfg.enable_log_linecount = atoi(value); 90 ctx.cfg.enable_log_linecount = atoi(value);
91 else if (!strcmp(name, "max-stats")) 91 else if (!strcmp(name, "max-stats"))
92 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 92 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
93 else if (!strcmp(name, "cache-size")) 93 else if (!strcmp(name, "cache-size"))
94 ctx.cfg.cache_size = atoi(value); 94 ctx.cfg.cache_size = atoi(value);
95 else if (!strcmp(name, "cache-root")) 95 else if (!strcmp(name, "cache-root"))
96 ctx.cfg.cache_root = xstrdup(value); 96 ctx.cfg.cache_root = xstrdup(value);
97 else if (!strcmp(name, "cache-root-ttl")) 97 else if (!strcmp(name, "cache-root-ttl"))
98 ctx.cfg.cache_root_ttl = atoi(value); 98 ctx.cfg.cache_root_ttl = atoi(value);
99 else if (!strcmp(name, "cache-repo-ttl")) 99 else if (!strcmp(name, "cache-repo-ttl"))
100 ctx.cfg.cache_repo_ttl = atoi(value); 100 ctx.cfg.cache_repo_ttl = atoi(value);
101 else if (!strcmp(name, "cache-scanrc-ttl")) 101 else if (!strcmp(name, "cache-scanrc-ttl"))
102 ctx.cfg.cache_scanrc_ttl = atoi(value); 102 ctx.cfg.cache_scanrc_ttl = atoi(value);
103 else if (!strcmp(name, "cache-static-ttl")) 103 else if (!strcmp(name, "cache-static-ttl"))
104 ctx.cfg.cache_static_ttl = atoi(value); 104 ctx.cfg.cache_static_ttl = atoi(value);
105 else if (!strcmp(name, "cache-dynamic-ttl")) 105 else if (!strcmp(name, "cache-dynamic-ttl"))
106 ctx.cfg.cache_dynamic_ttl = atoi(value); 106 ctx.cfg.cache_dynamic_ttl = atoi(value);
107 else if (!strcmp(name, "about-filter")) 107 else if (!strcmp(name, "about-filter"))
108 ctx.cfg.about_filter = new_filter(value, 0); 108 ctx.cfg.about_filter = new_filter(value, 0);
109 else if (!strcmp(name, "commit-filter")) 109 else if (!strcmp(name, "commit-filter"))
110 ctx.cfg.commit_filter = new_filter(value, 0); 110 ctx.cfg.commit_filter = new_filter(value, 0);
111 else if (!strcmp(name, "embedded")) 111 else if (!strcmp(name, "embedded"))
112 ctx.cfg.embedded = atoi(value); 112 ctx.cfg.embedded = atoi(value);
113 else if (!strcmp(name, "max-message-length")) 113 else if (!strcmp(name, "max-message-length"))
114 ctx.cfg.max_msg_len = atoi(value); 114 ctx.cfg.max_msg_len = atoi(value);
115 else if (!strcmp(name, "max-repodesc-length")) 115 else if (!strcmp(name, "max-repodesc-length"))
116 ctx.cfg.max_repodesc_len = atoi(value); 116 ctx.cfg.max_repodesc_len = atoi(value);
117 else if (!strcmp(name, "max-repo-count")) 117 else if (!strcmp(name, "max-repo-count"))
118 ctx.cfg.max_repo_count = atoi(value); 118 ctx.cfg.max_repo_count = atoi(value);
119 else if (!strcmp(name, "max-commit-count")) 119 else if (!strcmp(name, "max-commit-count"))
120 ctx.cfg.max_commit_count = atoi(value); 120 ctx.cfg.max_commit_count = atoi(value);
121 else if (!strcmp(name, "scan-path")) 121 else if (!strcmp(name, "scan-path"))
122 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 122 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
123 process_cached_repolist(value); 123 process_cached_repolist(value);
124 else 124 else
125 scan_tree(value); 125 scan_tree(value);
126 else if (!strcmp(name, "source-filter")) 126 else if (!strcmp(name, "source-filter"))
127 ctx.cfg.source_filter = new_filter(value, 1); 127 ctx.cfg.source_filter = new_filter(value, 1);
128 else if (!strcmp(name, "summary-log")) 128 else if (!strcmp(name, "summary-log"))
129 ctx.cfg.summary_log = atoi(value); 129 ctx.cfg.summary_log = atoi(value);
130 else if (!strcmp(name, "summary-branches")) 130 else if (!strcmp(name, "summary-branches"))
131 ctx.cfg.summary_branches = atoi(value); 131 ctx.cfg.summary_branches = atoi(value);
132 else if (!strcmp(name, "summary-tags")) 132 else if (!strcmp(name, "summary-tags"))
133 ctx.cfg.summary_tags = atoi(value); 133 ctx.cfg.summary_tags = atoi(value);
134 else if (!strcmp(name, "agefile")) 134 else if (!strcmp(name, "agefile"))
135 ctx.cfg.agefile = xstrdup(value); 135 ctx.cfg.agefile = xstrdup(value);
136 else if (!strcmp(name, "renamelimit")) 136 else if (!strcmp(name, "renamelimit"))
137 ctx.cfg.renamelimit = atoi(value); 137 ctx.cfg.renamelimit = atoi(value);
138 else if (!strcmp(name, "robots")) 138 else if (!strcmp(name, "robots"))
139 ctx.cfg.robots = xstrdup(value); 139 ctx.cfg.robots = xstrdup(value);
140 else if (!strcmp(name, "clone-prefix")) 140 else if (!strcmp(name, "clone-prefix"))
141 ctx.cfg.clone_prefix = xstrdup(value); 141 ctx.cfg.clone_prefix = xstrdup(value);
142 else if (!strcmp(name, "local-time")) 142 else if (!strcmp(name, "local-time"))
143 ctx.cfg.local_time = atoi(value); 143 ctx.cfg.local_time = atoi(value);
144 else if (!prefixcmp(name, "mimetype.")) 144 else if (!prefixcmp(name, "mimetype."))
145 add_mimetype(name + 9, value); 145 add_mimetype(name + 9, value);
146 else if (!strcmp(name, "repo.group")) 146 else if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
147 ctx.cfg.repo_group = xstrdup(value); 147 ctx.cfg.section = xstrdup(value);
148 else if (!strcmp(name, "repo.url")) 148 else if (!strcmp(name, "repo.url"))
149 ctx.repo = cgit_add_repo(value); 149 ctx.repo = cgit_add_repo(value);
150 else if (!strcmp(name, "repo.name")) 150 else if (!strcmp(name, "repo.name"))
151 ctx.repo->name = xstrdup(value); 151 ctx.repo->name = xstrdup(value);
152 else if (ctx.repo && !strcmp(name, "repo.path")) 152 else if (ctx.repo && !strcmp(name, "repo.path"))
153 ctx.repo->path = trim_end(value, '/'); 153 ctx.repo->path = trim_end(value, '/');
154 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 154 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
155 ctx.repo->clone_url = xstrdup(value); 155 ctx.repo->clone_url = xstrdup(value);
156 else if (ctx.repo && !strcmp(name, "repo.desc")) 156 else if (ctx.repo && !strcmp(name, "repo.desc"))
157 ctx.repo->desc = xstrdup(value); 157 ctx.repo->desc = xstrdup(value);
158 else if (ctx.repo && !strcmp(name, "repo.owner")) 158 else if (ctx.repo && !strcmp(name, "repo.owner"))
159 ctx.repo->owner = xstrdup(value); 159 ctx.repo->owner = xstrdup(value);
160 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 160 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
161 ctx.repo->defbranch = xstrdup(value); 161 ctx.repo->defbranch = xstrdup(value);
162 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 162 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
163 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 163 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
164 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 164 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
165 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 165 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
166 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 166 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
167 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 167 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
168 else if (ctx.repo && !strcmp(name, "repo.max-stats")) 168 else if (ctx.repo && !strcmp(name, "repo.max-stats"))
169 ctx.repo->max_stats = cgit_find_stats_period(value, NULL); 169 ctx.repo->max_stats = cgit_find_stats_period(value, NULL);
170 else if (ctx.repo && !strcmp(name, "repo.module-link")) 170 else if (ctx.repo && !strcmp(name, "repo.module-link"))
171 ctx.repo->module_link= xstrdup(value); 171 ctx.repo->module_link= xstrdup(value);
172 else if (ctx.repo && !strcmp(name, "repo.about-filter")) 172 else if (ctx.repo && !strcmp(name, "repo.about-filter"))
173 ctx.repo->about_filter = new_filter(value, 0); 173 ctx.repo->about_filter = new_filter(value, 0);
174 else if (ctx.repo && !strcmp(name, "repo.commit-filter")) 174 else if (ctx.repo && !strcmp(name, "repo.commit-filter"))
175 ctx.repo->commit_filter = new_filter(value, 0); 175 ctx.repo->commit_filter = new_filter(value, 0);
176 else if (ctx.repo && !strcmp(name, "repo.source-filter")) 176 else if (ctx.repo && !strcmp(name, "repo.source-filter"))
177 ctx.repo->source_filter = new_filter(value, 1); 177 ctx.repo->source_filter = new_filter(value, 1);
178 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 178 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
179 if (*value == '/') 179 if (*value == '/')
180 ctx.repo->readme = xstrdup(value); 180 ctx.repo->readme = xstrdup(value);
181 else 181 else
182 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 182 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
183 } else if (!strcmp(name, "include")) 183 } else if (!strcmp(name, "include"))
184 parse_configfile(value, config_cb); 184 parse_configfile(value, config_cb);
185} 185}
186 186
187static void querystring_cb(const char *name, const char *value) 187static void querystring_cb(const char *name, const char *value)
188{ 188{
189 if (!value) 189 if (!value)
190 value = ""; 190 value = "";
191 191
192 if (!strcmp(name,"r")) { 192 if (!strcmp(name,"r")) {
193 ctx.qry.repo = xstrdup(value); 193 ctx.qry.repo = xstrdup(value);
194 ctx.repo = cgit_get_repoinfo(value); 194 ctx.repo = cgit_get_repoinfo(value);
195 } else if (!strcmp(name, "p")) { 195 } else if (!strcmp(name, "p")) {
196 ctx.qry.page = xstrdup(value); 196 ctx.qry.page = xstrdup(value);
197 } else if (!strcmp(name, "url")) { 197 } else if (!strcmp(name, "url")) {
198 ctx.qry.url = xstrdup(value); 198 ctx.qry.url = xstrdup(value);
199 cgit_parse_url(value); 199 cgit_parse_url(value);
200 } else if (!strcmp(name, "qt")) { 200 } else if (!strcmp(name, "qt")) {
201 ctx.qry.grep = xstrdup(value); 201 ctx.qry.grep = xstrdup(value);
202 } else if (!strcmp(name, "q")) { 202 } else if (!strcmp(name, "q")) {
203 ctx.qry.search = xstrdup(value); 203 ctx.qry.search = xstrdup(value);
204 } else if (!strcmp(name, "h")) { 204 } else if (!strcmp(name, "h")) {
205 ctx.qry.head = xstrdup(value); 205 ctx.qry.head = xstrdup(value);
206 ctx.qry.has_symref = 1; 206 ctx.qry.has_symref = 1;
207 } else if (!strcmp(name, "id")) { 207 } else if (!strcmp(name, "id")) {
208 ctx.qry.sha1 = xstrdup(value); 208 ctx.qry.sha1 = xstrdup(value);
209 ctx.qry.has_sha1 = 1; 209 ctx.qry.has_sha1 = 1;
210 } else if (!strcmp(name, "id2")) { 210 } else if (!strcmp(name, "id2")) {
211 ctx.qry.sha2 = xstrdup(value); 211 ctx.qry.sha2 = xstrdup(value);
212 ctx.qry.has_sha1 = 1; 212 ctx.qry.has_sha1 = 1;
213 } else if (!strcmp(name, "ofs")) { 213 } else if (!strcmp(name, "ofs")) {
214 ctx.qry.ofs = atoi(value); 214 ctx.qry.ofs = atoi(value);
215 } else if (!strcmp(name, "path")) { 215 } else if (!strcmp(name, "path")) {
216 ctx.qry.path = trim_end(value, '/'); 216 ctx.qry.path = trim_end(value, '/');
217 } else if (!strcmp(name, "name")) { 217 } else if (!strcmp(name, "name")) {
218 ctx.qry.name = xstrdup(value); 218 ctx.qry.name = xstrdup(value);
219 } else if (!strcmp(name, "mimetype")) { 219 } else if (!strcmp(name, "mimetype")) {
220 ctx.qry.mimetype = xstrdup(value); 220 ctx.qry.mimetype = xstrdup(value);
221 } else if (!strcmp(name, "s")){ 221 } else if (!strcmp(name, "s")){
222 ctx.qry.sort = xstrdup(value); 222 ctx.qry.sort = xstrdup(value);
223 } else if (!strcmp(name, "showmsg")) { 223 } else if (!strcmp(name, "showmsg")) {
224 ctx.qry.showmsg = atoi(value); 224 ctx.qry.showmsg = atoi(value);
225 } else if (!strcmp(name, "period")) { 225 } else if (!strcmp(name, "period")) {
226 ctx.qry.period = xstrdup(value); 226 ctx.qry.period = xstrdup(value);
227 } 227 }
228} 228}
229 229
230char *xstrdupn(const char *str) 230char *xstrdupn(const char *str)
231{ 231{
232 return (str ? xstrdup(str) : NULL); 232 return (str ? xstrdup(str) : NULL);
233} 233}
234 234
235static void prepare_context(struct cgit_context *ctx) 235static void prepare_context(struct cgit_context *ctx)
236{ 236{
237 memset(ctx, 0, sizeof(ctx)); 237 memset(ctx, 0, sizeof(ctx));
238 ctx->cfg.agefile = "info/web/last-modified"; 238 ctx->cfg.agefile = "info/web/last-modified";
239 ctx->cfg.nocache = 0; 239 ctx->cfg.nocache = 0;
240 ctx->cfg.cache_size = 0; 240 ctx->cfg.cache_size = 0;
241 ctx->cfg.cache_dynamic_ttl = 5; 241 ctx->cfg.cache_dynamic_ttl = 5;
242 ctx->cfg.cache_max_create_time = 5; 242 ctx->cfg.cache_max_create_time = 5;
243 ctx->cfg.cache_repo_ttl = 5; 243 ctx->cfg.cache_repo_ttl = 5;
244 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 244 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
245 ctx->cfg.cache_root_ttl = 5; 245 ctx->cfg.cache_root_ttl = 5;
246 ctx->cfg.cache_scanrc_ttl = 15; 246 ctx->cfg.cache_scanrc_ttl = 15;
247 ctx->cfg.cache_static_ttl = -1; 247 ctx->cfg.cache_static_ttl = -1;
248 ctx->cfg.css = "/cgit.css"; 248 ctx->cfg.css = "/cgit.css";
249 ctx->cfg.logo = "/cgit.png"; 249 ctx->cfg.logo = "/cgit.png";
250 ctx->cfg.local_time = 0; 250 ctx->cfg.local_time = 0;
251 ctx->cfg.max_repo_count = 50; 251 ctx->cfg.max_repo_count = 50;
252 ctx->cfg.max_commit_count = 50; 252 ctx->cfg.max_commit_count = 50;
253 ctx->cfg.max_lock_attempts = 5; 253 ctx->cfg.max_lock_attempts = 5;
254 ctx->cfg.max_msg_len = 80; 254 ctx->cfg.max_msg_len = 80;
255 ctx->cfg.max_repodesc_len = 80; 255 ctx->cfg.max_repodesc_len = 80;
256 ctx->cfg.max_stats = 0; 256 ctx->cfg.max_stats = 0;
257 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 257 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
258 ctx->cfg.renamelimit = -1; 258 ctx->cfg.renamelimit = -1;
259 ctx->cfg.robots = "index, nofollow"; 259 ctx->cfg.robots = "index, nofollow";
260 ctx->cfg.root_title = "Git repository browser"; 260 ctx->cfg.root_title = "Git repository browser";
261 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 261 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
262 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 262 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
263 ctx->cfg.summary_branches = 10; 263 ctx->cfg.summary_branches = 10;
264 ctx->cfg.summary_log = 10; 264 ctx->cfg.summary_log = 10;
265 ctx->cfg.summary_tags = 10; 265 ctx->cfg.summary_tags = 10;
266 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 266 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
267 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 267 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
268 ctx->env.https = xstrdupn(getenv("HTTPS")); 268 ctx->env.https = xstrdupn(getenv("HTTPS"));
269 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 269 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
270 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 270 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
271 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 271 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
272 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 272 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
273 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 273 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
274 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 274 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
275 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 275 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
276 ctx->page.mimetype = "text/html"; 276 ctx->page.mimetype = "text/html";
277 ctx->page.charset = PAGE_ENCODING; 277 ctx->page.charset = PAGE_ENCODING;
278 ctx->page.filename = NULL; 278 ctx->page.filename = NULL;
279 ctx->page.size = 0; 279 ctx->page.size = 0;
280 ctx->page.modified = time(NULL); 280 ctx->page.modified = time(NULL);
281 ctx->page.expires = ctx->page.modified; 281 ctx->page.expires = ctx->page.modified;
282 ctx->page.etag = NULL; 282 ctx->page.etag = NULL;
283 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 283 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
284 if (ctx->env.script_name) 284 if (ctx->env.script_name)
285 ctx->cfg.script_name = ctx->env.script_name; 285 ctx->cfg.script_name = ctx->env.script_name;
286 if (ctx->env.query_string) 286 if (ctx->env.query_string)
287 ctx->qry.raw = ctx->env.query_string; 287 ctx->qry.raw = ctx->env.query_string;
288 if (!ctx->env.cgit_config) 288 if (!ctx->env.cgit_config)
289 ctx->env.cgit_config = CGIT_CONFIG; 289 ctx->env.cgit_config = CGIT_CONFIG;
290} 290}
291 291
292struct refmatch { 292struct refmatch {
293 char *req_ref; 293 char *req_ref;
294 char *first_ref; 294 char *first_ref;
295 int match; 295 int match;
296}; 296};
297 297
298int find_current_ref(const char *refname, const unsigned char *sha1, 298int find_current_ref(const char *refname, const unsigned char *sha1,
299 int flags, void *cb_data) 299 int flags, void *cb_data)
300{ 300{
301 struct refmatch *info; 301 struct refmatch *info;
302 302
303 info = (struct refmatch *)cb_data; 303 info = (struct refmatch *)cb_data;
304 if (!strcmp(refname, info->req_ref)) 304 if (!strcmp(refname, info->req_ref))
305 info->match = 1; 305 info->match = 1;
306 if (!info->first_ref) 306 if (!info->first_ref)
307 info->first_ref = xstrdup(refname); 307 info->first_ref = xstrdup(refname);
308 return info->match; 308 return info->match;
309} 309}
310 310
311char *find_default_branch(struct cgit_repo *repo) 311char *find_default_branch(struct cgit_repo *repo)
312{ 312{
313 struct refmatch info; 313 struct refmatch info;
314 char *ref; 314 char *ref;
315 315
316 info.req_ref = repo->defbranch; 316 info.req_ref = repo->defbranch;
317 info.first_ref = NULL; 317 info.first_ref = NULL;
318 info.match = 0; 318 info.match = 0;
319 for_each_branch_ref(find_current_ref, &info); 319 for_each_branch_ref(find_current_ref, &info);
320 if (info.match) 320 if (info.match)
321 ref = info.req_ref; 321 ref = info.req_ref;
322 else 322 else
323 ref = info.first_ref; 323 ref = info.first_ref;
324 if (ref) 324 if (ref)
325 ref = xstrdup(ref); 325 ref = xstrdup(ref);
326 return ref; 326 return ref;
327} 327}
328 328
329static int prepare_repo_cmd(struct cgit_context *ctx) 329static int prepare_repo_cmd(struct cgit_context *ctx)
330{ 330{
331 char *tmp; 331 char *tmp;
332 unsigned char sha1[20]; 332 unsigned char sha1[20];
333 int nongit = 0; 333 int nongit = 0;
334 334
335 setenv("GIT_DIR", ctx->repo->path, 1); 335 setenv("GIT_DIR", ctx->repo->path, 1);
336 setup_git_directory_gently(&nongit); 336 setup_git_directory_gently(&nongit);
337 if (nongit) { 337 if (nongit) {
338 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 338 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
339 "config error"); 339 "config error");
340 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 340 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
341 ctx->repo = NULL; 341 ctx->repo = NULL;
342 cgit_print_http_headers(ctx); 342 cgit_print_http_headers(ctx);
343 cgit_print_docstart(ctx); 343 cgit_print_docstart(ctx);
344 cgit_print_pageheader(ctx); 344 cgit_print_pageheader(ctx);
345 cgit_print_error(tmp); 345 cgit_print_error(tmp);
346 cgit_print_docend(); 346 cgit_print_docend();
347 return 1; 347 return 1;
348 } 348 }
349 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 349 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
350 350
351 if (!ctx->qry.head) { 351 if (!ctx->qry.head) {
352 ctx->qry.nohead = 1; 352 ctx->qry.nohead = 1;
353 ctx->qry.head = find_default_branch(ctx->repo); 353 ctx->qry.head = find_default_branch(ctx->repo);
354 ctx->repo->defbranch = ctx->qry.head; 354 ctx->repo->defbranch = ctx->qry.head;
355 } 355 }
356 356
357 if (!ctx->qry.head) { 357 if (!ctx->qry.head) {
358 cgit_print_http_headers(ctx); 358 cgit_print_http_headers(ctx);
359 cgit_print_docstart(ctx); 359 cgit_print_docstart(ctx);
360 cgit_print_pageheader(ctx); 360 cgit_print_pageheader(ctx);
361 cgit_print_error("Repository seems to be empty"); 361 cgit_print_error("Repository seems to be empty");
362 cgit_print_docend(); 362 cgit_print_docend();
363 return 1; 363 return 1;
364 } 364 }
365 365
366 if (get_sha1(ctx->qry.head, sha1)) { 366 if (get_sha1(ctx->qry.head, sha1)) {
367 tmp = xstrdup(ctx->qry.head); 367 tmp = xstrdup(ctx->qry.head);
368 ctx->qry.head = ctx->repo->defbranch; 368 ctx->qry.head = ctx->repo->defbranch;
369 ctx->page.status = 404; 369 ctx->page.status = 404;
370 ctx->page.statusmsg = "not found"; 370 ctx->page.statusmsg = "not found";
371 cgit_print_http_headers(ctx); 371 cgit_print_http_headers(ctx);
372 cgit_print_docstart(ctx); 372 cgit_print_docstart(ctx);
373 cgit_print_pageheader(ctx); 373 cgit_print_pageheader(ctx);
374 cgit_print_error(fmt("Invalid branch: %s", tmp)); 374 cgit_print_error(fmt("Invalid branch: %s", tmp));
375 cgit_print_docend(); 375 cgit_print_docend();
376 return 1; 376 return 1;
377 } 377 }
378 return 0; 378 return 0;
379} 379}
380 380
381static void process_request(void *cbdata) 381static void process_request(void *cbdata)
382{ 382{
383 struct cgit_context *ctx = cbdata; 383 struct cgit_context *ctx = cbdata;
384 struct cgit_cmd *cmd; 384 struct cgit_cmd *cmd;
385 385
386 cmd = cgit_get_cmd(ctx); 386 cmd = cgit_get_cmd(ctx);
387 if (!cmd) { 387 if (!cmd) {
388 ctx->page.title = "cgit error"; 388 ctx->page.title = "cgit error";
389 cgit_print_http_headers(ctx); 389 cgit_print_http_headers(ctx);
390 cgit_print_docstart(ctx); 390 cgit_print_docstart(ctx);
391 cgit_print_pageheader(ctx); 391 cgit_print_pageheader(ctx);
392 cgit_print_error("Invalid request"); 392 cgit_print_error("Invalid request");
393 cgit_print_docend(); 393 cgit_print_docend();
394 return; 394 return;
395 } 395 }
396 396
397 if (cmd->want_repo && !ctx->repo) { 397 if (cmd->want_repo && !ctx->repo) {
398 cgit_print_http_headers(ctx); 398 cgit_print_http_headers(ctx);
399 cgit_print_docstart(ctx); 399 cgit_print_docstart(ctx);
400 cgit_print_pageheader(ctx); 400 cgit_print_pageheader(ctx);
401 cgit_print_error(fmt("No repository selected")); 401 cgit_print_error(fmt("No repository selected"));
402 cgit_print_docend(); 402 cgit_print_docend();
403 return; 403 return;
404 } 404 }
405 405
406 if (ctx->repo && prepare_repo_cmd(ctx)) 406 if (ctx->repo && prepare_repo_cmd(ctx))
407 return; 407 return;
408 408
409 if (cmd->want_layout) { 409 if (cmd->want_layout) {
410 cgit_print_http_headers(ctx); 410 cgit_print_http_headers(ctx);
411 cgit_print_docstart(ctx); 411 cgit_print_docstart(ctx);
412 cgit_print_pageheader(ctx); 412 cgit_print_pageheader(ctx);
413 } 413 }
414 414
415 cmd->fn(ctx); 415 cmd->fn(ctx);
416 416
417 if (cmd->want_layout) 417 if (cmd->want_layout)
418 cgit_print_docend(); 418 cgit_print_docend();
419} 419}
420 420
421int cmp_repos(const void *a, const void *b) 421int cmp_repos(const void *a, const void *b)
422{ 422{
423 const struct cgit_repo *ra = a, *rb = b; 423 const struct cgit_repo *ra = a, *rb = b;
424 return strcmp(ra->url, rb->url); 424 return strcmp(ra->url, rb->url);
425} 425}
426 426
427void print_repo(FILE *f, struct cgit_repo *repo) 427void print_repo(FILE *f, struct cgit_repo *repo)
428{ 428{
429 fprintf(f, "repo.url=%s\n", repo->url); 429 fprintf(f, "repo.url=%s\n", repo->url);
430 fprintf(f, "repo.name=%s\n", repo->name); 430 fprintf(f, "repo.name=%s\n", repo->name);
431 fprintf(f, "repo.path=%s\n", repo->path); 431 fprintf(f, "repo.path=%s\n", repo->path);
432 if (repo->owner) 432 if (repo->owner)
433 fprintf(f, "repo.owner=%s\n", repo->owner); 433 fprintf(f, "repo.owner=%s\n", repo->owner);
434 if (repo->desc) 434 if (repo->desc)
435 fprintf(f, "repo.desc=%s\n", repo->desc); 435 fprintf(f, "repo.desc=%s\n", repo->desc);
436 if (repo->readme) 436 if (repo->readme)
437 fprintf(f, "repo.readme=%s\n", repo->readme); 437 fprintf(f, "repo.readme=%s\n", repo->readme);
438 fprintf(f, "\n"); 438 fprintf(f, "\n");
439} 439}
440 440
441void print_repolist(FILE *f, struct cgit_repolist *list, int start) 441void print_repolist(FILE *f, struct cgit_repolist *list, int start)
442{ 442{
443 int i; 443 int i;
444 444
445 for(i = start; i < list->count; i++) 445 for(i = start; i < list->count; i++)
446 print_repo(f, &list->repos[i]); 446 print_repo(f, &list->repos[i]);
447} 447}
448 448
449/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc' 449/* Scan 'path' for git repositories, save the resulting repolist in 'cached_rc'
450 * and return 0 on success. 450 * and return 0 on success.
451 */ 451 */
452static int generate_cached_repolist(const char *path, const char *cached_rc) 452static int generate_cached_repolist(const char *path, const char *cached_rc)
453{ 453{
454 char *locked_rc; 454 char *locked_rc;
455 int idx; 455 int idx;
456 FILE *f; 456 FILE *f;
457 457
458 locked_rc = xstrdup(fmt("%s.lock", cached_rc)); 458 locked_rc = xstrdup(fmt("%s.lock", cached_rc));
459 f = fopen(locked_rc, "wx"); 459 f = fopen(locked_rc, "wx");
460 if (!f) { 460 if (!f) {
461 /* Inform about the error unless the lockfile already existed, 461 /* Inform about the error unless the lockfile already existed,
462 * since that only means we've got concurrent requests. 462 * since that only means we've got concurrent requests.
463 */ 463 */
464 if (errno != EEXIST) 464 if (errno != EEXIST)
465 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n", 465 fprintf(stderr, "[cgit] Error opening %s: %s (%d)\n",
466 locked_rc, strerror(errno), errno); 466 locked_rc, strerror(errno), errno);
467 return errno; 467 return errno;
468 } 468 }
469 idx = cgit_repolist.count; 469 idx = cgit_repolist.count;
470 scan_tree(path); 470 scan_tree(path);
471 print_repolist(f, &cgit_repolist, idx); 471 print_repolist(f, &cgit_repolist, idx);
472 if (rename(locked_rc, cached_rc)) 472 if (rename(locked_rc, cached_rc))
473 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n", 473 fprintf(stderr, "[cgit] Error renaming %s to %s: %s (%d)\n",
474 locked_rc, cached_rc, strerror(errno), errno); 474 locked_rc, cached_rc, strerror(errno), errno);
475 fclose(f); 475 fclose(f);
476 return 0; 476 return 0;
477} 477}
478 478
479static void process_cached_repolist(const char *path) 479static void process_cached_repolist(const char *path)
480{ 480{
481 struct stat st; 481 struct stat st;
482 char *cached_rc; 482 char *cached_rc;
483 time_t age; 483 time_t age;
484 484
485 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 485 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root,
486 hash_str(path))); 486 hash_str(path)));
487 487
488 if (stat(cached_rc, &st)) { 488 if (stat(cached_rc, &st)) {
489 /* Nothing is cached, we need to scan without forking. And 489 /* Nothing is cached, we need to scan without forking. And
490 * if we fail to generate a cached repolist, we need to 490 * if we fail to generate a cached repolist, we need to
491 * invoke scan_tree manually. 491 * invoke scan_tree manually.
492 */ 492 */
493 if (generate_cached_repolist(path, cached_rc)) 493 if (generate_cached_repolist(path, cached_rc))
494 scan_tree(path); 494 scan_tree(path);
495 return; 495 return;
496 } 496 }
497 497
498 parse_configfile(cached_rc, config_cb); 498 parse_configfile(cached_rc, config_cb);
499 499
500 /* If the cached configfile hasn't expired, lets exit now */ 500 /* If the cached configfile hasn't expired, lets exit now */
501 age = time(NULL) - st.st_mtime; 501 age = time(NULL) - st.st_mtime;
502 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 502 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
503 return; 503 return;
504 504
505 /* The cached repolist has been parsed, but it was old. So lets 505 /* The cached repolist has been parsed, but it was old. So lets
506 * rescan the specified path and generate a new cached repolist 506 * rescan the specified path and generate a new cached repolist
507 * in a child-process to avoid latency for the current request. 507 * in a child-process to avoid latency for the current request.
508 */ 508 */
509 if (fork()) 509 if (fork())
510 return; 510 return;
511 511
512 exit(generate_cached_repolist(path, cached_rc)); 512 exit(generate_cached_repolist(path, cached_rc));
513} 513}
514 514
515static void cgit_parse_args(int argc, const char **argv) 515static void cgit_parse_args(int argc, const char **argv)
516{ 516{
517 int i; 517 int i;
518 int scan = 0; 518 int scan = 0;
519 519
520 for (i = 1; i < argc; i++) { 520 for (i = 1; i < argc; i++) {
521 if (!strncmp(argv[i], "--cache=", 8)) { 521 if (!strncmp(argv[i], "--cache=", 8)) {
522 ctx.cfg.cache_root = xstrdup(argv[i]+8); 522 ctx.cfg.cache_root = xstrdup(argv[i]+8);
523 } 523 }
524 if (!strcmp(argv[i], "--nocache")) { 524 if (!strcmp(argv[i], "--nocache")) {
525 ctx.cfg.nocache = 1; 525 ctx.cfg.nocache = 1;
526 } 526 }
527 if (!strcmp(argv[i], "--nohttp")) { 527 if (!strcmp(argv[i], "--nohttp")) {
528 ctx.env.no_http = "1"; 528 ctx.env.no_http = "1";
529 } 529 }
530 if (!strncmp(argv[i], "--query=", 8)) { 530 if (!strncmp(argv[i], "--query=", 8)) {
531 ctx.qry.raw = xstrdup(argv[i]+8); 531 ctx.qry.raw = xstrdup(argv[i]+8);
diff --git a/cgit.css b/cgit.css
index e3b32e7..3c65114 100644
--- a/cgit.css
+++ b/cgit.css
@@ -48,556 +48,556 @@ table#header td.form {
48 vertical-align: bottom; 48 vertical-align: bottom;
49 padding-right: 1em; 49 padding-right: 1em;
50 padding-bottom: 2px; 50 padding-bottom: 2px;
51 white-space: nowrap; 51 white-space: nowrap;
52} 52}
53 53
54table#header td.form form, 54table#header td.form form,
55table#header td.form input, 55table#header td.form input,
56table#header td.form select { 56table#header td.form select {
57 font-size: 90%; 57 font-size: 90%;
58} 58}
59 59
60table#header td.sub { 60table#header td.sub {
61 color: #777; 61 color: #777;
62 border-top: solid 1px #ccc; 62 border-top: solid 1px #ccc;
63 padding-left: 10px; 63 padding-left: 10px;
64} 64}
65 65
66table.tabs { 66table.tabs {
67 /* border-bottom: solid 2px #ccc; */ 67 /* border-bottom: solid 2px #ccc; */
68 border-collapse: collapse; 68 border-collapse: collapse;
69 margin-top: 2em; 69 margin-top: 2em;
70 margin-bottom: 0px; 70 margin-bottom: 0px;
71 width: 100%; 71 width: 100%;
72} 72}
73 73
74table.tabs td { 74table.tabs td {
75 padding: 0px 1em; 75 padding: 0px 1em;
76 vertical-align: bottom; 76 vertical-align: bottom;
77} 77}
78 78
79table.tabs td a { 79table.tabs td a {
80 padding: 2px 0.75em; 80 padding: 2px 0.75em;
81 color: #777; 81 color: #777;
82 font-size: 110%; 82 font-size: 110%;
83} 83}
84 84
85table.tabs td a.active { 85table.tabs td a.active {
86 color: #000; 86 color: #000;
87 background-color: #ccc; 87 background-color: #ccc;
88} 88}
89 89
90table.tabs td.form { 90table.tabs td.form {
91 text-align: right; 91 text-align: right;
92} 92}
93 93
94table.tabs td.form form { 94table.tabs td.form form {
95 padding-bottom: 2px; 95 padding-bottom: 2px;
96 font-size: 90%; 96 font-size: 90%;
97 white-space: nowrap; 97 white-space: nowrap;
98} 98}
99 99
100table.tabs td.form input, 100table.tabs td.form input,
101table.tabs td.form select { 101table.tabs td.form select {
102 font-size: 90%; 102 font-size: 90%;
103} 103}
104 104
105div.content { 105div.content {
106 margin: 0px; 106 margin: 0px;
107 padding: 2em; 107 padding: 2em;
108 border-top: solid 3px #ccc; 108 border-top: solid 3px #ccc;
109 border-bottom: solid 3px #ccc; 109 border-bottom: solid 3px #ccc;
110} 110}
111 111
112 112
113table.list { 113table.list {
114 width: 100%; 114 width: 100%;
115 border: none; 115 border: none;
116 border-collapse: collapse; 116 border-collapse: collapse;
117} 117}
118 118
119table.list tr { 119table.list tr {
120 background: white; 120 background: white;
121} 121}
122 122
123table.list tr.logheader { 123table.list tr.logheader {
124 background: #eee; 124 background: #eee;
125} 125}
126 126
127table.list tr:hover { 127table.list tr:hover {
128 background: #eee; 128 background: #eee;
129} 129}
130 130
131table.list tr.nohover:hover { 131table.list tr.nohover:hover {
132 background: white; 132 background: white;
133} 133}
134 134
135table.list th { 135table.list th {
136 font-weight: bold; 136 font-weight: bold;
137 /* color: #888; 137 /* color: #888;
138 border-top: dashed 1px #888; 138 border-top: dashed 1px #888;
139 border-bottom: dashed 1px #888; 139 border-bottom: dashed 1px #888;
140 */ 140 */
141 padding: 0.1em 0.5em 0.05em 0.5em; 141 padding: 0.1em 0.5em 0.05em 0.5em;
142 vertical-align: baseline; 142 vertical-align: baseline;
143} 143}
144 144
145table.list td { 145table.list td {
146 border: none; 146 border: none;
147 padding: 0.1em 0.5em 0.1em 0.5em; 147 padding: 0.1em 0.5em 0.1em 0.5em;
148} 148}
149 149
150table.list td.logsubject { 150table.list td.logsubject {
151 font-family: monospace; 151 font-family: monospace;
152 font-weight: bold; 152 font-weight: bold;
153} 153}
154 154
155table.list td.logmsg { 155table.list td.logmsg {
156 font-family: monospace; 156 font-family: monospace;
157 white-space: pre; 157 white-space: pre;
158 padding: 1em 0.5em 2em 0.5em; 158 padding: 1em 0.5em 2em 0.5em;
159} 159}
160 160
161table.list td a { 161table.list td a {
162 color: black; 162 color: black;
163} 163}
164 164
165table.list td a:hover { 165table.list td a:hover {
166 color: #00f; 166 color: #00f;
167} 167}
168 168
169img { 169img {
170 border: none; 170 border: none;
171} 171}
172 172
173input#switch-btn { 173input#switch-btn {
174 margin: 2px 0px 0px 0px; 174 margin: 2px 0px 0px 0px;
175} 175}
176 176
177td#sidebar input.txt { 177td#sidebar input.txt {
178 width: 100%; 178 width: 100%;
179 margin: 2px 0px 0px 0px; 179 margin: 2px 0px 0px 0px;
180} 180}
181 181
182table#grid { 182table#grid {
183 margin: 0px; 183 margin: 0px;
184} 184}
185 185
186td#content { 186td#content {
187 vertical-align: top; 187 vertical-align: top;
188 padding: 1em 2em 1em 1em; 188 padding: 1em 2em 1em 1em;
189 border: none; 189 border: none;
190} 190}
191 191
192div#summary { 192div#summary {
193 vertical-align: top; 193 vertical-align: top;
194 margin-bottom: 1em; 194 margin-bottom: 1em;
195} 195}
196 196
197table#downloads { 197table#downloads {
198 float: right; 198 float: right;
199 border-collapse: collapse; 199 border-collapse: collapse;
200 border: solid 1px #777; 200 border: solid 1px #777;
201 margin-left: 0.5em; 201 margin-left: 0.5em;
202 margin-bottom: 0.5em; 202 margin-bottom: 0.5em;
203} 203}
204 204
205table#downloads th { 205table#downloads th {
206 background-color: #ccc; 206 background-color: #ccc;
207} 207}
208 208
209div#blob { 209div#blob {
210 border: solid 1px black; 210 border: solid 1px black;
211} 211}
212 212
213div.error { 213div.error {
214 color: red; 214 color: red;
215 font-weight: bold; 215 font-weight: bold;
216 margin: 1em 2em; 216 margin: 1em 2em;
217} 217}
218 218
219a.ls-blob, a.ls-dir, a.ls-mod { 219a.ls-blob, a.ls-dir, a.ls-mod {
220 font-family: monospace; 220 font-family: monospace;
221} 221}
222 222
223td.ls-size { 223td.ls-size {
224 text-align: right; 224 text-align: right;
225 font-family: monospace; 225 font-family: monospace;
226 width: 10em; 226 width: 10em;
227} 227}
228 228
229td.ls-mode { 229td.ls-mode {
230 font-family: monospace; 230 font-family: monospace;
231 width: 10em; 231 width: 10em;
232} 232}
233 233
234table.blob { 234table.blob {
235 margin-top: 0.5em; 235 margin-top: 0.5em;
236 border-top: solid 1px black; 236 border-top: solid 1px black;
237} 237}
238 238
239table.blob td.lines { 239table.blob td.lines {
240 margin: 0; padding: 0; 240 margin: 0; padding: 0;
241 vertical-align: top; 241 vertical-align: top;
242 color: black; 242 color: black;
243} 243}
244 244
245table.blob td.linenumbers { 245table.blob td.linenumbers {
246 margin: 0; padding: 0; 246 margin: 0; padding: 0;
247 vertical-align: top; 247 vertical-align: top;
248 border-right: 1px solid gray; 248 border-right: 1px solid gray;
249 background-color: #eee; 249 background-color: #eee;
250} 250}
251 251
252table.blob pre { 252table.blob pre {
253 padding: 0; margin: 0; 253 padding: 0; margin: 0;
254} 254}
255 255
256table.blob a.no { 256table.blob a.no {
257 color: gray; 257 color: gray;
258 text-align: right; 258 text-align: right;
259 text-decoration: none; 259 text-decoration: none;
260} 260}
261 261
262table.blob a.no a:hover { 262table.blob a.no a:hover {
263 color: black; 263 color: black;
264} 264}
265 265
266table.bin-blob { 266table.bin-blob {
267 margin-top: 0.5em; 267 margin-top: 0.5em;
268 border: solid 1px black; 268 border: solid 1px black;
269} 269}
270 270
271table.bin-blob th { 271table.bin-blob th {
272 font-family: monospace; 272 font-family: monospace;
273 white-space: pre; 273 white-space: pre;
274 border: solid 1px #777; 274 border: solid 1px #777;
275 padding: 0.5em 1em; 275 padding: 0.5em 1em;
276} 276}
277 277
278table.bin-blob td { 278table.bin-blob td {
279 font-family: monospace; 279 font-family: monospace;
280 white-space: pre; 280 white-space: pre;
281 border-left: solid 1px #777; 281 border-left: solid 1px #777;
282 padding: 0em 1em; 282 padding: 0em 1em;
283} 283}
284 284
285table.nowrap td { 285table.nowrap td {
286 white-space: nowrap; 286 white-space: nowrap;
287} 287}
288 288
289table.commit-info { 289table.commit-info {
290 border-collapse: collapse; 290 border-collapse: collapse;
291 margin-top: 1.5em; 291 margin-top: 1.5em;
292} 292}
293 293
294table.commit-info th { 294table.commit-info th {
295 text-align: left; 295 text-align: left;
296 font-weight: normal; 296 font-weight: normal;
297 padding: 0.1em 1em 0.1em 0.1em; 297 padding: 0.1em 1em 0.1em 0.1em;
298 vertical-align: top; 298 vertical-align: top;
299} 299}
300 300
301table.commit-info td { 301table.commit-info td {
302 font-weight: normal; 302 font-weight: normal;
303 padding: 0.1em 1em 0.1em 0.1em; 303 padding: 0.1em 1em 0.1em 0.1em;
304} 304}
305 305
306div.commit-subject { 306div.commit-subject {
307 font-weight: bold; 307 font-weight: bold;
308 font-size: 125%; 308 font-size: 125%;
309 margin: 1.5em 0em 0.5em 0em; 309 margin: 1.5em 0em 0.5em 0em;
310 padding: 0em; 310 padding: 0em;
311} 311}
312 312
313div.commit-msg { 313div.commit-msg {
314 white-space: pre; 314 white-space: pre;
315 font-family: monospace; 315 font-family: monospace;
316} 316}
317 317
318div.diffstat-header { 318div.diffstat-header {
319 font-weight: bold; 319 font-weight: bold;
320 padding-top: 1.5em; 320 padding-top: 1.5em;
321} 321}
322 322
323table.diffstat { 323table.diffstat {
324 border-collapse: collapse; 324 border-collapse: collapse;
325 border: solid 1px #aaa; 325 border: solid 1px #aaa;
326 background-color: #eee; 326 background-color: #eee;
327} 327}
328 328
329table.diffstat th { 329table.diffstat th {
330 font-weight: normal; 330 font-weight: normal;
331 text-align: left; 331 text-align: left;
332 text-decoration: underline; 332 text-decoration: underline;
333 padding: 0.1em 1em 0.1em 0.1em; 333 padding: 0.1em 1em 0.1em 0.1em;
334 font-size: 100%; 334 font-size: 100%;
335} 335}
336 336
337table.diffstat td { 337table.diffstat td {
338 padding: 0.2em 0.2em 0.1em 0.1em; 338 padding: 0.2em 0.2em 0.1em 0.1em;
339 font-size: 100%; 339 font-size: 100%;
340 border: none; 340 border: none;
341} 341}
342 342
343table.diffstat td.mode { 343table.diffstat td.mode {
344 white-space: nowrap; 344 white-space: nowrap;
345} 345}
346 346
347table.diffstat td span.modechange { 347table.diffstat td span.modechange {
348 padding-left: 1em; 348 padding-left: 1em;
349 color: red; 349 color: red;
350} 350}
351 351
352table.diffstat td.add a { 352table.diffstat td.add a {
353 color: green; 353 color: green;
354} 354}
355 355
356table.diffstat td.del a { 356table.diffstat td.del a {
357 color: red; 357 color: red;
358} 358}
359 359
360table.diffstat td.upd a { 360table.diffstat td.upd a {
361 color: blue; 361 color: blue;
362} 362}
363 363
364table.diffstat td.graph { 364table.diffstat td.graph {
365 width: 500px; 365 width: 500px;
366 vertical-align: middle; 366 vertical-align: middle;
367} 367}
368 368
369table.diffstat td.graph table { 369table.diffstat td.graph table {
370 border: none; 370 border: none;
371} 371}
372 372
373table.diffstat td.graph td { 373table.diffstat td.graph td {
374 padding: 0px; 374 padding: 0px;
375 border: 0px; 375 border: 0px;
376 height: 7pt; 376 height: 7pt;
377} 377}
378 378
379table.diffstat td.graph td.add { 379table.diffstat td.graph td.add {
380 background-color: #5c5; 380 background-color: #5c5;
381} 381}
382 382
383table.diffstat td.graph td.rem { 383table.diffstat td.graph td.rem {
384 background-color: #c55; 384 background-color: #c55;
385} 385}
386 386
387div.diffstat-summary { 387div.diffstat-summary {
388 color: #888; 388 color: #888;
389 padding-top: 0.5em; 389 padding-top: 0.5em;
390} 390}
391 391
392table.diff { 392table.diff {
393 width: 100%; 393 width: 100%;
394} 394}
395 395
396table.diff td { 396table.diff td {
397 font-family: monospace; 397 font-family: monospace;
398 white-space: pre; 398 white-space: pre;
399} 399}
400 400
401table.diff td div.head { 401table.diff td div.head {
402 font-weight: bold; 402 font-weight: bold;
403 margin-top: 1em; 403 margin-top: 1em;
404 color: black; 404 color: black;
405} 405}
406 406
407table.diff td div.hunk { 407table.diff td div.hunk {
408 color: #009; 408 color: #009;
409} 409}
410 410
411table.diff td div.add { 411table.diff td div.add {
412 color: green; 412 color: green;
413} 413}
414 414
415table.diff td div.del { 415table.diff td div.del {
416 color: red; 416 color: red;
417} 417}
418 418
419.sha1 { 419.sha1 {
420 font-family: monospace; 420 font-family: monospace;
421 font-size: 90%; 421 font-size: 90%;
422} 422}
423 423
424.left { 424.left {
425 text-align: left; 425 text-align: left;
426} 426}
427 427
428.right { 428.right {
429 text-align: right; 429 text-align: right;
430} 430}
431 431
432table.list td.repogroup { 432table.list td.reposection {
433 font-style: italic; 433 font-style: italic;
434 color: #888; 434 color: #888;
435} 435}
436 436
437a.button { 437a.button {
438 font-size: 80%; 438 font-size: 80%;
439 padding: 0em 0.5em; 439 padding: 0em 0.5em;
440} 440}
441 441
442a.primary { 442a.primary {
443 font-size: 100%; 443 font-size: 100%;
444} 444}
445 445
446a.secondary { 446a.secondary {
447 font-size: 90%; 447 font-size: 90%;
448} 448}
449 449
450td.toplevel-repo { 450td.toplevel-repo {
451 451
452} 452}
453 453
454table.list td.sublevel-repo { 454table.list td.sublevel-repo {
455 padding-left: 1.5em; 455 padding-left: 1.5em;
456} 456}
457 457
458div.pager { 458div.pager {
459 text-align: center; 459 text-align: center;
460 margin: 1em 0em 0em 0em; 460 margin: 1em 0em 0em 0em;
461} 461}
462 462
463div.pager a { 463div.pager a {
464 color: #777; 464 color: #777;
465 margin: 0em 0.5em; 465 margin: 0em 0.5em;
466} 466}
467 467
468span.age-mins { 468span.age-mins {
469 font-weight: bold; 469 font-weight: bold;
470 color: #080; 470 color: #080;
471} 471}
472 472
473span.age-hours { 473span.age-hours {
474 color: #080; 474 color: #080;
475} 475}
476 476
477span.age-days { 477span.age-days {
478 color: #040; 478 color: #040;
479} 479}
480 480
481span.age-weeks { 481span.age-weeks {
482 color: #444; 482 color: #444;
483} 483}
484 484
485span.age-months { 485span.age-months {
486 color: #888; 486 color: #888;
487} 487}
488 488
489span.age-years { 489span.age-years {
490 color: #bbb; 490 color: #bbb;
491} 491}
492div.footer { 492div.footer {
493 margin-top: 0.5em; 493 margin-top: 0.5em;
494 text-align: center; 494 text-align: center;
495 font-size: 80%; 495 font-size: 80%;
496 color: #ccc; 496 color: #ccc;
497} 497}
498a.branch-deco { 498a.branch-deco {
499 margin: 0px 0.5em; 499 margin: 0px 0.5em;
500 padding: 0px 0.25em; 500 padding: 0px 0.25em;
501 background-color: #88ff88; 501 background-color: #88ff88;
502 border: solid 1px #007700; 502 border: solid 1px #007700;
503} 503}
504a.tag-deco { 504a.tag-deco {
505 margin: 0px 0.5em; 505 margin: 0px 0.5em;
506 padding: 0px 0.25em; 506 padding: 0px 0.25em;
507 background-color: #ffff88; 507 background-color: #ffff88;
508 border: solid 1px #777700; 508 border: solid 1px #777700;
509} 509}
510a.remote-deco { 510a.remote-deco {
511 margin: 0px 0.5em; 511 margin: 0px 0.5em;
512 padding: 0px 0.25em; 512 padding: 0px 0.25em;
513 background-color: #ccccff; 513 background-color: #ccccff;
514 border: solid 1px #000077; 514 border: solid 1px #000077;
515} 515}
516a.deco { 516a.deco {
517 margin: 0px 0.5em; 517 margin: 0px 0.5em;
518 padding: 0px 0.25em; 518 padding: 0px 0.25em;
519 background-color: #ff8888; 519 background-color: #ff8888;
520 border: solid 1px #770000; 520 border: solid 1px #770000;
521} 521}
522 522
523div.commit-subject a { 523div.commit-subject a {
524 margin-left: 1em; 524 margin-left: 1em;
525 font-size: 75%; 525 font-size: 75%;
526} 526}
527 527
528table.stats { 528table.stats {
529 border: solid 1px black; 529 border: solid 1px black;
530 border-collapse: collapse; 530 border-collapse: collapse;
531} 531}
532 532
533table.stats th { 533table.stats th {
534 text-align: left; 534 text-align: left;
535 padding: 1px 0.5em; 535 padding: 1px 0.5em;
536 background-color: #eee; 536 background-color: #eee;
537 border: solid 1px black; 537 border: solid 1px black;
538} 538}
539 539
540table.stats td { 540table.stats td {
541 text-align: right; 541 text-align: right;
542 padding: 1px 0.5em; 542 padding: 1px 0.5em;
543 border: solid 1px black; 543 border: solid 1px black;
544} 544}
545 545
546table.stats td.total { 546table.stats td.total {
547 font-weight: bold; 547 font-weight: bold;
548 text-align: left; 548 text-align: left;
549} 549}
550 550
551table.stats td.sum { 551table.stats td.sum {
552 color: #c00; 552 color: #c00;
553 font-weight: bold; 553 font-weight: bold;
554 /*background-color: #eee; */ 554 /*background-color: #eee; */
555} 555}
556 556
557table.stats td.left { 557table.stats td.left {
558 text-align: left; 558 text-align: left;
559} 559}
560 560
561table.vgraph { 561table.vgraph {
562 border-collapse: separate; 562 border-collapse: separate;
563 border: solid 1px black; 563 border: solid 1px black;
564 height: 200px; 564 height: 200px;
565} 565}
566 566
567table.vgraph th { 567table.vgraph th {
568 background-color: #eee; 568 background-color: #eee;
569 font-weight: bold; 569 font-weight: bold;
570 border: solid 1px white; 570 border: solid 1px white;
571 padding: 1px 0.5em; 571 padding: 1px 0.5em;
572} 572}
573 573
574table.vgraph td { 574table.vgraph td {
575 vertical-align: bottom; 575 vertical-align: bottom;
576 padding: 0px 10px; 576 padding: 0px 10px;
577} 577}
578 578
579table.vgraph div.bar { 579table.vgraph div.bar {
580 background-color: #eee; 580 background-color: #eee;
581} 581}
582 582
583table.hgraph { 583table.hgraph {
584 border: solid 1px black; 584 border: solid 1px black;
585 width: 800px; 585 width: 800px;
586} 586}
587 587
588table.hgraph th { 588table.hgraph th {
589 background-color: #eee; 589 background-color: #eee;
590 font-weight: bold; 590 font-weight: bold;
591 border: solid 1px black; 591 border: solid 1px black;
592 padding: 1px 0.5em; 592 padding: 1px 0.5em;
593} 593}
594 594
595table.hgraph td { 595table.hgraph td {
596 vertical-align: center; 596 vertical-align: center;
597 padding: 2px 2px; 597 padding: 2px 2px;
598} 598}
599 599
600table.hgraph div.bar { 600table.hgraph div.bar {
601 background-color: #eee; 601 background-color: #eee;
602 height: 1em; 602 height: 1em;
603} 603}
diff --git a/cgit.h b/cgit.h
index 5659580..fc7c7d5 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,289 +1,289 @@
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 *group;
69 char *module_link; 68 char *module_link;
70 char *readme; 69 char *readme;
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 max_stats; 75 int max_stats;
76 time_t mtime; 76 time_t mtime;
77 struct cgit_filter *about_filter; 77 struct cgit_filter *about_filter;
78 struct cgit_filter *commit_filter; 78 struct cgit_filter *commit_filter;
79 struct cgit_filter *source_filter; 79 struct cgit_filter *source_filter;
80}; 80};
81 81
82struct cgit_repolist { 82struct cgit_repolist {
83 int length; 83 int length;
84 int count; 84 int count;
85 struct cgit_repo *repos; 85 struct cgit_repo *repos;
86}; 86};
87 87
88struct commitinfo { 88struct commitinfo {
89 struct commit *commit; 89 struct commit *commit;
90 char *author; 90 char *author;
91 char *author_email; 91 char *author_email;
92 unsigned long author_date; 92 unsigned long author_date;
93 char *committer; 93 char *committer;
94 char *committer_email; 94 char *committer_email;
95 unsigned long committer_date; 95 unsigned long committer_date;
96 char *subject; 96 char *subject;
97 char *msg; 97 char *msg;
98 char *msg_encoding; 98 char *msg_encoding;
99}; 99};
100 100
101struct taginfo { 101struct taginfo {
102 char *tagger; 102 char *tagger;
103 char *tagger_email; 103 char *tagger_email;
104 unsigned long tagger_date; 104 unsigned long tagger_date;
105 char *msg; 105 char *msg;
106}; 106};
107 107
108struct refinfo { 108struct refinfo {
109 const char *refname; 109 const char *refname;
110 struct object *object; 110 struct object *object;
111 union { 111 union {
112 struct taginfo *tag; 112 struct taginfo *tag;
113 struct commitinfo *commit; 113 struct commitinfo *commit;
114 }; 114 };
115}; 115};
116 116
117struct reflist { 117struct reflist {
118 struct refinfo **refs; 118 struct refinfo **refs;
119 int alloc; 119 int alloc;
120 int count; 120 int count;
121}; 121};
122 122
123struct cgit_query { 123struct cgit_query {
124 int has_symref; 124 int has_symref;
125 int has_sha1; 125 int has_sha1;
126 char *raw; 126 char *raw;
127 char *repo; 127 char *repo;
128 char *page; 128 char *page;
129 char *search; 129 char *search;
130 char *grep; 130 char *grep;
131 char *head; 131 char *head;
132 char *sha1; 132 char *sha1;
133 char *sha2; 133 char *sha2;
134 char *path; 134 char *path;
135 char *name; 135 char *name;
136 char *mimetype; 136 char *mimetype;
137 char *url; 137 char *url;
138 char *period; 138 char *period;
139 int ofs; 139 int ofs;
140 int nohead; 140 int nohead;
141 char *sort; 141 char *sort;
142 int showmsg; 142 int showmsg;
143}; 143};
144 144
145struct cgit_config { 145struct cgit_config {
146 char *agefile; 146 char *agefile;
147 char *cache_root; 147 char *cache_root;
148 char *clone_prefix; 148 char *clone_prefix;
149 char *css; 149 char *css;
150 char *favicon; 150 char *favicon;
151 char *footer; 151 char *footer;
152 char *head_include; 152 char *head_include;
153 char *header; 153 char *header;
154 char *index_header; 154 char *index_header;
155 char *index_info; 155 char *index_info;
156 char *logo; 156 char *logo;
157 char *logo_link; 157 char *logo_link;
158 char *module_link; 158 char *module_link;
159 char *repo_group;
160 char *robots; 159 char *robots;
161 char *root_title; 160 char *root_title;
162 char *root_desc; 161 char *root_desc;
163 char *root_readme; 162 char *root_readme;
164 char *script_name; 163 char *script_name;
164 char *section;
165 char *virtual_root; 165 char *virtual_root;
166 int cache_size; 166 int cache_size;
167 int cache_dynamic_ttl; 167 int cache_dynamic_ttl;
168 int cache_max_create_time; 168 int cache_max_create_time;
169 int cache_repo_ttl; 169 int cache_repo_ttl;
170 int cache_root_ttl; 170 int cache_root_ttl;
171 int cache_scanrc_ttl; 171 int cache_scanrc_ttl;
172 int cache_static_ttl; 172 int cache_static_ttl;
173 int embedded; 173 int embedded;
174 int enable_index_links; 174 int enable_index_links;
175 int enable_log_filecount; 175 int enable_log_filecount;
176 int enable_log_linecount; 176 int enable_log_linecount;
177 int local_time; 177 int local_time;
178 int max_repo_count; 178 int max_repo_count;
179 int max_commit_count; 179 int max_commit_count;
180 int max_lock_attempts; 180 int max_lock_attempts;
181 int max_msg_len; 181 int max_msg_len;
182 int max_repodesc_len; 182 int max_repodesc_len;
183 int max_stats; 183 int max_stats;
184 int nocache; 184 int nocache;
185 int noplainemail; 185 int noplainemail;
186 int noheader; 186 int noheader;
187 int renamelimit; 187 int renamelimit;
188 int snapshots; 188 int snapshots;
189 int summary_branches; 189 int summary_branches;
190 int summary_log; 190 int summary_log;
191 int summary_tags; 191 int summary_tags;
192 struct string_list mimetypes; 192 struct string_list mimetypes;
193 struct cgit_filter *about_filter; 193 struct cgit_filter *about_filter;
194 struct cgit_filter *commit_filter; 194 struct cgit_filter *commit_filter;
195 struct cgit_filter *source_filter; 195 struct cgit_filter *source_filter;
196}; 196};
197 197
198struct cgit_page { 198struct cgit_page {
199 time_t modified; 199 time_t modified;
200 time_t expires; 200 time_t expires;
201 size_t size; 201 size_t size;
202 char *mimetype; 202 char *mimetype;
203 char *charset; 203 char *charset;
204 char *filename; 204 char *filename;
205 char *etag; 205 char *etag;
206 char *title; 206 char *title;
207 int status; 207 int status;
208 char *statusmsg; 208 char *statusmsg;
209}; 209};
210 210
211struct cgit_environment { 211struct cgit_environment {
212 char *cgit_config; 212 char *cgit_config;
213 char *http_host; 213 char *http_host;
214 char *https; 214 char *https;
215 char *no_http; 215 char *no_http;
216 char *path_info; 216 char *path_info;
217 char *query_string; 217 char *query_string;
218 char *request_method; 218 char *request_method;
219 char *script_name; 219 char *script_name;
220 char *server_name; 220 char *server_name;
221 char *server_port; 221 char *server_port;
222}; 222};
223 223
224struct cgit_context { 224struct cgit_context {
225 struct cgit_environment env; 225 struct cgit_environment env;
226 struct cgit_query qry; 226 struct cgit_query qry;
227 struct cgit_config cfg; 227 struct cgit_config cfg;
228 struct cgit_repo *repo; 228 struct cgit_repo *repo;
229 struct cgit_page page; 229 struct cgit_page page;
230}; 230};
231 231
232struct cgit_snapshot_format { 232struct cgit_snapshot_format {
233 const char *suffix; 233 const char *suffix;
234 const char *mimetype; 234 const char *mimetype;
235 write_archive_fn_t write_func; 235 write_archive_fn_t write_func;
236 int bit; 236 int bit;
237}; 237};
238 238
239extern const char *cgit_version; 239extern const char *cgit_version;
240 240
241extern struct cgit_repolist cgit_repolist; 241extern struct cgit_repolist cgit_repolist;
242extern struct cgit_context ctx; 242extern struct cgit_context ctx;
243extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 243extern const struct cgit_snapshot_format cgit_snapshot_formats[];
244 244
245extern struct cgit_repo *cgit_add_repo(const char *url); 245extern struct cgit_repo *cgit_add_repo(const char *url);
246extern struct cgit_repo *cgit_get_repoinfo(const char *url); 246extern struct cgit_repo *cgit_get_repoinfo(const char *url);
247extern void cgit_repo_config_cb(const char *name, const char *value); 247extern void cgit_repo_config_cb(const char *name, const char *value);
248 248
249extern int chk_zero(int result, char *msg); 249extern int chk_zero(int result, char *msg);
250extern int chk_positive(int result, char *msg); 250extern int chk_positive(int result, char *msg);
251extern int chk_non_negative(int result, char *msg); 251extern int chk_non_negative(int result, char *msg);
252 252
253extern char *trim_end(const char *str, char c); 253extern char *trim_end(const char *str, char c);
254extern char *strlpart(char *txt, int maxlen); 254extern char *strlpart(char *txt, int maxlen);
255extern char *strrpart(char *txt, int maxlen); 255extern char *strrpart(char *txt, int maxlen);
256 256
257extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 257extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
258extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 258extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
259 int flags, void *cb_data); 259 int flags, void *cb_data);
260 260
261extern void *cgit_free_commitinfo(struct commitinfo *info); 261extern void *cgit_free_commitinfo(struct commitinfo *info);
262 262
263extern int cgit_diff_files(const unsigned char *old_sha1, 263extern int cgit_diff_files(const unsigned char *old_sha1,
264 const unsigned char *new_sha1, 264 const unsigned char *new_sha1,
265 unsigned long *old_size, unsigned long *new_size, 265 unsigned long *old_size, unsigned long *new_size,
266 int *binary, linediff_fn fn); 266 int *binary, linediff_fn fn);
267 267
268extern void cgit_diff_tree(const unsigned char *old_sha1, 268extern void cgit_diff_tree(const unsigned char *old_sha1,
269 const unsigned char *new_sha1, 269 const unsigned char *new_sha1,
270 filepair_fn fn, const char *prefix); 270 filepair_fn fn, const char *prefix);
271 271
272extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 272extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
273 273
274extern char *fmt(const char *format,...); 274extern char *fmt(const char *format,...);
275 275
276extern struct commitinfo *cgit_parse_commit(struct commit *commit); 276extern struct commitinfo *cgit_parse_commit(struct commit *commit);
277extern struct taginfo *cgit_parse_tag(struct tag *tag); 277extern struct taginfo *cgit_parse_tag(struct tag *tag);
278extern void cgit_parse_url(const char *url); 278extern void cgit_parse_url(const char *url);
279 279
280extern const char *cgit_repobasename(const char *reponame); 280extern const char *cgit_repobasename(const char *reponame);
281 281
282extern int cgit_parse_snapshots_mask(const char *str); 282extern int cgit_parse_snapshots_mask(const char *str);
283 283
284extern int cgit_open_filter(struct cgit_filter *filter); 284extern int cgit_open_filter(struct cgit_filter *filter);
285extern int cgit_close_filter(struct cgit_filter *filter); 285extern int cgit_close_filter(struct cgit_filter *filter);
286 286
287extern int readfile(const char *path, char **buf, size_t *size); 287extern int readfile(const char *path, char **buf, size_t *size);
288 288
289#endif /* CGIT_H */ 289#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 2abbd41..4d009f9 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,465 +1,470 @@
1CGITRC(5) 1CGITRC(5)
2======== 2========
3 3
4 4
5NAME 5NAME
6---- 6----
7cgitrc - runtime configuration for cgit 7cgitrc - runtime configuration for cgit
8 8
9 9
10SYNOPSIS 10SYNOPSIS
11-------- 11--------
12Cgitrc contains all runtime settings for cgit, including the list of git 12Cgitrc contains all runtime settings for cgit, including the list of git
13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank 13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank
14lines, and lines starting with '#', are ignored. 14lines, and lines starting with '#', are ignored.
15 15
16 16
17LOCATION 17LOCATION
18-------- 18--------
19The default location of cgitrc, defined at compile time, is /etc/cgitrc. At 19The default location of cgitrc, defined at compile time, is /etc/cgitrc. At
20runtime, cgit will consult the environment variable CGIT_CONFIG and, if 20runtime, cgit will consult the environment variable CGIT_CONFIG and, if
21defined, use its value instead. 21defined, use its value instead.
22 22
23 23
24GLOBAL SETTINGS 24GLOBAL SETTINGS
25--------------- 25---------------
26about-filter:: 26about-filter::
27 Specifies a command which will be invoked to format the content of 27 Specifies a command which will be invoked to format the content of
28 about pages (both top-level and for each repository). The command will 28 about pages (both top-level and for each repository). The command will
29 get the content of the about-file on its STDIN, and the STDOUT from the 29 get the content of the about-file on its STDIN, and the STDOUT from the
30 command will be included verbatim on the about page. Default value: 30 command will be included verbatim on the about page. Default value:
31 none. 31 none.
32 32
33agefile:: 33agefile::
34 Specifies a path, relative to each repository path, which can be used 34 Specifies a path, relative to each repository path, which can be used
35 to specify the date and time of the youngest commit in the repository. 35 to specify the date and time of the youngest commit in the repository.
36 The first line in the file is used as input to the "parse_date" 36 The first line in the file is used as input to the "parse_date"
37 function in libgit. Recommended timestamp-format is "yyyy-mm-dd 37 function in libgit. Recommended timestamp-format is "yyyy-mm-dd
38 hh:mm:ss". Default value: "info/web/last-modified". 38 hh:mm:ss". Default value: "info/web/last-modified".
39 39
40cache-root:: 40cache-root::
41 Path used to store the cgit cache entries. Default value: 41 Path used to store the cgit cache entries. Default value:
42 "/var/cache/cgit". 42 "/var/cache/cgit".
43 43
44cache-dynamic-ttl:: 44cache-dynamic-ttl::
45 Number which specifies the time-to-live, in minutes, for the cached 45 Number which specifies the time-to-live, in minutes, for the cached
46 version of repository pages accessed without a fixed SHA1. Default 46 version of repository pages accessed without a fixed SHA1. Default
47 value: "5". 47 value: "5".
48 48
49cache-repo-ttl:: 49cache-repo-ttl::
50 Number which specifies the time-to-live, in minutes, for the cached 50 Number which specifies the time-to-live, in minutes, for the cached
51 version of the repository summary page. Default value: "5". 51 version of the repository summary page. Default value: "5".
52 52
53cache-root-ttl:: 53cache-root-ttl::
54 Number which specifies the time-to-live, in minutes, for the cached 54 Number which specifies the time-to-live, in minutes, for the cached
55 version of the repository index page. Default value: "5". 55 version of the repository index page. Default value: "5".
56 56
57cache-scanrc-ttl:: 57cache-scanrc-ttl::
58 Number which specifies the time-to-live, in minutes, for the result 58 Number which specifies the time-to-live, in minutes, for the result
59 of scanning a path for git repositories. Default value: "15". 59 of scanning a path for git repositories. Default value: "15".
60 60
61cache-size:: 61cache-size::
62 The maximum number of entries in the cgit cache. Default value: "0" 62 The maximum number of entries in the cgit cache. Default value: "0"
63 (i.e. caching is disabled). 63 (i.e. caching is disabled).
64 64
65cache-static-ttl:: 65cache-static-ttl::
66 Number which specifies the time-to-live, in minutes, for the cached 66 Number which specifies the time-to-live, in minutes, for the cached
67 version of repository pages accessed with a fixed SHA1. Default value: 67 version of repository pages accessed with a fixed SHA1. Default value:
68 "5". 68 "5".
69 69
70clone-prefix:: 70clone-prefix::
71 Space-separated list of common prefixes which, when combined with a 71 Space-separated list of common prefixes which, when combined with a
72 repository url, generates valid clone urls for the repository. This 72 repository url, generates valid clone urls for the repository. This
73 setting is only used if `repo.clone-url` is unspecified. Default value: 73 setting is only used if `repo.clone-url` is unspecified. Default value:
74 none. 74 none.
75 75
76commit-filter:: 76commit-filter::
77 Specifies a command which will be invoked to format commit messages. 77 Specifies a command which will be invoked to format commit messages.
78 The command will get the message on its STDIN, and the STDOUT from the 78 The command will get the message on its STDIN, and the STDOUT from the
79 command will be included verbatim as the commit message, i.e. this can 79 command will be included verbatim as the commit message, i.e. this can
80 be used to implement bugtracker integration. Default value: none. 80 be used to implement bugtracker integration. Default value: none.
81 81
82css:: 82css::
83 Url which specifies the css document to include in all cgit pages. 83 Url which specifies the css document to include in all cgit pages.
84 Default value: "/cgit.css". 84 Default value: "/cgit.css".
85 85
86embedded:: 86embedded::
87 Flag which, when set to "1", will make cgit generate a html fragment 87 Flag which, when set to "1", will make cgit generate a html fragment
88 suitable for embedding in other html pages. Default value: none. See 88 suitable for embedding in other html pages. Default value: none. See
89 also: "noheader". 89 also: "noheader".
90 90
91enable-index-links:: 91enable-index-links::
92 Flag which, when set to "1", will make cgit generate extra links for 92 Flag which, when set to "1", will make cgit generate extra links for
93 each repo in the repository index (specifically, to the "summary", 93 each repo in the repository index (specifically, to the "summary",
94 "commit" and "tree" pages). Default value: "0". 94 "commit" and "tree" pages). Default value: "0".
95 95
96enable-log-filecount:: 96enable-log-filecount::
97 Flag which, when set to "1", will make cgit print the number of 97 Flag which, when set to "1", will make cgit print the number of
98 modified files for each commit on the repository log page. Default 98 modified files for each commit on the repository log page. Default
99 value: "0". 99 value: "0".
100 100
101enable-log-linecount:: 101enable-log-linecount::
102 Flag which, when set to "1", will make cgit print the number of added 102 Flag which, when set to "1", will make cgit print the number of added
103 and removed lines for each commit on the repository log page. Default 103 and removed lines for each commit on the repository log page. Default
104 value: "0". 104 value: "0".
105 105
106favicon:: 106favicon::
107 Url used as link to a shortcut icon for cgit. If specified, it is 107 Url used as link to a shortcut icon for cgit. If specified, it is
108 suggested to use the value "/favicon.ico" since certain browsers will 108 suggested to use the value "/favicon.ico" since certain browsers will
109 ignore other values. Default value: none. 109 ignore other values. Default value: none.
110 110
111footer:: 111footer::
112 The content of the file specified with this option will be included 112 The content of the file specified with this option will be included
113 verbatim at the bottom of all pages (i.e. it replaces the standard 113 verbatim at the bottom of all pages (i.e. it replaces the standard
114 "generated by..." message. Default value: none. 114 "generated by..." message. Default value: none.
115 115
116head-include:: 116head-include::
117 The content of the file specified with this option will be included 117 The content of the file specified with this option will be included
118 verbatim in the html HEAD section on all pages. Default value: none. 118 verbatim in the html HEAD section on all pages. Default value: none.
119 119
120header:: 120header::
121 The content of the file specified with this option will be included 121 The content of the file specified with this option will be included
122 verbatim at the top of all pages. Default value: none. 122 verbatim at the top of all pages. Default value: none.
123 123
124include:: 124include::
125 Name of a configfile to include before the rest of the current config- 125 Name of a configfile to include before the rest of the current config-
126 file is parsed. Default value: none. 126 file is parsed. Default value: none.
127 127
128index-header:: 128index-header::
129 The content of the file specified with this option will be included 129 The content of the file specified with this option will be included
130 verbatim above the repository index. This setting is deprecated, and 130 verbatim above the repository index. This setting is deprecated, and
131 will not be supported by cgit-1.0 (use root-readme instead). Default 131 will not be supported by cgit-1.0 (use root-readme instead). Default
132 value: none. 132 value: none.
133 133
134index-info:: 134index-info::
135 The content of the file specified with this option will be included 135 The content of the file specified with this option will be included
136 verbatim below the heading on the repository index page. This setting 136 verbatim below the heading on the repository index page. This setting
137 is deprecated, and will not be supported by cgit-1.0 (use root-desc 137 is deprecated, and will not be supported by cgit-1.0 (use root-desc
138 instead). Default value: none. 138 instead). Default value: none.
139 139
140local-time:: 140local-time::
141 Flag which, if set to "1", makes cgit print commit and tag times in the 141 Flag which, if set to "1", makes cgit print commit and tag times in the
142 servers timezone. Default value: "0". 142 servers timezone. Default value: "0".
143 143
144logo:: 144logo::
145 Url which specifies the source of an image which will be used as a logo 145 Url which specifies the source of an image which will be used as a logo
146 on all cgit pages. Default value: "/cgit.png". 146 on all cgit pages. Default value: "/cgit.png".
147 147
148logo-link:: 148logo-link::
149 Url loaded when clicking on the cgit logo image. If unspecified the 149 Url loaded when clicking on the cgit logo image. If unspecified the
150 calculated url of the repository index page will be used. Default 150 calculated url of the repository index page will be used. Default
151 value: none. 151 value: none.
152 152
153max-commit-count:: 153max-commit-count::
154 Specifies the number of entries to list per page in "log" view. Default 154 Specifies the number of entries to list per page in "log" view. Default
155 value: "50". 155 value: "50".
156 156
157max-message-length:: 157max-message-length::
158 Specifies the maximum number of commit message characters to display in 158 Specifies the maximum number of commit message characters to display in
159 "log" view. Default value: "80". 159 "log" view. Default value: "80".
160 160
161max-repo-count:: 161max-repo-count::
162 Specifies the number of entries to list per page on therepository 162 Specifies the number of entries to list per page on therepository
163 index page. Default value: "50". 163 index page. Default value: "50".
164 164
165max-repodesc-length:: 165max-repodesc-length::
166 Specifies the maximum number of repo description characters to display 166 Specifies the maximum number of repo description characters to display
167 on the repository index page. Default value: "80". 167 on the repository index page. Default value: "80".
168 168
169max-stats:: 169max-stats::
170 Set the default maximum statistics period. Valid values are "week", 170 Set the default maximum statistics period. Valid values are "week",
171 "month", "quarter" and "year". If unspecified, statistics are 171 "month", "quarter" and "year". If unspecified, statistics are
172 disabled. Default value: none. See also: "repo.max-stats". 172 disabled. Default value: none. See also: "repo.max-stats".
173 173
174mimetype.<ext>:: 174mimetype.<ext>::
175 Set the mimetype for the specified filename extension. This is used 175 Set the mimetype for the specified filename extension. This is used
176 by the `plain` command when returning blob content. 176 by the `plain` command when returning blob content.
177 177
178module-link:: 178module-link::
179 Text which will be used as the formatstring for a hyperlink when a 179 Text which will be used as the formatstring for a hyperlink when a
180 submodule is printed in a directory listing. The arguments for the 180 submodule is printed in a directory listing. The arguments for the
181 formatstring are the path and SHA1 of the submodule commit. Default 181 formatstring are the path and SHA1 of the submodule commit. Default
182 value: "./?repo=%s&page=commit&id=%s" 182 value: "./?repo=%s&page=commit&id=%s"
183 183
184nocache:: 184nocache::
185 If set to the value "1" caching will be disabled. This settings is 185 If set to the value "1" caching will be disabled. This settings is
186 deprecated, and will not be honored starting with cgit-1.0. Default 186 deprecated, and will not be honored starting with cgit-1.0. Default
187 value: "0". 187 value: "0".
188 188
189noplainemail:: 189noplainemail::
190 If set to "1" showing full author email adresses will be disabled. 190 If set to "1" showing full author email adresses will be disabled.
191 Default value: "0". 191 Default value: "0".
192 192
193noheader:: 193noheader::
194 Flag which, when set to "1", will make cgit omit the standard header 194 Flag which, when set to "1", will make cgit omit the standard header
195 on all pages. Default value: none. See also: "embedded". 195 on all pages. Default value: none. See also: "embedded".
196 196
197renamelimit:: 197renamelimit::
198 Maximum number of files to consider when detecting renames. The value 198 Maximum number of files to consider when detecting renames. The value
199 "-1" uses the compiletime value in git (for further info, look at 199 "-1" uses the compiletime value in git (for further info, look at
200 `man git-diff`). Default value: "-1". 200 `man git-diff`). Default value: "-1".
201 201
202repo.group:: 202repo.group::
203 A value for the current repository group, which all repositories 203 Legacy alias for 'section' which will be deprecated starting with
204 specified after this setting will inherit. Default value: none. 204 cgit-1.0.
205 205
206robots:: 206robots::
207 Text used as content for the "robots" meta-tag. Default value: 207 Text used as content for the "robots" meta-tag. Default value:
208 "index, nofollow". 208 "index, nofollow".
209 209
210root-desc:: 210root-desc::
211 Text printed below the heading on the repository index page. Default 211 Text printed below the heading on the repository index page. Default
212 value: "a fast webinterface for the git dscm". 212 value: "a fast webinterface for the git dscm".
213 213
214root-readme:: 214root-readme::
215 The content of the file specified with this option will be included 215 The content of the file specified with this option will be included
216 verbatim below the "about" link on the repository index page. Default 216 verbatim below the "about" link on the repository index page. Default
217 value: none. 217 value: none.
218 218
219root-title:: 219root-title::
220 Text printed as heading on the repository index page. Default value: 220 Text printed as heading on the repository index page. Default value:
221 "Git Repository Browser". 221 "Git Repository Browser".
222 222
223scan-path:: 223scan-path::
224 A path which will be scanned for repositories. If caching is enabled, 224 A path which will be scanned for repositories. If caching is enabled,
225 the result will be cached as a cgitrc include-file in the cache 225 the result will be cached as a cgitrc include-file in the cache
226 directory. Default value: none. See also: cache-scanrc-ttl. 226 directory. Default value: none. See also: cache-scanrc-ttl.
227 227
228section:
229 The name of the current repository section - all repositories defined
230 after this option will inherit the current section name. Default value:
231 none.
232
228snapshots:: 233snapshots::
229 Text which specifies the default set of snapshot formats generated by 234 Text which specifies the default set of snapshot formats generated by
230 cgit. The value is a space-separated list of zero or more of the 235 cgit. The value is a space-separated list of zero or more of the
231 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 236 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
232 237
233source-filter:: 238source-filter::
234 Specifies a command which will be invoked to format plaintext blobs 239 Specifies a command which will be invoked to format plaintext blobs
235 in the tree view. The command will get the blob content on its STDIN 240 in the tree view. The command will get the blob content on its STDIN
236 and the name of the blob as its only command line argument. The STDOUT 241 and the name of the blob as its only command line argument. The STDOUT
237 from the command will be included verbatim as the blob contents, i.e. 242 from the command will be included verbatim as the blob contents, i.e.
238 this can be used to implement e.g. syntax highlighting. Default value: 243 this can be used to implement e.g. syntax highlighting. Default value:
239 none. 244 none.
240 245
241summary-branches:: 246summary-branches::
242 Specifies the number of branches to display in the repository "summary" 247 Specifies the number of branches to display in the repository "summary"
243 view. Default value: "10". 248 view. Default value: "10".
244 249
245summary-log:: 250summary-log::
246 Specifies the number of log entries to display in the repository 251 Specifies the number of log entries to display in the repository
247 "summary" view. Default value: "10". 252 "summary" view. Default value: "10".
248 253
249summary-tags:: 254summary-tags::
250 Specifies the number of tags to display in the repository "summary" 255 Specifies the number of tags to display in the repository "summary"
251 view. Default value: "10". 256 view. Default value: "10".
252 257
253virtual-root:: 258virtual-root::
254 Url which, if specified, will be used as root for all cgit links. It 259 Url which, if specified, will be used as root for all cgit links. It
255 will also cause cgit to generate 'virtual urls', i.e. urls like 260 will also cause cgit to generate 'virtual urls', i.e. urls like
256 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 261 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
257 value: none. 262 value: none.
258 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 263 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
259 same kind of virtual urls, so this option will probably be deprecated. 264 same kind of virtual urls, so this option will probably be deprecated.
260 265
261REPOSITORY SETTINGS 266REPOSITORY SETTINGS
262------------------- 267-------------------
263repo.about-filter:: 268repo.about-filter::
264 Override the default about-filter. Default value: <about-filter>. 269 Override the default about-filter. Default value: <about-filter>.
265 270
266repo.clone-url:: 271repo.clone-url::
267 A list of space-separated urls which can be used to clone this repo. 272 A list of space-separated urls which can be used to clone this repo.
268 Default value: none. 273 Default value: none.
269 274
270repo.commit-filter:: 275repo.commit-filter::
271 Override the default commit-filter. Default value: <commit-filter>. 276 Override the default commit-filter. Default value: <commit-filter>.
272 277
273repo.defbranch:: 278repo.defbranch::
274 The name of the default branch for this repository. If no such branch 279 The name of the default branch for this repository. If no such branch
275 exists in the repository, the first branch name (when sorted) is used 280 exists in the repository, the first branch name (when sorted) is used
276 as default instead. Default value: "master". 281 as default instead. Default value: "master".
277 282
278repo.desc:: 283repo.desc::
279 The value to show as repository description. Default value: none. 284 The value to show as repository description. Default value: none.
280 285
281repo.enable-log-filecount:: 286repo.enable-log-filecount::
282 A flag which can be used to disable the global setting 287 A flag which can be used to disable the global setting
283 `enable-log-filecount'. Default value: none. 288 `enable-log-filecount'. Default value: none.
284 289
285repo.enable-log-linecount:: 290repo.enable-log-linecount::
286 A flag which can be used to disable the global setting 291 A flag which can be used to disable the global setting
287 `enable-log-linecount'. Default value: none. 292 `enable-log-linecount'. Default value: none.
288 293
289repo.max-stats:: 294repo.max-stats::
290 Override the default maximum statistics period. Valid values are equal 295 Override the default maximum statistics period. Valid values are equal
291 to the values specified for the global "max-stats" setting. Default 296 to the values specified for the global "max-stats" setting. Default
292 value: none. 297 value: none.
293 298
294repo.name:: 299repo.name::
295 The value to show as repository name. Default value: <repo.url>. 300 The value to show as repository name. Default value: <repo.url>.
296 301
297repo.owner:: 302repo.owner::
298 A value used to identify the owner of the repository. Default value: 303 A value used to identify the owner of the repository. Default value:
299 none. 304 none.
300 305
301repo.path:: 306repo.path::
302 An absolute path to the repository directory. For non-bare repositories 307 An absolute path to the repository directory. For non-bare repositories
303 this is the .git-directory. Default value: none. 308 this is the .git-directory. Default value: none.
304 309
305repo.readme:: 310repo.readme::
306 A path (relative to <repo.path>) which specifies a file to include 311 A path (relative to <repo.path>) which specifies a file to include
307 verbatim as the "About" page for this repo. Default value: none. 312 verbatim as the "About" page for this repo. Default value: none.
308 313
309repo.snapshots:: 314repo.snapshots::
310 A mask of allowed snapshot-formats for this repo, restricted by the 315 A mask of allowed snapshot-formats for this repo, restricted by the
311 "snapshots" global setting. Default value: <snapshots>. 316 "snapshots" global setting. Default value: <snapshots>.
312 317
313repo.source-filter:: 318repo.source-filter::
314 Override the default source-filter. Default value: <source-filter>. 319 Override the default source-filter. Default value: <source-filter>.
315 320
316repo.url:: 321repo.url::
317 The relative url used to access the repository. This must be the first 322 The relative url used to access the repository. This must be the first
318 setting specified for each repo. Default value: none. 323 setting specified for each repo. Default value: none.
319 324
320 325
321EXAMPLE CGITRC FILE 326EXAMPLE CGITRC FILE
322------------------- 327-------------------
323 328
324.... 329....
325# Enable caching of up to 1000 output entriess 330# Enable caching of up to 1000 output entriess
326cache-size=1000 331cache-size=1000
327 332
328 333
329# Specify some default clone prefixes 334# Specify some default clone prefixes
330clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git 335clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
331 336
332# Specify the css url 337# Specify the css url
333css=/css/cgit.css 338css=/css/cgit.css
334 339
335 340
336# Show extra links for each repository on the index page 341# Show extra links for each repository on the index page
337enable-index-links=1 342enable-index-links=1
338 343
339 344
340# Show number of affected files per commit on the log pages 345# Show number of affected files per commit on the log pages
341enable-log-filecount=1 346enable-log-filecount=1
342 347
343 348
344# Show number of added/removed lines per commit on the log pages 349# Show number of added/removed lines per commit on the log pages
345enable-log-linecount=1 350enable-log-linecount=1
346 351
347 352
348# Add a cgit favicon 353# Add a cgit favicon
349favicon=/favicon.ico 354favicon=/favicon.ico
350 355
351 356
352# Use a custom logo 357# Use a custom logo
353logo=/img/mylogo.png 358logo=/img/mylogo.png
354 359
355 360
356# Enable statistics per week, month and quarter 361# Enable statistics per week, month and quarter
357max-stats=quarter 362max-stats=quarter
358 363
359 364
360# Set the title and heading of the repository index page 365# Set the title and heading of the repository index page
361root-title=foobar.com git repositories 366root-title=foobar.com git repositories
362 367
363 368
364# Set a subheading for the repository index page 369# Set a subheading for the repository index page
365root-desc=tracking the foobar development 370root-desc=tracking the foobar development
366 371
367 372
368# Include some more info about foobar.com on the index page 373# Include some more info about foobar.com on the index page
369root-readme=/var/www/htdocs/about.html 374root-readme=/var/www/htdocs/about.html
370 375
371 376
372# Allow download of tar.gz, tar.bz2 and zip-files 377# Allow download of tar.gz, tar.bz2 and zip-files
373snapshots=tar.gz tar.bz2 zip 378snapshots=tar.gz tar.bz2 zip
374 379
375 380
376## 381##
377## List of common mimetypes 382## List of common mimetypes
378## 383##
379 384
380mimetype.git=image/git 385mimetype.git=image/git
381mimetype.html=text/html 386mimetype.html=text/html
382mimetype.jpg=image/jpeg 387mimetype.jpg=image/jpeg
383mimetype.jpeg=image/jpeg 388mimetype.jpeg=image/jpeg
384mimetype.pdf=application/pdf 389mimetype.pdf=application/pdf
385mimetype.png=image/png 390mimetype.png=image/png
386mimetype.svg=image/svg+xml 391mimetype.svg=image/svg+xml
387 392
388 393
389## 394##
390## List of repositories. 395## List of repositories.
391## PS: Any repositories listed when repo.group is unset will not be 396## PS: Any repositories listed when repo.group is unset will not be
392## displayed under a group heading 397## displayed under a group heading
393## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') 398## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
394## and included like this: 399## and included like this:
395## include=/etc/cgitrepos 400## include=/etc/cgitrepos
396## 401##
397 402
398 403
399repo.url=foo 404repo.url=foo
400repo.path=/pub/git/foo.git 405repo.path=/pub/git/foo.git
401repo.desc=the master foo repository 406repo.desc=the master foo repository
402repo.owner=fooman@foobar.com 407repo.owner=fooman@foobar.com
403repo.readme=info/web/about.html 408repo.readme=info/web/about.html
404 409
405 410
406repo.url=bar 411repo.url=bar
407repo.path=/pub/git/bar.git 412repo.path=/pub/git/bar.git
408repo.desc=the bars for your foo 413repo.desc=the bars for your foo
409repo.owner=barman@foobar.com 414repo.owner=barman@foobar.com
410repo.readme=info/web/about.html 415repo.readme=info/web/about.html
411 416
412 417
413# The next repositories will be displayed under the 'extras' heading 418# The next repositories will be displayed under the 'extras' heading
414repo.group=extras 419repo.group=extras
415 420
416 421
417repo.url=baz 422repo.url=baz
418repo.path=/pub/git/baz.git 423repo.path=/pub/git/baz.git
419repo.desc=a set of extensions for bar users 424repo.desc=a set of extensions for bar users
420 425
421repo.url=wiz 426repo.url=wiz
422repo.path=/pub/git/wiz.git 427repo.path=/pub/git/wiz.git
423repo.desc=the wizard of foo 428repo.desc=the wizard of foo
424 429
425 430
426# Add some mirrored repositories 431# Add some mirrored repositories
427repo.group=mirrors 432repo.group=mirrors
428 433
429 434
430repo.url=git 435repo.url=git
431repo.path=/pub/git/git.git 436repo.path=/pub/git/git.git
432repo.desc=the dscm 437repo.desc=the dscm
433 438
434 439
435repo.url=linux 440repo.url=linux
436repo.path=/pub/git/linux.git 441repo.path=/pub/git/linux.git
437repo.desc=the kernel 442repo.desc=the kernel
438 443
439# Disable adhoc downloads of this repo 444# Disable adhoc downloads of this repo
440repo.snapshots=0 445repo.snapshots=0
441 446
442# Disable line-counts for this repo 447# Disable line-counts for this repo
443repo.enable-log-linecount=0 448repo.enable-log-linecount=0
444 449
445# Restrict the max statistics period for this repo 450# Restrict the max statistics period for this repo
446repo.max-stats=month 451repo.max-stats=month
447.... 452....
448 453
449 454
450BUGS 455BUGS
451---- 456----
452Comments currently cannot appear on the same line as a setting; the comment 457Comments currently cannot appear on the same line as a setting; the comment
453will be included as part of the value. E.g. this line: 458will be included as part of the value. E.g. this line:
454 459
455 robots=index # allow indexing 460 robots=index # allow indexing
456 461
457will generate the following html element: 462will generate the following html element:
458 463
459 <meta name='robots' content='index # allow indexing'/> 464 <meta name='robots' content='index # allow indexing'/>
460 465
461 466
462 467
463AUTHOR 468AUTHOR
464------ 469------
465Lars Hjemli <hjemli@gmail.com> 470Lars Hjemli <hjemli@gmail.com>
diff --git a/shared.c b/shared.c
index 4cb9573..9475581 100644
--- a/shared.c
+++ b/shared.c
@@ -1,416 +1,416 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback functions
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 10
11struct cgit_repolist cgit_repolist; 11struct cgit_repolist cgit_repolist;
12struct cgit_context ctx; 12struct cgit_context ctx;
13int cgit_cmd; 13int cgit_cmd;
14 14
15int chk_zero(int result, char *msg) 15int chk_zero(int result, char *msg)
16{ 16{
17 if (result != 0) 17 if (result != 0)
18 die("%s: %s", msg, strerror(errno)); 18 die("%s: %s", msg, strerror(errno));
19 return result; 19 return result;
20} 20}
21 21
22int chk_positive(int result, char *msg) 22int chk_positive(int result, char *msg)
23{ 23{
24 if (result <= 0) 24 if (result <= 0)
25 die("%s: %s", msg, strerror(errno)); 25 die("%s: %s", msg, strerror(errno));
26 return result; 26 return result;
27} 27}
28 28
29int chk_non_negative(int result, char *msg) 29int chk_non_negative(int result, char *msg)
30{ 30{
31 if (result < 0) 31 if (result < 0)
32 die("%s: %s",msg, strerror(errno)); 32 die("%s: %s",msg, strerror(errno));
33 return result; 33 return result;
34} 34}
35 35
36struct cgit_repo *cgit_add_repo(const char *url) 36struct cgit_repo *cgit_add_repo(const char *url)
37{ 37{
38 struct cgit_repo *ret; 38 struct cgit_repo *ret;
39 39
40 if (++cgit_repolist.count > cgit_repolist.length) { 40 if (++cgit_repolist.count > cgit_repolist.length) {
41 if (cgit_repolist.length == 0) 41 if (cgit_repolist.length == 0)
42 cgit_repolist.length = 8; 42 cgit_repolist.length = 8;
43 else 43 else
44 cgit_repolist.length *= 2; 44 cgit_repolist.length *= 2;
45 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 45 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
46 cgit_repolist.length * 46 cgit_repolist.length *
47 sizeof(struct cgit_repo)); 47 sizeof(struct cgit_repo));
48 } 48 }
49 49
50 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 50 ret = &cgit_repolist.repos[cgit_repolist.count-1];
51 ret->url = trim_end(url, '/'); 51 ret->url = trim_end(url, '/');
52 ret->name = ret->url; 52 ret->name = ret->url;
53 ret->path = NULL; 53 ret->path = NULL;
54 ret->desc = "[no description]"; 54 ret->desc = "[no description]";
55 ret->owner = NULL; 55 ret->owner = NULL;
56 ret->group = ctx.cfg.repo_group; 56 ret->section = ctx.cfg.section;
57 ret->defbranch = "master"; 57 ret->defbranch = "master";
58 ret->snapshots = ctx.cfg.snapshots; 58 ret->snapshots = ctx.cfg.snapshots;
59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->max_stats = ctx.cfg.max_stats; 61 ret->max_stats = ctx.cfg.max_stats;
62 ret->module_link = ctx.cfg.module_link; 62 ret->module_link = ctx.cfg.module_link;
63 ret->readme = NULL; 63 ret->readme = NULL;
64 ret->mtime = -1; 64 ret->mtime = -1;
65 ret->about_filter = ctx.cfg.about_filter; 65 ret->about_filter = ctx.cfg.about_filter;
66 ret->commit_filter = ctx.cfg.commit_filter; 66 ret->commit_filter = ctx.cfg.commit_filter;
67 ret->source_filter = ctx.cfg.source_filter; 67 ret->source_filter = ctx.cfg.source_filter;
68 return ret; 68 return ret;
69} 69}
70 70
71struct cgit_repo *cgit_get_repoinfo(const char *url) 71struct cgit_repo *cgit_get_repoinfo(const char *url)
72{ 72{
73 int i; 73 int i;
74 struct cgit_repo *repo; 74 struct cgit_repo *repo;
75 75
76 for (i=0; i<cgit_repolist.count; i++) { 76 for (i=0; i<cgit_repolist.count; i++) {
77 repo = &cgit_repolist.repos[i]; 77 repo = &cgit_repolist.repos[i];
78 if (!strcmp(repo->url, url)) 78 if (!strcmp(repo->url, url))
79 return repo; 79 return repo;
80 } 80 }
81 return NULL; 81 return NULL;
82} 82}
83 83
84void *cgit_free_commitinfo(struct commitinfo *info) 84void *cgit_free_commitinfo(struct commitinfo *info)
85{ 85{
86 free(info->author); 86 free(info->author);
87 free(info->author_email); 87 free(info->author_email);
88 free(info->committer); 88 free(info->committer);
89 free(info->committer_email); 89 free(info->committer_email);
90 free(info->subject); 90 free(info->subject);
91 free(info->msg); 91 free(info->msg);
92 free(info->msg_encoding); 92 free(info->msg_encoding);
93 free(info); 93 free(info);
94 return NULL; 94 return NULL;
95} 95}
96 96
97char *trim_end(const char *str, char c) 97char *trim_end(const char *str, char c)
98{ 98{
99 int len; 99 int len;
100 char *s, *t; 100 char *s, *t;
101 101
102 if (str == NULL) 102 if (str == NULL)
103 return NULL; 103 return NULL;
104 t = (char *)str; 104 t = (char *)str;
105 len = strlen(t); 105 len = strlen(t);
106 while(len > 0 && t[len - 1] == c) 106 while(len > 0 && t[len - 1] == c)
107 len--; 107 len--;
108 108
109 if (len == 0) 109 if (len == 0)
110 return NULL; 110 return NULL;
111 111
112 c = t[len]; 112 c = t[len];
113 t[len] = '\0'; 113 t[len] = '\0';
114 s = xstrdup(t); 114 s = xstrdup(t);
115 t[len] = c; 115 t[len] = c;
116 return s; 116 return s;
117} 117}
118 118
119char *strlpart(char *txt, int maxlen) 119char *strlpart(char *txt, int maxlen)
120{ 120{
121 char *result; 121 char *result;
122 122
123 if (!txt) 123 if (!txt)
124 return txt; 124 return txt;
125 125
126 if (strlen(txt) <= maxlen) 126 if (strlen(txt) <= maxlen)
127 return txt; 127 return txt;
128 result = xmalloc(maxlen + 1); 128 result = xmalloc(maxlen + 1);
129 memcpy(result, txt, maxlen - 3); 129 memcpy(result, txt, maxlen - 3);
130 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.'; 130 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.';
131 result[maxlen] = '\0'; 131 result[maxlen] = '\0';
132 return result; 132 return result;
133} 133}
134 134
135char *strrpart(char *txt, int maxlen) 135char *strrpart(char *txt, int maxlen)
136{ 136{
137 char *result; 137 char *result;
138 138
139 if (!txt) 139 if (!txt)
140 return txt; 140 return txt;
141 141
142 if (strlen(txt) <= maxlen) 142 if (strlen(txt) <= maxlen)
143 return txt; 143 return txt;
144 result = xmalloc(maxlen + 1); 144 result = xmalloc(maxlen + 1);
145 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3); 145 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
146 result[0] = result[1] = result[2] = '.'; 146 result[0] = result[1] = result[2] = '.';
147 return result; 147 return result;
148} 148}
149 149
150void cgit_add_ref(struct reflist *list, struct refinfo *ref) 150void cgit_add_ref(struct reflist *list, struct refinfo *ref)
151{ 151{
152 size_t size; 152 size_t size;
153 153
154 if (list->count >= list->alloc) { 154 if (list->count >= list->alloc) {
155 list->alloc += (list->alloc ? list->alloc : 4); 155 list->alloc += (list->alloc ? list->alloc : 4);
156 size = list->alloc * sizeof(struct refinfo *); 156 size = list->alloc * sizeof(struct refinfo *);
157 list->refs = xrealloc(list->refs, size); 157 list->refs = xrealloc(list->refs, size);
158 } 158 }
159 list->refs[list->count++] = ref; 159 list->refs[list->count++] = ref;
160} 160}
161 161
162struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 162struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
163{ 163{
164 struct refinfo *ref; 164 struct refinfo *ref;
165 165
166 ref = xmalloc(sizeof (struct refinfo)); 166 ref = xmalloc(sizeof (struct refinfo));
167 ref->refname = xstrdup(refname); 167 ref->refname = xstrdup(refname);
168 ref->object = parse_object(sha1); 168 ref->object = parse_object(sha1);
169 switch (ref->object->type) { 169 switch (ref->object->type) {
170 case OBJ_TAG: 170 case OBJ_TAG:
171 ref->tag = cgit_parse_tag((struct tag *)ref->object); 171 ref->tag = cgit_parse_tag((struct tag *)ref->object);
172 break; 172 break;
173 case OBJ_COMMIT: 173 case OBJ_COMMIT:
174 ref->commit = cgit_parse_commit((struct commit *)ref->object); 174 ref->commit = cgit_parse_commit((struct commit *)ref->object);
175 break; 175 break;
176 } 176 }
177 return ref; 177 return ref;
178} 178}
179 179
180int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, 180int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
181 void *cb_data) 181 void *cb_data)
182{ 182{
183 struct reflist *list = (struct reflist *)cb_data; 183 struct reflist *list = (struct reflist *)cb_data;
184 struct refinfo *info = cgit_mk_refinfo(refname, sha1); 184 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
185 185
186 if (info) 186 if (info)
187 cgit_add_ref(list, info); 187 cgit_add_ref(list, info);
188 return 0; 188 return 0;
189} 189}
190 190
191void cgit_diff_tree_cb(struct diff_queue_struct *q, 191void cgit_diff_tree_cb(struct diff_queue_struct *q,
192 struct diff_options *options, void *data) 192 struct diff_options *options, void *data)
193{ 193{
194 int i; 194 int i;
195 195
196 for (i = 0; i < q->nr; i++) { 196 for (i = 0; i < q->nr; i++) {
197 if (q->queue[i]->status == 'U') 197 if (q->queue[i]->status == 'U')
198 continue; 198 continue;
199 ((filepair_fn)data)(q->queue[i]); 199 ((filepair_fn)data)(q->queue[i]);
200 } 200 }
201} 201}
202 202
203static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 203static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
204{ 204{
205 enum object_type type; 205 enum object_type type;
206 206
207 if (is_null_sha1(sha1)) { 207 if (is_null_sha1(sha1)) {
208 file->ptr = (char *)""; 208 file->ptr = (char *)"";
209 file->size = 0; 209 file->size = 0;
210 } else { 210 } else {
211 file->ptr = read_sha1_file(sha1, &type, 211 file->ptr = read_sha1_file(sha1, &type,
212 (unsigned long *)&file->size); 212 (unsigned long *)&file->size);
213 } 213 }
214 return 1; 214 return 1;
215} 215}
216 216
217/* 217/*
218 * Receive diff-buffers from xdiff and concatenate them as 218 * Receive diff-buffers from xdiff and concatenate them as
219 * needed across multiple callbacks. 219 * needed across multiple callbacks.
220 * 220 *
221 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 221 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
222 * ripped from git and modified to use globals instead of 222 * ripped from git and modified to use globals instead of
223 * a special callback-struct. 223 * a special callback-struct.
224 */ 224 */
225char *diffbuf = NULL; 225char *diffbuf = NULL;
226int buflen = 0; 226int buflen = 0;
227 227
228int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 228int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
229{ 229{
230 int i; 230 int i;
231 231
232 for (i = 0; i < nbuf; i++) { 232 for (i = 0; i < nbuf; i++) {
233 if (mb[i].ptr[mb[i].size-1] != '\n') { 233 if (mb[i].ptr[mb[i].size-1] != '\n') {
234 /* Incomplete line */ 234 /* Incomplete line */
235 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 235 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
236 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 236 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
237 buflen += mb[i].size; 237 buflen += mb[i].size;
238 continue; 238 continue;
239 } 239 }
240 240
241 /* we have a complete line */ 241 /* we have a complete line */
242 if (!diffbuf) { 242 if (!diffbuf) {
243 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 243 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
244 continue; 244 continue;
245 } 245 }
246 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 246 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
247 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 247 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
248 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 248 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
249 free(diffbuf); 249 free(diffbuf);
250 diffbuf = NULL; 250 diffbuf = NULL;
251 buflen = 0; 251 buflen = 0;
252 } 252 }
253 if (diffbuf) { 253 if (diffbuf) {
254 ((linediff_fn)priv)(diffbuf, buflen); 254 ((linediff_fn)priv)(diffbuf, buflen);
255 free(diffbuf); 255 free(diffbuf);
256 diffbuf = NULL; 256 diffbuf = NULL;
257 buflen = 0; 257 buflen = 0;
258 } 258 }
259 return 0; 259 return 0;
260} 260}
261 261
262int cgit_diff_files(const unsigned char *old_sha1, 262int cgit_diff_files(const unsigned char *old_sha1,
263 const unsigned char *new_sha1, unsigned long *old_size, 263 const unsigned char *new_sha1, unsigned long *old_size,
264 unsigned long *new_size, int *binary, linediff_fn fn) 264 unsigned long *new_size, int *binary, linediff_fn fn)
265{ 265{
266 mmfile_t file1, file2; 266 mmfile_t file1, file2;
267 xpparam_t diff_params; 267 xpparam_t diff_params;
268 xdemitconf_t emit_params; 268 xdemitconf_t emit_params;
269 xdemitcb_t emit_cb; 269 xdemitcb_t emit_cb;
270 270
271 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 271 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
272 return 1; 272 return 1;
273 273
274 *old_size = file1.size; 274 *old_size = file1.size;
275 *new_size = file2.size; 275 *new_size = file2.size;
276 276
277 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || 277 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
278 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { 278 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
279 *binary = 1; 279 *binary = 1;
280 return 0; 280 return 0;
281 } 281 }
282 282
283 memset(&diff_params, 0, sizeof(diff_params)); 283 memset(&diff_params, 0, sizeof(diff_params));
284 memset(&emit_params, 0, sizeof(emit_params)); 284 memset(&emit_params, 0, sizeof(emit_params));
285 memset(&emit_cb, 0, sizeof(emit_cb)); 285 memset(&emit_cb, 0, sizeof(emit_cb));
286 diff_params.flags = XDF_NEED_MINIMAL; 286 diff_params.flags = XDF_NEED_MINIMAL;
287 emit_params.ctxlen = 3; 287 emit_params.ctxlen = 3;
288 emit_params.flags = XDL_EMIT_FUNCNAMES; 288 emit_params.flags = XDL_EMIT_FUNCNAMES;
289 emit_cb.outf = filediff_cb; 289 emit_cb.outf = filediff_cb;
290 emit_cb.priv = fn; 290 emit_cb.priv = fn;
291 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 291 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
292 return 0; 292 return 0;
293} 293}
294 294
295void cgit_diff_tree(const unsigned char *old_sha1, 295void cgit_diff_tree(const unsigned char *old_sha1,
296 const unsigned char *new_sha1, 296 const unsigned char *new_sha1,
297 filepair_fn fn, const char *prefix) 297 filepair_fn fn, const char *prefix)
298{ 298{
299 struct diff_options opt; 299 struct diff_options opt;
300 int ret; 300 int ret;
301 int prefixlen; 301 int prefixlen;
302 302
303 diff_setup(&opt); 303 diff_setup(&opt);
304 opt.output_format = DIFF_FORMAT_CALLBACK; 304 opt.output_format = DIFF_FORMAT_CALLBACK;
305 opt.detect_rename = 1; 305 opt.detect_rename = 1;
306 opt.rename_limit = ctx.cfg.renamelimit; 306 opt.rename_limit = ctx.cfg.renamelimit;
307 DIFF_OPT_SET(&opt, RECURSIVE); 307 DIFF_OPT_SET(&opt, RECURSIVE);
308 opt.format_callback = cgit_diff_tree_cb; 308 opt.format_callback = cgit_diff_tree_cb;
309 opt.format_callback_data = fn; 309 opt.format_callback_data = fn;
310 if (prefix) { 310 if (prefix) {
311 opt.nr_paths = 1; 311 opt.nr_paths = 1;
312 opt.paths = &prefix; 312 opt.paths = &prefix;
313 prefixlen = strlen(prefix); 313 prefixlen = strlen(prefix);
314 opt.pathlens = &prefixlen; 314 opt.pathlens = &prefixlen;
315 } 315 }
316 diff_setup_done(&opt); 316 diff_setup_done(&opt);
317 317
318 if (old_sha1 && !is_null_sha1(old_sha1)) 318 if (old_sha1 && !is_null_sha1(old_sha1))
319 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 319 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
320 else 320 else
321 ret = diff_root_tree_sha1(new_sha1, "", &opt); 321 ret = diff_root_tree_sha1(new_sha1, "", &opt);
322 diffcore_std(&opt); 322 diffcore_std(&opt);
323 diff_flush(&opt); 323 diff_flush(&opt);
324} 324}
325 325
326void cgit_diff_commit(struct commit *commit, filepair_fn fn) 326void cgit_diff_commit(struct commit *commit, filepair_fn fn)
327{ 327{
328 unsigned char *old_sha1 = NULL; 328 unsigned char *old_sha1 = NULL;
329 329
330 if (commit->parents) 330 if (commit->parents)
331 old_sha1 = commit->parents->item->object.sha1; 331 old_sha1 = commit->parents->item->object.sha1;
332 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); 332 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
333} 333}
334 334
335int cgit_parse_snapshots_mask(const char *str) 335int cgit_parse_snapshots_mask(const char *str)
336{ 336{
337 const struct cgit_snapshot_format *f; 337 const struct cgit_snapshot_format *f;
338 static const char *delim = " \t,:/|;"; 338 static const char *delim = " \t,:/|;";
339 int tl, sl, rv = 0; 339 int tl, sl, rv = 0;
340 340
341 /* favor legacy setting */ 341 /* favor legacy setting */
342 if(atoi(str)) 342 if(atoi(str))
343 return 1; 343 return 1;
344 for(;;) { 344 for(;;) {
345 str += strspn(str,delim); 345 str += strspn(str,delim);
346 tl = strcspn(str,delim); 346 tl = strcspn(str,delim);
347 if (!tl) 347 if (!tl)
348 break; 348 break;
349 for (f = cgit_snapshot_formats; f->suffix; f++) { 349 for (f = cgit_snapshot_formats; f->suffix; f++) {
350 sl = strlen(f->suffix); 350 sl = strlen(f->suffix);
351 if((tl == sl && !strncmp(f->suffix, str, tl)) || 351 if((tl == sl && !strncmp(f->suffix, str, tl)) ||
352 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { 352 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) {
353 rv |= f->bit; 353 rv |= f->bit;
354 break; 354 break;
355 } 355 }
356 } 356 }
357 str += tl; 357 str += tl;
358 } 358 }
359 return rv; 359 return rv;
360} 360}
361 361
362int cgit_open_filter(struct cgit_filter *filter) 362int cgit_open_filter(struct cgit_filter *filter)
363{ 363{
364 364
365 filter->old_stdout = chk_positive(dup(STDOUT_FILENO), 365 filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
366 "Unable to duplicate STDOUT"); 366 "Unable to duplicate STDOUT");
367 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess"); 367 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
368 filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); 368 filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
369 if (filter->pid == 0) { 369 if (filter->pid == 0) {
370 close(filter->pipe_fh[1]); 370 close(filter->pipe_fh[1]);
371 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO), 371 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
372 "Unable to use pipe as STDIN"); 372 "Unable to use pipe as STDIN");
373 execvp(filter->cmd, filter->argv); 373 execvp(filter->cmd, filter->argv);
374 die("Unable to exec subprocess %s: %s (%d)", filter->cmd, 374 die("Unable to exec subprocess %s: %s (%d)", filter->cmd,
375 strerror(errno), errno); 375 strerror(errno), errno);
376 } 376 }
377 close(filter->pipe_fh[0]); 377 close(filter->pipe_fh[0]);
378 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO), 378 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
379 "Unable to use pipe as STDOUT"); 379 "Unable to use pipe as STDOUT");
380 close(filter->pipe_fh[1]); 380 close(filter->pipe_fh[1]);
381 return 0; 381 return 0;
382} 382}
383 383
384int cgit_close_filter(struct cgit_filter *filter) 384int cgit_close_filter(struct cgit_filter *filter)
385{ 385{
386 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), 386 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
387 "Unable to restore STDOUT"); 387 "Unable to restore STDOUT");
388 close(filter->old_stdout); 388 close(filter->old_stdout);
389 if (filter->pid < 0) 389 if (filter->pid < 0)
390 return 0; 390 return 0;
391 waitpid(filter->pid, &filter->exitstatus, 0); 391 waitpid(filter->pid, &filter->exitstatus, 0);
392 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus)) 392 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus))
393 return 0; 393 return 0;
394 die("Subprocess %s exited abnormally", filter->cmd); 394 die("Subprocess %s exited abnormally", filter->cmd);
395} 395}
396 396
397/* Read the content of the specified file into a newly allocated buffer, 397/* Read the content of the specified file into a newly allocated buffer,
398 * zeroterminate the buffer and return 0 on success, errno otherwise. 398 * zeroterminate the buffer and return 0 on success, errno otherwise.
399 */ 399 */
400int readfile(const char *path, char **buf, size_t *size) 400int readfile(const char *path, char **buf, size_t *size)
401{ 401{
402 int fd; 402 int fd;
403 struct stat st; 403 struct stat st;
404 404
405 fd = open(path, O_RDONLY); 405 fd = open(path, O_RDONLY);
406 if (fd == -1) 406 if (fd == -1)
407 return errno; 407 return errno;
408 if (fstat(fd, &st)) 408 if (fstat(fd, &st))
409 return errno; 409 return errno;
410 if (!S_ISREG(st.st_mode)) 410 if (!S_ISREG(st.st_mode))
411 return EISDIR; 411 return EISDIR;
412 *buf = xmalloc(st.st_size + 1); 412 *buf = xmalloc(st.st_size + 1);
413 *size = read_in_full(fd, *buf, st.st_size); 413 *size = read_in_full(fd, *buf, st.st_size);
414 (*buf)[*size] = '\0'; 414 (*buf)[*size] = '\0';
415 return (*size == st.st_size ? 0 : errno); 415 return (*size == st.st_size ? 0 : errno);
416} 416}
diff --git a/ui-repolist.c b/ui-repolist.c
index 7c7aa9b..4dea3b3 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -1,285 +1,285 @@
1/* ui-repolist.c: functions for generating the repolist page 1/* ui-repolist.c: functions for generating the repolist page
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/* This is needed for strcasestr to be defined by <string.h> */ 9/* This is needed for strcasestr to be defined by <string.h> */
10#define _GNU_SOURCE 1 10#define _GNU_SOURCE 1
11#include <string.h> 11#include <string.h>
12 12
13#include <time.h> 13#include <time.h>
14 14
15#include "cgit.h" 15#include "cgit.h"
16#include "html.h" 16#include "html.h"
17#include "ui-shared.h" 17#include "ui-shared.h"
18 18
19time_t read_agefile(char *path) 19time_t read_agefile(char *path)
20{ 20{
21 time_t result; 21 time_t result;
22 size_t size; 22 size_t size;
23 char *buf; 23 char *buf;
24 static char buf2[64]; 24 static char buf2[64];
25 25
26 if (readfile(path, &buf, &size)) 26 if (readfile(path, &buf, &size))
27 return -1; 27 return -1;
28 28
29 if (parse_date(buf, buf2, sizeof(buf2))) 29 if (parse_date(buf, buf2, sizeof(buf2)))
30 result = strtoul(buf2, NULL, 10); 30 result = strtoul(buf2, NULL, 10);
31 else 31 else
32 result = 0; 32 result = 0;
33 free(buf); 33 free(buf);
34 return result; 34 return result;
35} 35}
36 36
37static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) 37static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
38{ 38{
39 char *path; 39 char *path;
40 struct stat s; 40 struct stat s;
41 struct cgit_repo *r = (struct cgit_repo *)repo; 41 struct cgit_repo *r = (struct cgit_repo *)repo;
42 42
43 if (repo->mtime != -1) { 43 if (repo->mtime != -1) {
44 *mtime = repo->mtime; 44 *mtime = repo->mtime;
45 return 1; 45 return 1;
46 } 46 }
47 path = fmt("%s/%s", repo->path, ctx.cfg.agefile); 47 path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
48 if (stat(path, &s) == 0) { 48 if (stat(path, &s) == 0) {
49 *mtime = read_agefile(path); 49 *mtime = read_agefile(path);
50 r->mtime = *mtime; 50 r->mtime = *mtime;
51 return 1; 51 return 1;
52 } 52 }
53 53
54 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch); 54 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
55 if (stat(path, &s) == 0) 55 if (stat(path, &s) == 0)
56 *mtime = s.st_mtime; 56 *mtime = s.st_mtime;
57 else 57 else
58 *mtime = 0; 58 *mtime = 0;
59 59
60 r->mtime = *mtime; 60 r->mtime = *mtime;
61 return (r->mtime != 0); 61 return (r->mtime != 0);
62} 62}
63 63
64static void print_modtime(struct cgit_repo *repo) 64static void print_modtime(struct cgit_repo *repo)
65{ 65{
66 time_t t; 66 time_t t;
67 if (get_repo_modtime(repo, &t)) 67 if (get_repo_modtime(repo, &t))
68 cgit_print_age(t, -1, NULL); 68 cgit_print_age(t, -1, NULL);
69} 69}
70 70
71int is_match(struct cgit_repo *repo) 71int is_match(struct cgit_repo *repo)
72{ 72{
73 if (!ctx.qry.search) 73 if (!ctx.qry.search)
74 return 1; 74 return 1;
75 if (repo->url && strcasestr(repo->url, ctx.qry.search)) 75 if (repo->url && strcasestr(repo->url, ctx.qry.search))
76 return 1; 76 return 1;
77 if (repo->name && strcasestr(repo->name, ctx.qry.search)) 77 if (repo->name && strcasestr(repo->name, ctx.qry.search))
78 return 1; 78 return 1;
79 if (repo->desc && strcasestr(repo->desc, ctx.qry.search)) 79 if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
80 return 1; 80 return 1;
81 if (repo->owner && strcasestr(repo->owner, ctx.qry.search)) 81 if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
82 return 1; 82 return 1;
83 return 0; 83 return 0;
84} 84}
85 85
86int is_in_url(struct cgit_repo *repo) 86int is_in_url(struct cgit_repo *repo)
87{ 87{
88 if (!ctx.qry.url) 88 if (!ctx.qry.url)
89 return 1; 89 return 1;
90 if (repo->url && !prefixcmp(repo->url, ctx.qry.url)) 90 if (repo->url && !prefixcmp(repo->url, ctx.qry.url))
91 return 1; 91 return 1;
92 return 0; 92 return 0;
93} 93}
94 94
95void print_sort_header(const char *title, const char *sort) 95void print_sort_header(const char *title, const char *sort)
96{ 96{
97 htmlf("<th class='left'><a href='./?s=%s", sort); 97 htmlf("<th class='left'><a href='./?s=%s", sort);
98 if (ctx.qry.search) { 98 if (ctx.qry.search) {
99 html("&q="); 99 html("&q=");
100 html_url_arg(ctx.qry.search); 100 html_url_arg(ctx.qry.search);
101 } 101 }
102 htmlf("'>%s</a></th>", title); 102 htmlf("'>%s</a></th>", title);
103} 103}
104 104
105void print_header(int columns) 105void print_header(int columns)
106{ 106{
107 html("<tr class='nohover'>"); 107 html("<tr class='nohover'>");
108 print_sort_header("Name", "name"); 108 print_sort_header("Name", "name");
109 print_sort_header("Description", "desc"); 109 print_sort_header("Description", "desc");
110 print_sort_header("Owner", "owner"); 110 print_sort_header("Owner", "owner");
111 print_sort_header("Idle", "idle"); 111 print_sort_header("Idle", "idle");
112 if (ctx.cfg.enable_index_links) 112 if (ctx.cfg.enable_index_links)
113 html("<th class='left'>Links</th>"); 113 html("<th class='left'>Links</th>");
114 html("</tr>\n"); 114 html("</tr>\n");
115} 115}
116 116
117 117
118void print_pager(int items, int pagelen, char *search) 118void print_pager(int items, int pagelen, char *search)
119{ 119{
120 int i; 120 int i;
121 html("<div class='pager'>"); 121 html("<div class='pager'>");
122 for(i = 0; i * pagelen < items; i++) 122 for(i = 0; i * pagelen < items; i++)
123 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL, 123 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL,
124 search, i * pagelen); 124 search, i * pagelen);
125 html("</div>"); 125 html("</div>");
126} 126}
127 127
128static int cmp(const char *s1, const char *s2) 128static int cmp(const char *s1, const char *s2)
129{ 129{
130 if (s1 && s2) 130 if (s1 && s2)
131 return strcmp(s1, s2); 131 return strcmp(s1, s2);
132 if (s1 && !s2) 132 if (s1 && !s2)
133 return -1; 133 return -1;
134 if (s2 && !s1) 134 if (s2 && !s1)
135 return 1; 135 return 1;
136 return 0; 136 return 0;
137} 137}
138 138
139static int sort_name(const void *a, const void *b) 139static int sort_name(const void *a, const void *b)
140{ 140{
141 const struct cgit_repo *r1 = a; 141 const struct cgit_repo *r1 = a;
142 const struct cgit_repo *r2 = b; 142 const struct cgit_repo *r2 = b;
143 143
144 return cmp(r1->name, r2->name); 144 return cmp(r1->name, r2->name);
145} 145}
146 146
147static int sort_desc(const void *a, const void *b) 147static int sort_desc(const void *a, const void *b)
148{ 148{
149 const struct cgit_repo *r1 = a; 149 const struct cgit_repo *r1 = a;
150 const struct cgit_repo *r2 = b; 150 const struct cgit_repo *r2 = b;
151 151
152 return cmp(r1->desc, r2->desc); 152 return cmp(r1->desc, r2->desc);
153} 153}
154 154
155static int sort_owner(const void *a, const void *b) 155static int sort_owner(const void *a, const void *b)
156{ 156{
157 const struct cgit_repo *r1 = a; 157 const struct cgit_repo *r1 = a;
158 const struct cgit_repo *r2 = b; 158 const struct cgit_repo *r2 = b;
159 159
160 return cmp(r1->owner, r2->owner); 160 return cmp(r1->owner, r2->owner);
161} 161}
162 162
163static int sort_idle(const void *a, const void *b) 163static int sort_idle(const void *a, const void *b)
164{ 164{
165 const struct cgit_repo *r1 = a; 165 const struct cgit_repo *r1 = a;
166 const struct cgit_repo *r2 = b; 166 const struct cgit_repo *r2 = b;
167 time_t t1, t2; 167 time_t t1, t2;
168 168
169 t1 = t2 = 0; 169 t1 = t2 = 0;
170 get_repo_modtime(r1, &t1); 170 get_repo_modtime(r1, &t1);
171 get_repo_modtime(r2, &t2); 171 get_repo_modtime(r2, &t2);
172 return t2 - t1; 172 return t2 - t1;
173} 173}
174 174
175struct sortcolumn { 175struct sortcolumn {
176 const char *name; 176 const char *name;
177 int (*fn)(const void *a, const void *b); 177 int (*fn)(const void *a, const void *b);
178}; 178};
179 179
180struct sortcolumn sortcolumn[] = { 180struct sortcolumn sortcolumn[] = {
181 {"name", sort_name}, 181 {"name", sort_name},
182 {"desc", sort_desc}, 182 {"desc", sort_desc},
183 {"owner", sort_owner}, 183 {"owner", sort_owner},
184 {"idle", sort_idle}, 184 {"idle", sort_idle},
185 {NULL, NULL} 185 {NULL, NULL}
186}; 186};
187 187
188int sort_repolist(char *field) 188int sort_repolist(char *field)
189{ 189{
190 struct sortcolumn *column; 190 struct sortcolumn *column;
191 191
192 for (column = &sortcolumn[0]; column->name; column++) { 192 for (column = &sortcolumn[0]; column->name; column++) {
193 if (strcmp(field, column->name)) 193 if (strcmp(field, column->name))
194 continue; 194 continue;
195 qsort(cgit_repolist.repos, cgit_repolist.count, 195 qsort(cgit_repolist.repos, cgit_repolist.count,
196 sizeof(struct cgit_repo), column->fn); 196 sizeof(struct cgit_repo), column->fn);
197 return 1; 197 return 1;
198 } 198 }
199 return 0; 199 return 0;
200} 200}
201 201
202 202
203void cgit_print_repolist() 203void cgit_print_repolist()
204{ 204{
205 int i, columns = 4, hits = 0, header = 0; 205 int i, columns = 4, hits = 0, header = 0;
206 char *last_group = NULL; 206 char *last_section = NULL;
207 int sorted = 0; 207 int sorted = 0;
208 208
209 if (ctx.cfg.enable_index_links) 209 if (ctx.cfg.enable_index_links)
210 columns++; 210 columns++;
211 211
212 ctx.page.title = ctx.cfg.root_title; 212 ctx.page.title = ctx.cfg.root_title;
213 cgit_print_http_headers(&ctx); 213 cgit_print_http_headers(&ctx);
214 cgit_print_docstart(&ctx); 214 cgit_print_docstart(&ctx);
215 cgit_print_pageheader(&ctx); 215 cgit_print_pageheader(&ctx);
216 216
217 if (ctx.cfg.index_header) 217 if (ctx.cfg.index_header)
218 html_include(ctx.cfg.index_header); 218 html_include(ctx.cfg.index_header);
219 219
220 if(ctx.qry.sort) 220 if(ctx.qry.sort)
221 sorted = sort_repolist(ctx.qry.sort); 221 sorted = sort_repolist(ctx.qry.sort);
222 222
223 html("<table summary='repository list' class='list nowrap'>"); 223 html("<table summary='repository list' class='list nowrap'>");
224 for (i=0; i<cgit_repolist.count; i++) { 224 for (i=0; i<cgit_repolist.count; i++) {
225 ctx.repo = &cgit_repolist.repos[i]; 225 ctx.repo = &cgit_repolist.repos[i];
226 if (!(is_match(ctx.repo) && is_in_url(ctx.repo))) 226 if (!(is_match(ctx.repo) && is_in_url(ctx.repo)))
227 continue; 227 continue;
228 hits++; 228 hits++;
229 if (hits <= ctx.qry.ofs) 229 if (hits <= ctx.qry.ofs)
230 continue; 230 continue;
231 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count) 231 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
232 continue; 232 continue;
233 if (!header++) 233 if (!header++)
234 print_header(columns); 234 print_header(columns);
235 if (!sorted && 235 if (!sorted &&
236 ((last_group == NULL && ctx.repo->group != NULL) || 236 ((last_section == NULL && ctx.repo->section != NULL) ||
237 (last_group != NULL && ctx.repo->group == NULL) || 237 (last_section != NULL && ctx.repo->section == NULL) ||
238 (last_group != NULL && ctx.repo->group != NULL && 238 (last_section != NULL && ctx.repo->section != NULL &&
239 strcmp(ctx.repo->group, last_group)))) { 239 strcmp(ctx.repo->section, last_section)))) {
240 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", 240 htmlf("<tr class='nohover'><td colspan='%d' class='reposection'>",
241 columns); 241 columns);
242 html_txt(ctx.repo->group); 242 html_txt(ctx.repo->section);
243 html("</td></tr>"); 243 html("</td></tr>");
244 last_group = ctx.repo->group; 244 last_section = ctx.repo->section;
245 } 245 }
246 htmlf("<tr><td class='%s'>", 246 htmlf("<tr><td class='%s'>",
247 !sorted && ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); 247 !sorted && ctx.repo->section ? "sublevel-repo" : "toplevel-repo");
248 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); 248 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
249 html("</td><td>"); 249 html("</td><td>");
250 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); 250 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
251 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); 251 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
252 html_link_close(); 252 html_link_close();
253 html("</td><td>"); 253 html("</td><td>");
254 html_txt(ctx.repo->owner); 254 html_txt(ctx.repo->owner);
255 html("</td><td>"); 255 html("</td><td>");
256 print_modtime(ctx.repo); 256 print_modtime(ctx.repo);
257 html("</td>"); 257 html("</td>");
258 if (ctx.cfg.enable_index_links) { 258 if (ctx.cfg.enable_index_links) {
259 html("<td>"); 259 html("<td>");
260 cgit_summary_link("summary", NULL, "button", NULL); 260 cgit_summary_link("summary", NULL, "button", NULL);
261 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 261 cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
262 0, NULL, NULL, ctx.qry.showmsg); 262 0, NULL, NULL, ctx.qry.showmsg);
263 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); 263 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
264 html("</td>"); 264 html("</td>");
265 } 265 }
266 html("</tr>\n"); 266 html("</tr>\n");
267 } 267 }
268 html("</table>"); 268 html("</table>");
269 if (!hits) 269 if (!hits)
270 cgit_print_error("No repositories found"); 270 cgit_print_error("No repositories found");
271 else if (hits > ctx.cfg.max_repo_count) 271 else if (hits > ctx.cfg.max_repo_count)
272 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search); 272 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search);
273 cgit_print_docend(); 273 cgit_print_docend();
274} 274}
275 275
276void cgit_print_site_readme() 276void cgit_print_site_readme()
277{ 277{
278 if (!ctx.cfg.root_readme) 278 if (!ctx.cfg.root_readme)
279 return; 279 return;
280 if (ctx.cfg.about_filter) 280 if (ctx.cfg.about_filter)
281 cgit_open_filter(ctx.cfg.about_filter); 281 cgit_open_filter(ctx.cfg.about_filter);
282 html_include(ctx.cfg.root_readme); 282 html_include(ctx.cfg.root_readme);
283 if (ctx.cfg.about_filter) 283 if (ctx.cfg.about_filter)
284 cgit_close_filter(ctx.cfg.about_filter); 284 cgit_close_filter(ctx.cfg.about_filter);
285} 285}