summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c3
-rw-r--r--cgit.css5
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt4
-rwxr-xr-xfilters/syntax-highlighting.sh29
-rw-r--r--ui-tree.c6
6 files changed, 31 insertions, 17 deletions
diff --git a/cgit.c b/cgit.c
index bfde9f9..08cb5d2 100644
--- a/cgit.c
+++ b/cgit.c
@@ -72,297 +72,300 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
72 else 72 else
73 repo->readme = xstrdup(fmt("%s/%s", repo->path, value)); 73 repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
74 } else if (ctx.cfg.enable_filter_overrides) { 74 } else if (ctx.cfg.enable_filter_overrides) {
75 if (!strcmp(name, "about-filter")) 75 if (!strcmp(name, "about-filter"))
76 repo->about_filter = new_filter(value, 0); 76 repo->about_filter = new_filter(value, 0);
77 else if (!strcmp(name, "commit-filter")) 77 else if (!strcmp(name, "commit-filter"))
78 repo->commit_filter = new_filter(value, 0); 78 repo->commit_filter = new_filter(value, 0);
79 else if (!strcmp(name, "source-filter")) 79 else if (!strcmp(name, "source-filter"))
80 repo->source_filter = new_filter(value, 1); 80 repo->source_filter = new_filter(value, 1);
81 } 81 }
82} 82}
83 83
84void config_cb(const char *name, const char *value) 84void config_cb(const char *name, const char *value)
85{ 85{
86 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 86 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
87 ctx.cfg.section = xstrdup(value); 87 ctx.cfg.section = xstrdup(value);
88 else if (!strcmp(name, "repo.url")) 88 else if (!strcmp(name, "repo.url"))
89 ctx.repo = cgit_add_repo(value); 89 ctx.repo = cgit_add_repo(value);
90 else if (ctx.repo && !strcmp(name, "repo.path")) 90 else if (ctx.repo && !strcmp(name, "repo.path"))
91 ctx.repo->path = trim_end(value, '/'); 91 ctx.repo->path = trim_end(value, '/');
92 else if (ctx.repo && !prefixcmp(name, "repo.")) 92 else if (ctx.repo && !prefixcmp(name, "repo."))
93 repo_config(ctx.repo, name + 5, value); 93 repo_config(ctx.repo, name + 5, value);
94 else if (!strcmp(name, "root-title")) 94 else if (!strcmp(name, "root-title"))
95 ctx.cfg.root_title = xstrdup(value); 95 ctx.cfg.root_title = xstrdup(value);
96 else if (!strcmp(name, "root-desc")) 96 else if (!strcmp(name, "root-desc"))
97 ctx.cfg.root_desc = xstrdup(value); 97 ctx.cfg.root_desc = xstrdup(value);
98 else if (!strcmp(name, "root-readme")) 98 else if (!strcmp(name, "root-readme"))
99 ctx.cfg.root_readme = xstrdup(value); 99 ctx.cfg.root_readme = xstrdup(value);
100 else if (!strcmp(name, "css")) 100 else if (!strcmp(name, "css"))
101 ctx.cfg.css = xstrdup(value); 101 ctx.cfg.css = xstrdup(value);
102 else if (!strcmp(name, "favicon")) 102 else if (!strcmp(name, "favicon"))
103 ctx.cfg.favicon = xstrdup(value); 103 ctx.cfg.favicon = xstrdup(value);
104 else if (!strcmp(name, "footer")) 104 else if (!strcmp(name, "footer"))
105 ctx.cfg.footer = xstrdup(value); 105 ctx.cfg.footer = xstrdup(value);
106 else if (!strcmp(name, "head-include")) 106 else if (!strcmp(name, "head-include"))
107 ctx.cfg.head_include = xstrdup(value); 107 ctx.cfg.head_include = xstrdup(value);
108 else if (!strcmp(name, "header")) 108 else if (!strcmp(name, "header"))
109 ctx.cfg.header = xstrdup(value); 109 ctx.cfg.header = xstrdup(value);
110 else if (!strcmp(name, "logo")) 110 else if (!strcmp(name, "logo"))
111 ctx.cfg.logo = xstrdup(value); 111 ctx.cfg.logo = xstrdup(value);
112 else if (!strcmp(name, "index-header")) 112 else if (!strcmp(name, "index-header"))
113 ctx.cfg.index_header = xstrdup(value); 113 ctx.cfg.index_header = xstrdup(value);
114 else if (!strcmp(name, "index-info")) 114 else if (!strcmp(name, "index-info"))
115 ctx.cfg.index_info = xstrdup(value); 115 ctx.cfg.index_info = xstrdup(value);
116 else if (!strcmp(name, "logo-link")) 116 else if (!strcmp(name, "logo-link"))
117 ctx.cfg.logo_link = xstrdup(value); 117 ctx.cfg.logo_link = xstrdup(value);
118 else if (!strcmp(name, "module-link")) 118 else if (!strcmp(name, "module-link"))
119 ctx.cfg.module_link = xstrdup(value); 119 ctx.cfg.module_link = xstrdup(value);
120 else if (!strcmp(name, "virtual-root")) { 120 else if (!strcmp(name, "virtual-root")) {
121 ctx.cfg.virtual_root = trim_end(value, '/'); 121 ctx.cfg.virtual_root = trim_end(value, '/');
122 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 122 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
123 ctx.cfg.virtual_root = ""; 123 ctx.cfg.virtual_root = "";
124 } else if (!strcmp(name, "nocache")) 124 } else if (!strcmp(name, "nocache"))
125 ctx.cfg.nocache = atoi(value); 125 ctx.cfg.nocache = atoi(value);
126 else if (!strcmp(name, "noplainemail")) 126 else if (!strcmp(name, "noplainemail"))
127 ctx.cfg.noplainemail = atoi(value); 127 ctx.cfg.noplainemail = atoi(value);
128 else if (!strcmp(name, "noheader")) 128 else if (!strcmp(name, "noheader"))
129 ctx.cfg.noheader = atoi(value); 129 ctx.cfg.noheader = atoi(value);
130 else if (!strcmp(name, "snapshots")) 130 else if (!strcmp(name, "snapshots"))
131 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 131 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
132 else if (!strcmp(name, "enable-filter-overrides")) 132 else if (!strcmp(name, "enable-filter-overrides"))
133 ctx.cfg.enable_filter_overrides = atoi(value); 133 ctx.cfg.enable_filter_overrides = atoi(value);
134 else if (!strcmp(name, "enable-index-links")) 134 else if (!strcmp(name, "enable-index-links"))
135 ctx.cfg.enable_index_links = atoi(value); 135 ctx.cfg.enable_index_links = atoi(value);
136 else if (!strcmp(name, "enable-log-filecount")) 136 else if (!strcmp(name, "enable-log-filecount"))
137 ctx.cfg.enable_log_filecount = atoi(value); 137 ctx.cfg.enable_log_filecount = atoi(value);
138 else if (!strcmp(name, "enable-log-linecount")) 138 else if (!strcmp(name, "enable-log-linecount"))
139 ctx.cfg.enable_log_linecount = atoi(value); 139 ctx.cfg.enable_log_linecount = atoi(value);
140 else if (!strcmp(name, "enable-tree-linenumbers")) 140 else if (!strcmp(name, "enable-tree-linenumbers"))
141 ctx.cfg.enable_tree_linenumbers = atoi(value); 141 ctx.cfg.enable_tree_linenumbers = atoi(value);
142 else if (!strcmp(name, "max-stats")) 142 else if (!strcmp(name, "max-stats"))
143 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 143 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
144 else if (!strcmp(name, "cache-size")) 144 else if (!strcmp(name, "cache-size"))
145 ctx.cfg.cache_size = atoi(value); 145 ctx.cfg.cache_size = atoi(value);
146 else if (!strcmp(name, "cache-root")) 146 else if (!strcmp(name, "cache-root"))
147 ctx.cfg.cache_root = xstrdup(value); 147 ctx.cfg.cache_root = xstrdup(value);
148 else if (!strcmp(name, "cache-root-ttl")) 148 else if (!strcmp(name, "cache-root-ttl"))
149 ctx.cfg.cache_root_ttl = atoi(value); 149 ctx.cfg.cache_root_ttl = atoi(value);
150 else if (!strcmp(name, "cache-repo-ttl")) 150 else if (!strcmp(name, "cache-repo-ttl"))
151 ctx.cfg.cache_repo_ttl = atoi(value); 151 ctx.cfg.cache_repo_ttl = atoi(value);
152 else if (!strcmp(name, "cache-scanrc-ttl")) 152 else if (!strcmp(name, "cache-scanrc-ttl"))
153 ctx.cfg.cache_scanrc_ttl = atoi(value); 153 ctx.cfg.cache_scanrc_ttl = atoi(value);
154 else if (!strcmp(name, "cache-static-ttl")) 154 else if (!strcmp(name, "cache-static-ttl"))
155 ctx.cfg.cache_static_ttl = atoi(value); 155 ctx.cfg.cache_static_ttl = atoi(value);
156 else if (!strcmp(name, "cache-dynamic-ttl")) 156 else if (!strcmp(name, "cache-dynamic-ttl"))
157 ctx.cfg.cache_dynamic_ttl = atoi(value); 157 ctx.cfg.cache_dynamic_ttl = atoi(value);
158 else if (!strcmp(name, "about-filter")) 158 else if (!strcmp(name, "about-filter"))
159 ctx.cfg.about_filter = new_filter(value, 0); 159 ctx.cfg.about_filter = new_filter(value, 0);
160 else if (!strcmp(name, "commit-filter")) 160 else if (!strcmp(name, "commit-filter"))
161 ctx.cfg.commit_filter = new_filter(value, 0); 161 ctx.cfg.commit_filter = new_filter(value, 0);
162 else if (!strcmp(name, "embedded")) 162 else if (!strcmp(name, "embedded"))
163 ctx.cfg.embedded = atoi(value); 163 ctx.cfg.embedded = atoi(value);
164 else if (!strcmp(name, "max-message-length")) 164 else if (!strcmp(name, "max-message-length"))
165 ctx.cfg.max_msg_len = atoi(value); 165 ctx.cfg.max_msg_len = atoi(value);
166 else if (!strcmp(name, "max-repodesc-length")) 166 else if (!strcmp(name, "max-repodesc-length"))
167 ctx.cfg.max_repodesc_len = atoi(value); 167 ctx.cfg.max_repodesc_len = atoi(value);
168 else if (!strcmp(name, "max-blob-size"))
169 ctx.cfg.max_blob_size = atoi(value);
168 else if (!strcmp(name, "max-repo-count")) 170 else if (!strcmp(name, "max-repo-count"))
169 ctx.cfg.max_repo_count = atoi(value); 171 ctx.cfg.max_repo_count = atoi(value);
170 else if (!strcmp(name, "max-commit-count")) 172 else if (!strcmp(name, "max-commit-count"))
171 ctx.cfg.max_commit_count = atoi(value); 173 ctx.cfg.max_commit_count = atoi(value);
172 else if (!strcmp(name, "scan-path")) 174 else if (!strcmp(name, "scan-path"))
173 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 175 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
174 process_cached_repolist(value); 176 process_cached_repolist(value);
175 else 177 else
176 scan_tree(value, repo_config); 178 scan_tree(value, repo_config);
177 else if (!strcmp(name, "source-filter")) 179 else if (!strcmp(name, "source-filter"))
178 ctx.cfg.source_filter = new_filter(value, 1); 180 ctx.cfg.source_filter = new_filter(value, 1);
179 else if (!strcmp(name, "summary-log")) 181 else if (!strcmp(name, "summary-log"))
180 ctx.cfg.summary_log = atoi(value); 182 ctx.cfg.summary_log = atoi(value);
181 else if (!strcmp(name, "summary-branches")) 183 else if (!strcmp(name, "summary-branches"))
182 ctx.cfg.summary_branches = atoi(value); 184 ctx.cfg.summary_branches = atoi(value);
183 else if (!strcmp(name, "summary-tags")) 185 else if (!strcmp(name, "summary-tags"))
184 ctx.cfg.summary_tags = atoi(value); 186 ctx.cfg.summary_tags = atoi(value);
185 else if (!strcmp(name, "agefile")) 187 else if (!strcmp(name, "agefile"))
186 ctx.cfg.agefile = xstrdup(value); 188 ctx.cfg.agefile = xstrdup(value);
187 else if (!strcmp(name, "renamelimit")) 189 else if (!strcmp(name, "renamelimit"))
188 ctx.cfg.renamelimit = atoi(value); 190 ctx.cfg.renamelimit = atoi(value);
189 else if (!strcmp(name, "robots")) 191 else if (!strcmp(name, "robots"))
190 ctx.cfg.robots = xstrdup(value); 192 ctx.cfg.robots = xstrdup(value);
191 else if (!strcmp(name, "clone-prefix")) 193 else if (!strcmp(name, "clone-prefix"))
192 ctx.cfg.clone_prefix = xstrdup(value); 194 ctx.cfg.clone_prefix = xstrdup(value);
193 else if (!strcmp(name, "local-time")) 195 else if (!strcmp(name, "local-time"))
194 ctx.cfg.local_time = atoi(value); 196 ctx.cfg.local_time = atoi(value);
195 else if (!prefixcmp(name, "mimetype.")) 197 else if (!prefixcmp(name, "mimetype."))
196 add_mimetype(name + 9, value); 198 add_mimetype(name + 9, value);
197 else if (!strcmp(name, "include")) 199 else if (!strcmp(name, "include"))
198 parse_configfile(value, config_cb); 200 parse_configfile(value, config_cb);
199} 201}
200 202
201static void querystring_cb(const char *name, const char *value) 203static void querystring_cb(const char *name, const char *value)
202{ 204{
203 if (!value) 205 if (!value)
204 value = ""; 206 value = "";
205 207
206 if (!strcmp(name,"r")) { 208 if (!strcmp(name,"r")) {
207 ctx.qry.repo = xstrdup(value); 209 ctx.qry.repo = xstrdup(value);
208 ctx.repo = cgit_get_repoinfo(value); 210 ctx.repo = cgit_get_repoinfo(value);
209 } else if (!strcmp(name, "p")) { 211 } else if (!strcmp(name, "p")) {
210 ctx.qry.page = xstrdup(value); 212 ctx.qry.page = xstrdup(value);
211 } else if (!strcmp(name, "url")) { 213 } else if (!strcmp(name, "url")) {
212 if (*value == '/') 214 if (*value == '/')
213 value++; 215 value++;
214 ctx.qry.url = xstrdup(value); 216 ctx.qry.url = xstrdup(value);
215 cgit_parse_url(value); 217 cgit_parse_url(value);
216 } else if (!strcmp(name, "qt")) { 218 } else if (!strcmp(name, "qt")) {
217 ctx.qry.grep = xstrdup(value); 219 ctx.qry.grep = xstrdup(value);
218 } else if (!strcmp(name, "q")) { 220 } else if (!strcmp(name, "q")) {
219 ctx.qry.search = xstrdup(value); 221 ctx.qry.search = xstrdup(value);
220 } else if (!strcmp(name, "h")) { 222 } else if (!strcmp(name, "h")) {
221 ctx.qry.head = xstrdup(value); 223 ctx.qry.head = xstrdup(value);
222 ctx.qry.has_symref = 1; 224 ctx.qry.has_symref = 1;
223 } else if (!strcmp(name, "id")) { 225 } else if (!strcmp(name, "id")) {
224 ctx.qry.sha1 = xstrdup(value); 226 ctx.qry.sha1 = xstrdup(value);
225 ctx.qry.has_sha1 = 1; 227 ctx.qry.has_sha1 = 1;
226 } else if (!strcmp(name, "id2")) { 228 } else if (!strcmp(name, "id2")) {
227 ctx.qry.sha2 = xstrdup(value); 229 ctx.qry.sha2 = xstrdup(value);
228 ctx.qry.has_sha1 = 1; 230 ctx.qry.has_sha1 = 1;
229 } else if (!strcmp(name, "ofs")) { 231 } else if (!strcmp(name, "ofs")) {
230 ctx.qry.ofs = atoi(value); 232 ctx.qry.ofs = atoi(value);
231 } else if (!strcmp(name, "path")) { 233 } else if (!strcmp(name, "path")) {
232 ctx.qry.path = trim_end(value, '/'); 234 ctx.qry.path = trim_end(value, '/');
233 } else if (!strcmp(name, "name")) { 235 } else if (!strcmp(name, "name")) {
234 ctx.qry.name = xstrdup(value); 236 ctx.qry.name = xstrdup(value);
235 } else if (!strcmp(name, "mimetype")) { 237 } else if (!strcmp(name, "mimetype")) {
236 ctx.qry.mimetype = xstrdup(value); 238 ctx.qry.mimetype = xstrdup(value);
237 } else if (!strcmp(name, "s")){ 239 } else if (!strcmp(name, "s")){
238 ctx.qry.sort = xstrdup(value); 240 ctx.qry.sort = xstrdup(value);
239 } else if (!strcmp(name, "showmsg")) { 241 } else if (!strcmp(name, "showmsg")) {
240 ctx.qry.showmsg = atoi(value); 242 ctx.qry.showmsg = atoi(value);
241 } else if (!strcmp(name, "period")) { 243 } else if (!strcmp(name, "period")) {
242 ctx.qry.period = xstrdup(value); 244 ctx.qry.period = xstrdup(value);
243 } 245 }
244} 246}
245 247
246char *xstrdupn(const char *str) 248char *xstrdupn(const char *str)
247{ 249{
248 return (str ? xstrdup(str) : NULL); 250 return (str ? xstrdup(str) : NULL);
249} 251}
250 252
251static void prepare_context(struct cgit_context *ctx) 253static void prepare_context(struct cgit_context *ctx)
252{ 254{
253 memset(ctx, 0, sizeof(ctx)); 255 memset(ctx, 0, sizeof(ctx));
254 ctx->cfg.agefile = "info/web/last-modified"; 256 ctx->cfg.agefile = "info/web/last-modified";
255 ctx->cfg.nocache = 0; 257 ctx->cfg.nocache = 0;
256 ctx->cfg.cache_size = 0; 258 ctx->cfg.cache_size = 0;
257 ctx->cfg.cache_dynamic_ttl = 5; 259 ctx->cfg.cache_dynamic_ttl = 5;
258 ctx->cfg.cache_max_create_time = 5; 260 ctx->cfg.cache_max_create_time = 5;
259 ctx->cfg.cache_repo_ttl = 5; 261 ctx->cfg.cache_repo_ttl = 5;
260 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 262 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
261 ctx->cfg.cache_root_ttl = 5; 263 ctx->cfg.cache_root_ttl = 5;
262 ctx->cfg.cache_scanrc_ttl = 15; 264 ctx->cfg.cache_scanrc_ttl = 15;
263 ctx->cfg.cache_static_ttl = -1; 265 ctx->cfg.cache_static_ttl = -1;
264 ctx->cfg.css = "/cgit.css"; 266 ctx->cfg.css = "/cgit.css";
265 ctx->cfg.logo = "/cgit.png"; 267 ctx->cfg.logo = "/cgit.png";
266 ctx->cfg.local_time = 0; 268 ctx->cfg.local_time = 0;
267 ctx->cfg.enable_tree_linenumbers = 1; 269 ctx->cfg.enable_tree_linenumbers = 1;
268 ctx->cfg.max_repo_count = 50; 270 ctx->cfg.max_repo_count = 50;
269 ctx->cfg.max_commit_count = 50; 271 ctx->cfg.max_commit_count = 50;
270 ctx->cfg.max_lock_attempts = 5; 272 ctx->cfg.max_lock_attempts = 5;
271 ctx->cfg.max_msg_len = 80; 273 ctx->cfg.max_msg_len = 80;
272 ctx->cfg.max_repodesc_len = 80; 274 ctx->cfg.max_repodesc_len = 80;
275 ctx->cfg.max_blob_size = 0;
273 ctx->cfg.max_stats = 0; 276 ctx->cfg.max_stats = 0;
274 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 277 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
275 ctx->cfg.renamelimit = -1; 278 ctx->cfg.renamelimit = -1;
276 ctx->cfg.robots = "index, nofollow"; 279 ctx->cfg.robots = "index, nofollow";
277 ctx->cfg.root_title = "Git repository browser"; 280 ctx->cfg.root_title = "Git repository browser";
278 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 281 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
279 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 282 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
280 ctx->cfg.section = ""; 283 ctx->cfg.section = "";
281 ctx->cfg.summary_branches = 10; 284 ctx->cfg.summary_branches = 10;
282 ctx->cfg.summary_log = 10; 285 ctx->cfg.summary_log = 10;
283 ctx->cfg.summary_tags = 10; 286 ctx->cfg.summary_tags = 10;
284 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 287 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
285 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 288 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
286 ctx->env.https = xstrdupn(getenv("HTTPS")); 289 ctx->env.https = xstrdupn(getenv("HTTPS"));
287 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 290 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
288 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 291 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
289 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 292 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
290 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 293 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
291 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 294 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
292 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 295 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
293 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 296 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
294 ctx->page.mimetype = "text/html"; 297 ctx->page.mimetype = "text/html";
295 ctx->page.charset = PAGE_ENCODING; 298 ctx->page.charset = PAGE_ENCODING;
296 ctx->page.filename = NULL; 299 ctx->page.filename = NULL;
297 ctx->page.size = 0; 300 ctx->page.size = 0;
298 ctx->page.modified = time(NULL); 301 ctx->page.modified = time(NULL);
299 ctx->page.expires = ctx->page.modified; 302 ctx->page.expires = ctx->page.modified;
300 ctx->page.etag = NULL; 303 ctx->page.etag = NULL;
301 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 304 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
302 if (ctx->env.script_name) 305 if (ctx->env.script_name)
303 ctx->cfg.script_name = ctx->env.script_name; 306 ctx->cfg.script_name = ctx->env.script_name;
304 if (ctx->env.query_string) 307 if (ctx->env.query_string)
305 ctx->qry.raw = ctx->env.query_string; 308 ctx->qry.raw = ctx->env.query_string;
306 if (!ctx->env.cgit_config) 309 if (!ctx->env.cgit_config)
307 ctx->env.cgit_config = CGIT_CONFIG; 310 ctx->env.cgit_config = CGIT_CONFIG;
308} 311}
309 312
310struct refmatch { 313struct refmatch {
311 char *req_ref; 314 char *req_ref;
312 char *first_ref; 315 char *first_ref;
313 int match; 316 int match;
314}; 317};
315 318
316int find_current_ref(const char *refname, const unsigned char *sha1, 319int find_current_ref(const char *refname, const unsigned char *sha1,
317 int flags, void *cb_data) 320 int flags, void *cb_data)
318{ 321{
319 struct refmatch *info; 322 struct refmatch *info;
320 323
321 info = (struct refmatch *)cb_data; 324 info = (struct refmatch *)cb_data;
322 if (!strcmp(refname, info->req_ref)) 325 if (!strcmp(refname, info->req_ref))
323 info->match = 1; 326 info->match = 1;
324 if (!info->first_ref) 327 if (!info->first_ref)
325 info->first_ref = xstrdup(refname); 328 info->first_ref = xstrdup(refname);
326 return info->match; 329 return info->match;
327} 330}
328 331
329char *find_default_branch(struct cgit_repo *repo) 332char *find_default_branch(struct cgit_repo *repo)
330{ 333{
331 struct refmatch info; 334 struct refmatch info;
332 char *ref; 335 char *ref;
333 336
334 info.req_ref = repo->defbranch; 337 info.req_ref = repo->defbranch;
335 info.first_ref = NULL; 338 info.first_ref = NULL;
336 info.match = 0; 339 info.match = 0;
337 for_each_branch_ref(find_current_ref, &info); 340 for_each_branch_ref(find_current_ref, &info);
338 if (info.match) 341 if (info.match)
339 ref = info.req_ref; 342 ref = info.req_ref;
340 else 343 else
341 ref = info.first_ref; 344 ref = info.first_ref;
342 if (ref) 345 if (ref)
343 ref = xstrdup(ref); 346 ref = xstrdup(ref);
344 return ref; 347 return ref;
345} 348}
346 349
347static int prepare_repo_cmd(struct cgit_context *ctx) 350static int prepare_repo_cmd(struct cgit_context *ctx)
348{ 351{
349 char *tmp; 352 char *tmp;
350 unsigned char sha1[20]; 353 unsigned char sha1[20];
351 int nongit = 0; 354 int nongit = 0;
352 355
353 setenv("GIT_DIR", ctx->repo->path, 1); 356 setenv("GIT_DIR", ctx->repo->path, 1);
354 setup_git_directory_gently(&nongit); 357 setup_git_directory_gently(&nongit);
355 if (nongit) { 358 if (nongit) {
356 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 359 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
357 "config error"); 360 "config error");
358 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 361 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
359 ctx->repo = NULL; 362 ctx->repo = NULL;
360 cgit_print_http_headers(ctx); 363 cgit_print_http_headers(ctx);
361 cgit_print_docstart(ctx); 364 cgit_print_docstart(ctx);
362 cgit_print_pageheader(ctx); 365 cgit_print_pageheader(ctx);
363 cgit_print_error(tmp); 366 cgit_print_error(tmp);
364 cgit_print_docend(); 367 cgit_print_docend();
365 return 1; 368 return 1;
366 } 369 }
367 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 370 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
368 371
diff --git a/cgit.css b/cgit.css
index c47ebc9..ef7d3c1 100644
--- a/cgit.css
+++ b/cgit.css
@@ -69,192 +69,197 @@ table.tabs {
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.ls-dir {
166 font-weight: bold;
167 color: #00f;
168}
169
165table.list td a:hover { 170table.list td a:hover {
166 color: #00f; 171 color: #00f;
167} 172}
168 173
169img { 174img {
170 border: none; 175 border: none;
171} 176}
172 177
173input#switch-btn { 178input#switch-btn {
174 margin: 2px 0px 0px 0px; 179 margin: 2px 0px 0px 0px;
175} 180}
176 181
177td#sidebar input.txt { 182td#sidebar input.txt {
178 width: 100%; 183 width: 100%;
179 margin: 2px 0px 0px 0px; 184 margin: 2px 0px 0px 0px;
180} 185}
181 186
182table#grid { 187table#grid {
183 margin: 0px; 188 margin: 0px;
184} 189}
185 190
186td#content { 191td#content {
187 vertical-align: top; 192 vertical-align: top;
188 padding: 1em 2em 1em 1em; 193 padding: 1em 2em 1em 1em;
189 border: none; 194 border: none;
190} 195}
191 196
192div#summary { 197div#summary {
193 vertical-align: top; 198 vertical-align: top;
194 margin-bottom: 1em; 199 margin-bottom: 1em;
195} 200}
196 201
197table#downloads { 202table#downloads {
198 float: right; 203 float: right;
199 border-collapse: collapse; 204 border-collapse: collapse;
200 border: solid 1px #777; 205 border: solid 1px #777;
201 margin-left: 0.5em; 206 margin-left: 0.5em;
202 margin-bottom: 0.5em; 207 margin-bottom: 0.5em;
203} 208}
204 209
205table#downloads th { 210table#downloads th {
206 background-color: #ccc; 211 background-color: #ccc;
207} 212}
208 213
209div#blob { 214div#blob {
210 border: solid 1px black; 215 border: solid 1px black;
211} 216}
212 217
213div.error { 218div.error {
214 color: red; 219 color: red;
215 font-weight: bold; 220 font-weight: bold;
216 margin: 1em 2em; 221 margin: 1em 2em;
217} 222}
218 223
219a.ls-blob, a.ls-dir, a.ls-mod { 224a.ls-blob, a.ls-dir, a.ls-mod {
220 font-family: monospace; 225 font-family: monospace;
221} 226}
222 227
223td.ls-size { 228td.ls-size {
224 text-align: right; 229 text-align: right;
225 font-family: monospace; 230 font-family: monospace;
226 width: 10em; 231 width: 10em;
227} 232}
228 233
229td.ls-mode { 234td.ls-mode {
230 font-family: monospace; 235 font-family: monospace;
231 width: 10em; 236 width: 10em;
232} 237}
233 238
234table.blob { 239table.blob {
235 margin-top: 0.5em; 240 margin-top: 0.5em;
236 border-top: solid 1px black; 241 border-top: solid 1px black;
237} 242}
238 243
239table.blob td.lines { 244table.blob td.lines {
240 margin: 0; padding: 0 0 0 0.5em; 245 margin: 0; padding: 0 0 0 0.5em;
241 vertical-align: top; 246 vertical-align: top;
242 color: black; 247 color: black;
243} 248}
244 249
245table.blob td.linenumbers { 250table.blob td.linenumbers {
246 margin: 0; padding: 0 0.5em 0 0.5em; 251 margin: 0; padding: 0 0.5em 0 0.5em;
247 vertical-align: top; 252 vertical-align: top;
248 text-align: right; 253 text-align: right;
249 border-right: 1px solid gray; 254 border-right: 1px solid gray;
250} 255}
251 256
252table.blob pre { 257table.blob pre {
253 padding: 0; margin: 0; 258 padding: 0; margin: 0;
254} 259}
255 260
256table.blob a.no { 261table.blob a.no {
257 color: gray; 262 color: gray;
258 text-align: right; 263 text-align: right;
259 text-decoration: none; 264 text-decoration: none;
260} 265}
diff --git a/cgit.h b/cgit.h
index 6c6c460..39853df 100644
--- a/cgit.h
+++ b/cgit.h
@@ -92,192 +92,193 @@ struct commitinfo {
92 struct commit *commit; 92 struct commit *commit;
93 char *author; 93 char *author;
94 char *author_email; 94 char *author_email;
95 unsigned long author_date; 95 unsigned long author_date;
96 char *committer; 96 char *committer;
97 char *committer_email; 97 char *committer_email;
98 unsigned long committer_date; 98 unsigned long committer_date;
99 char *subject; 99 char *subject;
100 char *msg; 100 char *msg;
101 char *msg_encoding; 101 char *msg_encoding;
102}; 102};
103 103
104struct taginfo { 104struct taginfo {
105 char *tagger; 105 char *tagger;
106 char *tagger_email; 106 char *tagger_email;
107 unsigned long tagger_date; 107 unsigned long tagger_date;
108 char *msg; 108 char *msg;
109}; 109};
110 110
111struct refinfo { 111struct refinfo {
112 const char *refname; 112 const char *refname;
113 struct object *object; 113 struct object *object;
114 union { 114 union {
115 struct taginfo *tag; 115 struct taginfo *tag;
116 struct commitinfo *commit; 116 struct commitinfo *commit;
117 }; 117 };
118}; 118};
119 119
120struct reflist { 120struct reflist {
121 struct refinfo **refs; 121 struct refinfo **refs;
122 int alloc; 122 int alloc;
123 int count; 123 int count;
124}; 124};
125 125
126struct cgit_query { 126struct cgit_query {
127 int has_symref; 127 int has_symref;
128 int has_sha1; 128 int has_sha1;
129 char *raw; 129 char *raw;
130 char *repo; 130 char *repo;
131 char *page; 131 char *page;
132 char *search; 132 char *search;
133 char *grep; 133 char *grep;
134 char *head; 134 char *head;
135 char *sha1; 135 char *sha1;
136 char *sha2; 136 char *sha2;
137 char *path; 137 char *path;
138 char *name; 138 char *name;
139 char *mimetype; 139 char *mimetype;
140 char *url; 140 char *url;
141 char *period; 141 char *period;
142 int ofs; 142 int ofs;
143 int nohead; 143 int nohead;
144 char *sort; 144 char *sort;
145 int showmsg; 145 int showmsg;
146}; 146};
147 147
148struct cgit_config { 148struct cgit_config {
149 char *agefile; 149 char *agefile;
150 char *cache_root; 150 char *cache_root;
151 char *clone_prefix; 151 char *clone_prefix;
152 char *css; 152 char *css;
153 char *favicon; 153 char *favicon;
154 char *footer; 154 char *footer;
155 char *head_include; 155 char *head_include;
156 char *header; 156 char *header;
157 char *index_header; 157 char *index_header;
158 char *index_info; 158 char *index_info;
159 char *logo; 159 char *logo;
160 char *logo_link; 160 char *logo_link;
161 char *module_link; 161 char *module_link;
162 char *robots; 162 char *robots;
163 char *root_title; 163 char *root_title;
164 char *root_desc; 164 char *root_desc;
165 char *root_readme; 165 char *root_readme;
166 char *script_name; 166 char *script_name;
167 char *section; 167 char *section;
168 char *virtual_root; 168 char *virtual_root;
169 int cache_size; 169 int cache_size;
170 int cache_dynamic_ttl; 170 int cache_dynamic_ttl;
171 int cache_max_create_time; 171 int cache_max_create_time;
172 int cache_repo_ttl; 172 int cache_repo_ttl;
173 int cache_root_ttl; 173 int cache_root_ttl;
174 int cache_scanrc_ttl; 174 int cache_scanrc_ttl;
175 int cache_static_ttl; 175 int cache_static_ttl;
176 int embedded; 176 int embedded;
177 int enable_filter_overrides; 177 int enable_filter_overrides;
178 int enable_index_links; 178 int enable_index_links;
179 int enable_log_filecount; 179 int enable_log_filecount;
180 int enable_log_linecount; 180 int enable_log_linecount;
181 int enable_tree_linenumbers; 181 int enable_tree_linenumbers;
182 int local_time; 182 int local_time;
183 int max_repo_count; 183 int max_repo_count;
184 int max_commit_count; 184 int max_commit_count;
185 int max_lock_attempts; 185 int max_lock_attempts;
186 int max_msg_len; 186 int max_msg_len;
187 int max_repodesc_len; 187 int max_repodesc_len;
188 int max_blob_size;
188 int max_stats; 189 int max_stats;
189 int nocache; 190 int nocache;
190 int noplainemail; 191 int noplainemail;
191 int noheader; 192 int noheader;
192 int renamelimit; 193 int renamelimit;
193 int snapshots; 194 int snapshots;
194 int summary_branches; 195 int summary_branches;
195 int summary_log; 196 int summary_log;
196 int summary_tags; 197 int summary_tags;
197 struct string_list mimetypes; 198 struct string_list mimetypes;
198 struct cgit_filter *about_filter; 199 struct cgit_filter *about_filter;
199 struct cgit_filter *commit_filter; 200 struct cgit_filter *commit_filter;
200 struct cgit_filter *source_filter; 201 struct cgit_filter *source_filter;
201}; 202};
202 203
203struct cgit_page { 204struct cgit_page {
204 time_t modified; 205 time_t modified;
205 time_t expires; 206 time_t expires;
206 size_t size; 207 size_t size;
207 char *mimetype; 208 char *mimetype;
208 char *charset; 209 char *charset;
209 char *filename; 210 char *filename;
210 char *etag; 211 char *etag;
211 char *title; 212 char *title;
212 int status; 213 int status;
213 char *statusmsg; 214 char *statusmsg;
214}; 215};
215 216
216struct cgit_environment { 217struct cgit_environment {
217 char *cgit_config; 218 char *cgit_config;
218 char *http_host; 219 char *http_host;
219 char *https; 220 char *https;
220 char *no_http; 221 char *no_http;
221 char *path_info; 222 char *path_info;
222 char *query_string; 223 char *query_string;
223 char *request_method; 224 char *request_method;
224 char *script_name; 225 char *script_name;
225 char *server_name; 226 char *server_name;
226 char *server_port; 227 char *server_port;
227}; 228};
228 229
229struct cgit_context { 230struct cgit_context {
230 struct cgit_environment env; 231 struct cgit_environment env;
231 struct cgit_query qry; 232 struct cgit_query qry;
232 struct cgit_config cfg; 233 struct cgit_config cfg;
233 struct cgit_repo *repo; 234 struct cgit_repo *repo;
234 struct cgit_page page; 235 struct cgit_page page;
235}; 236};
236 237
237struct cgit_snapshot_format { 238struct cgit_snapshot_format {
238 const char *suffix; 239 const char *suffix;
239 const char *mimetype; 240 const char *mimetype;
240 write_archive_fn_t write_func; 241 write_archive_fn_t write_func;
241 int bit; 242 int bit;
242}; 243};
243 244
244extern const char *cgit_version; 245extern const char *cgit_version;
245 246
246extern struct cgit_repolist cgit_repolist; 247extern struct cgit_repolist cgit_repolist;
247extern struct cgit_context ctx; 248extern struct cgit_context ctx;
248extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 249extern const struct cgit_snapshot_format cgit_snapshot_formats[];
249 250
250extern struct cgit_repo *cgit_add_repo(const char *url); 251extern struct cgit_repo *cgit_add_repo(const char *url);
251extern struct cgit_repo *cgit_get_repoinfo(const char *url); 252extern struct cgit_repo *cgit_get_repoinfo(const char *url);
252extern void cgit_repo_config_cb(const char *name, const char *value); 253extern void cgit_repo_config_cb(const char *name, const char *value);
253 254
254extern int chk_zero(int result, char *msg); 255extern int chk_zero(int result, char *msg);
255extern int chk_positive(int result, char *msg); 256extern int chk_positive(int result, char *msg);
256extern int chk_non_negative(int result, char *msg); 257extern int chk_non_negative(int result, char *msg);
257 258
258extern char *trim_end(const char *str, char c); 259extern char *trim_end(const char *str, char c);
259extern char *strlpart(char *txt, int maxlen); 260extern char *strlpart(char *txt, int maxlen);
260extern char *strrpart(char *txt, int maxlen); 261extern char *strrpart(char *txt, int maxlen);
261 262
262extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 263extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
263extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 264extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
264 int flags, void *cb_data); 265 int flags, void *cb_data);
265 266
266extern void *cgit_free_commitinfo(struct commitinfo *info); 267extern void *cgit_free_commitinfo(struct commitinfo *info);
267 268
268extern int cgit_diff_files(const unsigned char *old_sha1, 269extern int cgit_diff_files(const unsigned char *old_sha1,
269 const unsigned char *new_sha1, 270 const unsigned char *new_sha1,
270 unsigned long *old_size, unsigned long *new_size, 271 unsigned long *old_size, unsigned long *new_size,
271 int *binary, linediff_fn fn); 272 int *binary, linediff_fn fn);
272 273
273extern void cgit_diff_tree(const unsigned char *old_sha1, 274extern void cgit_diff_tree(const unsigned char *old_sha1,
274 const unsigned char *new_sha1, 275 const unsigned char *new_sha1,
275 filepair_fn fn, const char *prefix); 276 filepair_fn fn, const char *prefix);
276 277
277extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 278extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
278 279
279extern char *fmt(const char *format,...); 280extern char *fmt(const char *format,...);
280 281
281extern struct commitinfo *cgit_parse_commit(struct commit *commit); 282extern struct commitinfo *cgit_parse_commit(struct commit *commit);
282extern struct taginfo *cgit_parse_tag(struct tag *tag); 283extern struct taginfo *cgit_parse_tag(struct tag *tag);
283extern void cgit_parse_url(const char *url); 284extern void cgit_parse_url(const char *url);
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 0c13485..e69140b 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -84,192 +84,196 @@ commit-filter::
84 84
85css:: 85css::
86 Url which specifies the css document to include in all cgit pages. 86 Url which specifies the css document to include in all cgit pages.
87 Default value: "/cgit.css". 87 Default value: "/cgit.css".
88 88
89embedded:: 89embedded::
90 Flag which, when set to "1", will make cgit generate a html fragment 90 Flag which, when set to "1", will make cgit generate a html fragment
91 suitable for embedding in other html pages. Default value: none. See 91 suitable for embedding in other html pages. Default value: none. See
92 also: "noheader". 92 also: "noheader".
93 93
94enable-filter-overrides:: 94enable-filter-overrides::
95 Flag which, when set to "1", allows all filter settings to be 95 Flag which, when set to "1", allows all filter settings to be
96 overridden in repository-specific cgitrc files. Default value: none. 96 overridden in repository-specific cgitrc files. Default value: none.
97 97
98enable-index-links:: 98enable-index-links::
99 Flag which, when set to "1", will make cgit generate extra links for 99 Flag which, when set to "1", will make cgit generate extra links for
100 each repo in the repository index (specifically, to the "summary", 100 each repo in the repository index (specifically, to the "summary",
101 "commit" and "tree" pages). Default value: "0". 101 "commit" and "tree" pages). Default value: "0".
102 102
103enable-log-filecount:: 103enable-log-filecount::
104 Flag which, when set to "1", will make cgit print the number of 104 Flag which, when set to "1", will make cgit print the number of
105 modified files for each commit on the repository log page. Default 105 modified files for each commit on the repository log page. Default
106 value: "0". 106 value: "0".
107 107
108enable-log-linecount:: 108enable-log-linecount::
109 Flag which, when set to "1", will make cgit print the number of added 109 Flag which, when set to "1", will make cgit print the number of added
110 and removed lines for each commit on the repository log page. Default 110 and removed lines for each commit on the repository log page. Default
111 value: "0". 111 value: "0".
112 112
113enable-tree-linenumbers:: 113enable-tree-linenumbers::
114 Flag which, when set to "1", will make cgit generate linenumber links 114 Flag which, when set to "1", will make cgit generate linenumber links
115 for plaintext blobs printed in the tree view. Default value: "1". 115 for plaintext blobs printed in the tree view. Default value: "1".
116 116
117favicon:: 117favicon::
118 Url used as link to a shortcut icon for cgit. If specified, it is 118 Url used as link to a shortcut icon for cgit. If specified, it is
119 suggested to use the value "/favicon.ico" since certain browsers will 119 suggested to use the value "/favicon.ico" since certain browsers will
120 ignore other values. Default value: none. 120 ignore other values. Default value: none.
121 121
122footer:: 122footer::
123 The content of the file specified with this option will be included 123 The content of the file specified with this option will be included
124 verbatim at the bottom of all pages (i.e. it replaces the standard 124 verbatim at the bottom of all pages (i.e. it replaces the standard
125 "generated by..." message. Default value: none. 125 "generated by..." message. Default value: none.
126 126
127head-include:: 127head-include::
128 The content of the file specified with this option will be included 128 The content of the file specified with this option will be included
129 verbatim in the html HEAD section on all pages. Default value: none. 129 verbatim in the html HEAD section on all pages. Default value: none.
130 130
131header:: 131header::
132 The content of the file specified with this option will be included 132 The content of the file specified with this option will be included
133 verbatim at the top of all pages. Default value: none. 133 verbatim at the top of all pages. Default value: none.
134 134
135include:: 135include::
136 Name of a configfile to include before the rest of the current config- 136 Name of a configfile to include before the rest of the current config-
137 file is parsed. Default value: none. 137 file is parsed. Default value: none.
138 138
139index-header:: 139index-header::
140 The content of the file specified with this option will be included 140 The content of the file specified with this option will be included
141 verbatim above the repository index. This setting is deprecated, and 141 verbatim above the repository index. This setting is deprecated, and
142 will not be supported by cgit-1.0 (use root-readme instead). Default 142 will not be supported by cgit-1.0 (use root-readme instead). Default
143 value: none. 143 value: none.
144 144
145index-info:: 145index-info::
146 The content of the file specified with this option will be included 146 The content of the file specified with this option will be included
147 verbatim below the heading on the repository index page. This setting 147 verbatim below the heading on the repository index page. This setting
148 is deprecated, and will not be supported by cgit-1.0 (use root-desc 148 is deprecated, and will not be supported by cgit-1.0 (use root-desc
149 instead). Default value: none. 149 instead). Default value: none.
150 150
151local-time:: 151local-time::
152 Flag which, if set to "1", makes cgit print commit and tag times in the 152 Flag which, if set to "1", makes cgit print commit and tag times in the
153 servers timezone. Default value: "0". 153 servers timezone. Default value: "0".
154 154
155logo:: 155logo::
156 Url which specifies the source of an image which will be used as a logo 156 Url which specifies the source of an image which will be used as a logo
157 on all cgit pages. Default value: "/cgit.png". 157 on all cgit pages. Default value: "/cgit.png".
158 158
159logo-link:: 159logo-link::
160 Url loaded when clicking on the cgit logo image. If unspecified the 160 Url loaded when clicking on the cgit logo image. If unspecified the
161 calculated url of the repository index page will be used. Default 161 calculated url of the repository index page will be used. Default
162 value: none. 162 value: none.
163 163
164max-commit-count:: 164max-commit-count::
165 Specifies the number of entries to list per page in "log" view. Default 165 Specifies the number of entries to list per page in "log" view. Default
166 value: "50". 166 value: "50".
167 167
168max-message-length:: 168max-message-length::
169 Specifies the maximum number of commit message characters to display in 169 Specifies the maximum number of commit message characters to display in
170 "log" view. Default value: "80". 170 "log" view. Default value: "80".
171 171
172max-repo-count:: 172max-repo-count::
173 Specifies the number of entries to list per page on therepository 173 Specifies the number of entries to list per page on therepository
174 index page. Default value: "50". 174 index page. Default value: "50".
175 175
176max-repodesc-length:: 176max-repodesc-length::
177 Specifies the maximum number of repo description characters to display 177 Specifies the maximum number of repo description characters to display
178 on the repository index page. Default value: "80". 178 on the repository index page. Default value: "80".
179 179
180max-blob-size::
181 Specifies the maximum size of a blob to display HTML for in KBytes.
182 Default value: "0" (limit disabled).
183
180max-stats:: 184max-stats::
181 Set the default maximum statistics period. Valid values are "week", 185 Set the default maximum statistics period. Valid values are "week",
182 "month", "quarter" and "year". If unspecified, statistics are 186 "month", "quarter" and "year". If unspecified, statistics are
183 disabled. Default value: none. See also: "repo.max-stats". 187 disabled. Default value: none. See also: "repo.max-stats".
184 188
185mimetype.<ext>:: 189mimetype.<ext>::
186 Set the mimetype for the specified filename extension. This is used 190 Set the mimetype for the specified filename extension. This is used
187 by the `plain` command when returning blob content. 191 by the `plain` command when returning blob content.
188 192
189module-link:: 193module-link::
190 Text which will be used as the formatstring for a hyperlink when a 194 Text which will be used as the formatstring for a hyperlink when a
191 submodule is printed in a directory listing. The arguments for the 195 submodule is printed in a directory listing. The arguments for the
192 formatstring are the path and SHA1 of the submodule commit. Default 196 formatstring are the path and SHA1 of the submodule commit. Default
193 value: "./?repo=%s&page=commit&id=%s" 197 value: "./?repo=%s&page=commit&id=%s"
194 198
195nocache:: 199nocache::
196 If set to the value "1" caching will be disabled. This settings is 200 If set to the value "1" caching will be disabled. This settings is
197 deprecated, and will not be honored starting with cgit-1.0. Default 201 deprecated, and will not be honored starting with cgit-1.0. Default
198 value: "0". 202 value: "0".
199 203
200noplainemail:: 204noplainemail::
201 If set to "1" showing full author email adresses will be disabled. 205 If set to "1" showing full author email adresses will be disabled.
202 Default value: "0". 206 Default value: "0".
203 207
204noheader:: 208noheader::
205 Flag which, when set to "1", will make cgit omit the standard header 209 Flag which, when set to "1", will make cgit omit the standard header
206 on all pages. Default value: none. See also: "embedded". 210 on all pages. Default value: none. See also: "embedded".
207 211
208renamelimit:: 212renamelimit::
209 Maximum number of files to consider when detecting renames. The value 213 Maximum number of files to consider when detecting renames. The value
210 "-1" uses the compiletime value in git (for further info, look at 214 "-1" uses the compiletime value in git (for further info, look at
211 `man git-diff`). Default value: "-1". 215 `man git-diff`). Default value: "-1".
212 216
213repo.group:: 217repo.group::
214 Legacy alias for "section". This option is deprecated and will not be 218 Legacy alias for "section". This option is deprecated and will not be
215 supported in cgit-1.0. 219 supported in cgit-1.0.
216 220
217robots:: 221robots::
218 Text used as content for the "robots" meta-tag. Default value: 222 Text used as content for the "robots" meta-tag. Default value:
219 "index, nofollow". 223 "index, nofollow".
220 224
221root-desc:: 225root-desc::
222 Text printed below the heading on the repository index page. Default 226 Text printed below the heading on the repository index page. Default
223 value: "a fast webinterface for the git dscm". 227 value: "a fast webinterface for the git dscm".
224 228
225root-readme:: 229root-readme::
226 The content of the file specified with this option will be included 230 The content of the file specified with this option will be included
227 verbatim below the "about" link on the repository index page. Default 231 verbatim below the "about" link on the repository index page. Default
228 value: none. 232 value: none.
229 233
230root-title:: 234root-title::
231 Text printed as heading on the repository index page. Default value: 235 Text printed as heading on the repository index page. Default value:
232 "Git Repository Browser". 236 "Git Repository Browser".
233 237
234scan-path:: 238scan-path::
235 A path which will be scanned for repositories. If caching is enabled, 239 A path which will be scanned for repositories. If caching is enabled,
236 the result will be cached as a cgitrc include-file in the cache 240 the result will be cached as a cgitrc include-file in the cache
237 directory. Default value: none. See also: cache-scanrc-ttl. 241 directory. Default value: none. See also: cache-scanrc-ttl.
238 242
239section:: 243section::
240 The name of the current repository section - all repositories defined 244 The name of the current repository section - all repositories defined
241 after this option will inherit the current section name. Default value: 245 after this option will inherit the current section name. Default value:
242 none. 246 none.
243 247
244snapshots:: 248snapshots::
245 Text which specifies the default set of snapshot formats generated by 249 Text which specifies the default set of snapshot formats generated by
246 cgit. The value is a space-separated list of zero or more of the 250 cgit. The value is a space-separated list of zero or more of the
247 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 251 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
248 252
249source-filter:: 253source-filter::
250 Specifies a command which will be invoked to format plaintext blobs 254 Specifies a command which will be invoked to format plaintext blobs
251 in the tree view. The command will get the blob content on its STDIN 255 in the tree view. The command will get the blob content on its STDIN
252 and the name of the blob as its only command line argument. The STDOUT 256 and the name of the blob as its only command line argument. The STDOUT
253 from the command will be included verbatim as the blob contents, i.e. 257 from the command will be included verbatim as the blob contents, i.e.
254 this can be used to implement e.g. syntax highlighting. Default value: 258 this can be used to implement e.g. syntax highlighting. Default value:
255 none. 259 none.
256 260
257summary-branches:: 261summary-branches::
258 Specifies the number of branches to display in the repository "summary" 262 Specifies the number of branches to display in the repository "summary"
259 view. Default value: "10". 263 view. Default value: "10".
260 264
261summary-log:: 265summary-log::
262 Specifies the number of log entries to display in the repository 266 Specifies the number of log entries to display in the repository
263 "summary" view. Default value: "10". 267 "summary" view. Default value: "10".
264 268
265summary-tags:: 269summary-tags::
266 Specifies the number of tags to display in the repository "summary" 270 Specifies the number of tags to display in the repository "summary"
267 view. Default value: "10". 271 view. Default value: "10".
268 272
269virtual-root:: 273virtual-root::
270 Url which, if specified, will be used as root for all cgit links. It 274 Url which, if specified, will be used as root for all cgit links. It
271 will also cause cgit to generate 'virtual urls', i.e. urls like 275 will also cause cgit to generate 'virtual urls', i.e. urls like
272 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 276 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
273 value: none. 277 value: none.
274 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 278 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
275 same kind of virtual urls, so this option will probably be deprecated. 279 same kind of virtual urls, so this option will probably be deprecated.
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
index 999ad0c..6b1c576 100755
--- a/filters/syntax-highlighting.sh
+++ b/filters/syntax-highlighting.sh
@@ -1,39 +1,34 @@
1#!/bin/sh 1#!/bin/sh
2# This script can be used to implement syntax highlighting in the cgit 2# This script can be used to implement syntax highlighting in the cgit
3# tree-view by refering to this file with the source-filter or repo.source- 3# tree-view by refering to this file with the source-filter or repo.source-
4# filter options in cgitrc. 4# filter options in cgitrc.
5# 5#
6# This script requires a shell supporting the ${var##pattern} syntax.
7# It is supported by at least dash and bash, however busybox environments
8# might have to use an external call to sed instead.
9#
6# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax 10# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
7# highlighting, so you'll probably want something like the following included 11# highlighting, so you'll probably want something like the following included
8# in your css file (generated by highlight 2.4.8 and adapted for cgit): 12# in your css file (generated by highlight 2.4.8 and adapted for cgit):
9# 13#
10# table.blob .num { color:#2928ff; } 14# table.blob .num { color:#2928ff; }
11# table.blob .esc { color:#ff00ff; } 15# table.blob .esc { color:#ff00ff; }
12# table.blob .str { color:#ff0000; } 16# table.blob .str { color:#ff0000; }
13# table.blob .dstr { color:#818100; } 17# table.blob .dstr { color:#818100; }
14# table.blob .slc { color:#838183; font-style:italic; } 18# table.blob .slc { color:#838183; font-style:italic; }
15# table.blob .com { color:#838183; font-style:italic; } 19# table.blob .com { color:#838183; font-style:italic; }
16# table.blob .dir { color:#008200; } 20# table.blob .dir { color:#008200; }
17# table.blob .sym { color:#000000; } 21# table.blob .sym { color:#000000; }
18# table.blob .kwa { color:#000000; font-weight:bold; } 22# table.blob .kwa { color:#000000; font-weight:bold; }
19# table.blob .kwb { color:#830000; } 23# table.blob .kwb { color:#830000; }
20# table.blob .kwc { color:#000000; font-weight:bold; } 24# table.blob .kwc { color:#000000; font-weight:bold; }
21# table.blob .kwd { color:#010181; } 25# table.blob .kwd { color:#010181; }
22 26
23case "$1" in 27# store filename and extension in local vars
24 *.c) 28BASENAME="$1"
25 highlight -f -I -X -S c 29EXTENSION="${BASENAME##*.}"
26 ;; 30
27 *.h) 31# map Makefile and Makefile.* to .mk
28 highlight -f -I -X -S c 32[ "${BASENAME%%.*}" == "Makefile" ] && EXTENSION=mk
29 ;; 33
30 *.sh) 34exec highlight --force -f -I -X -S $EXTENSION 2>/dev/null
31 highlight -f -I -X -S sh
32 ;;
33 *.css)
34 highlight -f -I -X -S css
35 ;;
36 *)
37 highlight -f -I -X -S txt
38 ;;
39esac
diff --git a/ui-tree.c b/ui-tree.c
index f53ab64..f281937 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -14,192 +14,198 @@
14char *curr_rev; 14char *curr_rev;
15char *match_path; 15char *match_path;
16int header = 0; 16int header = 0;
17 17
18static void print_text_buffer(const char *name, char *buf, unsigned long size) 18static void print_text_buffer(const char *name, char *buf, unsigned long size)
19{ 19{
20 unsigned long lineno, idx; 20 unsigned long lineno, idx;
21 const char *numberfmt = 21 const char *numberfmt =
22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; 22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n";
23 23
24 html("<table summary='blob content' class='blob'>\n"); 24 html("<table summary='blob content' class='blob'>\n");
25 25
26 if (ctx.cfg.enable_tree_linenumbers) { 26 if (ctx.cfg.enable_tree_linenumbers) {
27 html("<tr><td class='linenumbers'><pre>"); 27 html("<tr><td class='linenumbers'><pre>");
28 idx = 0; 28 idx = 0;
29 lineno = 0; 29 lineno = 0;
30 30
31 if (size) { 31 if (size) {
32 htmlf(numberfmt, ++lineno); 32 htmlf(numberfmt, ++lineno);
33 while(idx < size - 1) { // skip absolute last newline 33 while(idx < size - 1) { // skip absolute last newline
34 if (buf[idx] == '\n') 34 if (buf[idx] == '\n')
35 htmlf(numberfmt, ++lineno); 35 htmlf(numberfmt, ++lineno);
36 idx++; 36 idx++;
37 } 37 }
38 } 38 }
39 html("</pre></td>\n"); 39 html("</pre></td>\n");
40 } 40 }
41 else { 41 else {
42 html("<tr>\n"); 42 html("<tr>\n");
43 } 43 }
44 44
45 if (ctx.repo->source_filter) { 45 if (ctx.repo->source_filter) {
46 html("<td class='lines'><pre><code>"); 46 html("<td class='lines'><pre><code>");
47 ctx.repo->source_filter->argv[1] = xstrdup(name); 47 ctx.repo->source_filter->argv[1] = xstrdup(name);
48 cgit_open_filter(ctx.repo->source_filter); 48 cgit_open_filter(ctx.repo->source_filter);
49 write(STDOUT_FILENO, buf, size); 49 write(STDOUT_FILENO, buf, size);
50 cgit_close_filter(ctx.repo->source_filter); 50 cgit_close_filter(ctx.repo->source_filter);
51 html("</code></pre></td></tr></table>\n"); 51 html("</code></pre></td></tr></table>\n");
52 return; 52 return;
53 } 53 }
54 54
55 html("<td class='lines'><pre><code>"); 55 html("<td class='lines'><pre><code>");
56 html_txt(buf); 56 html_txt(buf);
57 html("</code></pre></td></tr></table>\n"); 57 html("</code></pre></td></tr></table>\n");
58} 58}
59 59
60#define ROWLEN 32 60#define ROWLEN 32
61 61
62static void print_binary_buffer(char *buf, unsigned long size) 62static void print_binary_buffer(char *buf, unsigned long size)
63{ 63{
64 unsigned long ofs, idx; 64 unsigned long ofs, idx;
65 static char ascii[ROWLEN + 1]; 65 static char ascii[ROWLEN + 1];
66 66
67 html("<table summary='blob content' class='bin-blob'>\n"); 67 html("<table summary='blob content' class='bin-blob'>\n");
68 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); 68 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>");
69 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { 69 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) {
70 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); 70 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs);
71 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 71 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
72 htmlf("%*s%02x", 72 htmlf("%*s%02x",
73 idx == 16 ? 4 : 1, "", 73 idx == 16 ? 4 : 1, "",
74 buf[idx] & 0xff); 74 buf[idx] & 0xff);
75 html(" </td><td class='hex'>"); 75 html(" </td><td class='hex'>");
76 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 76 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
77 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; 77 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.';
78 ascii[idx] = '\0'; 78 ascii[idx] = '\0';
79 html_txt(ascii); 79 html_txt(ascii);
80 html("</td></tr>\n"); 80 html("</td></tr>\n");
81 } 81 }
82 html("</table>\n"); 82 html("</table>\n");
83} 83}
84 84
85static void print_object(const unsigned char *sha1, char *path, const char *basename) 85static void print_object(const unsigned char *sha1, char *path, const char *basename)
86{ 86{
87 enum object_type type; 87 enum object_type type;
88 char *buf; 88 char *buf;
89 unsigned long size; 89 unsigned long size;
90 90
91 type = sha1_object_info(sha1, &size); 91 type = sha1_object_info(sha1, &size);
92 if (type == OBJ_BAD) { 92 if (type == OBJ_BAD) {
93 cgit_print_error(fmt("Bad object name: %s", 93 cgit_print_error(fmt("Bad object name: %s",
94 sha1_to_hex(sha1))); 94 sha1_to_hex(sha1)));
95 return; 95 return;
96 } 96 }
97 97
98 buf = read_sha1_file(sha1, &type, &size); 98 buf = read_sha1_file(sha1, &type, &size);
99 if (!buf) { 99 if (!buf) {
100 cgit_print_error(fmt("Error reading object %s", 100 cgit_print_error(fmt("Error reading object %s",
101 sha1_to_hex(sha1))); 101 sha1_to_hex(sha1)));
102 return; 102 return;
103 } 103 }
104 104
105 html(" ("); 105 html(" (");
106 cgit_plain_link("plain", NULL, NULL, ctx.qry.head, 106 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
107 curr_rev, path); 107 curr_rev, path);
108 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); 108 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1));
109 109
110 if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
111 htmlf("<div class='error'>blob size (%dKB) exceeds display size limit (%dKB).</div>",
112 size / 1024, ctx.cfg.max_blob_size);
113 return;
114 }
115
110 if (buffer_is_binary(buf, size)) 116 if (buffer_is_binary(buf, size))
111 print_binary_buffer(buf, size); 117 print_binary_buffer(buf, size);
112 else 118 else
113 print_text_buffer(basename, buf, size); 119 print_text_buffer(basename, buf, size);
114} 120}
115 121
116 122
117static int ls_item(const unsigned char *sha1, const char *base, int baselen, 123static int ls_item(const unsigned char *sha1, const char *base, int baselen,
118 const char *pathname, unsigned int mode, int stage, 124 const char *pathname, unsigned int mode, int stage,
119 void *cbdata) 125 void *cbdata)
120{ 126{
121 char *name; 127 char *name;
122 char *fullpath; 128 char *fullpath;
123 char *class; 129 char *class;
124 enum object_type type; 130 enum object_type type;
125 unsigned long size = 0; 131 unsigned long size = 0;
126 132
127 name = xstrdup(pathname); 133 name = xstrdup(pathname);
128 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 134 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
129 ctx.qry.path ? "/" : "", name); 135 ctx.qry.path ? "/" : "", name);
130 136
131 if (!S_ISGITLINK(mode)) { 137 if (!S_ISGITLINK(mode)) {
132 type = sha1_object_info(sha1, &size); 138 type = sha1_object_info(sha1, &size);
133 if (type == OBJ_BAD) { 139 if (type == OBJ_BAD) {
134 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 140 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
135 name, 141 name,
136 sha1_to_hex(sha1)); 142 sha1_to_hex(sha1));
137 return 0; 143 return 0;
138 } 144 }
139 } 145 }
140 146
141 html("<tr><td class='ls-mode'>"); 147 html("<tr><td class='ls-mode'>");
142 cgit_print_filemode(mode); 148 cgit_print_filemode(mode);
143 html("</td><td>"); 149 html("</td><td>");
144 if (S_ISGITLINK(mode)) { 150 if (S_ISGITLINK(mode)) {
145 htmlf("<a class='ls-mod' href='"); 151 htmlf("<a class='ls-mod' href='");
146 html_attr(fmt(ctx.repo->module_link, 152 html_attr(fmt(ctx.repo->module_link,
147 name, 153 name,
148 sha1_to_hex(sha1))); 154 sha1_to_hex(sha1)));
149 html("'>"); 155 html("'>");
150 html_txt(name); 156 html_txt(name);
151 html("</a>"); 157 html("</a>");
152 } else if (S_ISDIR(mode)) { 158 } else if (S_ISDIR(mode)) {
153 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, 159 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
154 curr_rev, fullpath); 160 curr_rev, fullpath);
155 } else { 161 } else {
156 class = strrchr(name, '.'); 162 class = strrchr(name, '.');
157 if (class != NULL) { 163 if (class != NULL) {
158 class = fmt("ls-blob %s", class + 1); 164 class = fmt("ls-blob %s", class + 1);
159 } else 165 } else
160 class = "ls-blob"; 166 class = "ls-blob";
161 cgit_tree_link(name, NULL, class, ctx.qry.head, 167 cgit_tree_link(name, NULL, class, ctx.qry.head,
162 curr_rev, fullpath); 168 curr_rev, fullpath);
163 } 169 }
164 htmlf("</td><td class='ls-size'>%li</td>", size); 170 htmlf("</td><td class='ls-size'>%li</td>", size);
165 171
166 html("<td>"); 172 html("<td>");
167 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, 173 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev,
168 fullpath, 0, NULL, NULL, ctx.qry.showmsg); 174 fullpath, 0, NULL, NULL, ctx.qry.showmsg);
169 if (ctx.repo->max_stats) 175 if (ctx.repo->max_stats)
170 cgit_stats_link("stats", NULL, "button", ctx.qry.head, 176 cgit_stats_link("stats", NULL, "button", ctx.qry.head,
171 fullpath); 177 fullpath);
172 html("</td></tr>\n"); 178 html("</td></tr>\n");
173 free(name); 179 free(name);
174 return 0; 180 return 0;
175} 181}
176 182
177static void ls_head() 183static void ls_head()
178{ 184{
179 html("<table summary='tree listing' class='list'>\n"); 185 html("<table summary='tree listing' class='list'>\n");
180 html("<tr class='nohover'>"); 186 html("<tr class='nohover'>");
181 html("<th class='left'>Mode</th>"); 187 html("<th class='left'>Mode</th>");
182 html("<th class='left'>Name</th>"); 188 html("<th class='left'>Name</th>");
183 html("<th class='right'>Size</th>"); 189 html("<th class='right'>Size</th>");
184 html("<th/>"); 190 html("<th/>");
185 html("</tr>\n"); 191 html("</tr>\n");
186 header = 1; 192 header = 1;
187} 193}
188 194
189static void ls_tail() 195static void ls_tail()
190{ 196{
191 if (!header) 197 if (!header)
192 return; 198 return;
193 html("</table>\n"); 199 html("</table>\n");
194 header = 0; 200 header = 0;
195} 201}
196 202
197static void ls_tree(const unsigned char *sha1, char *path) 203static void ls_tree(const unsigned char *sha1, char *path)
198{ 204{
199 struct tree *tree; 205 struct tree *tree;
200 206
201 tree = parse_tree_indirect(sha1); 207 tree = parse_tree_indirect(sha1);
202 if (!tree) { 208 if (!tree) {
203 cgit_print_error(fmt("Not a tree object: %s", 209 cgit_print_error(fmt("Not a tree object: %s",
204 sha1_to_hex(sha1))); 210 sha1_to_hex(sha1)));
205 return; 211 return;