summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2009-07-25 09:59:22 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2009-07-25 09:59:22 (UTC)
commit681fdc45473143de3f3c5f69fbc7b94f5d6b0b75 (patch) (unidiff)
tree3bfca05875524bee0e5444fb791707bc3e593dbd
parent7e5c048505efe1902fb476cc2cb3160ff7df013d (diff)
parent3ff58ddd51bcbcbc9b7649bad1a39aa98af4b49f (diff)
downloadcgit-681fdc45473143de3f3c5f69fbc7b94f5d6b0b75.zip
cgit-681fdc45473143de3f3c5f69fbc7b94f5d6b0b75.tar.gz
cgit-681fdc45473143de3f3c5f69fbc7b94f5d6b0b75.tar.bz2
Merge branch 'plain-etag'
Conflicts: ui-shared.c
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c4
-rw-r--r--cgit.h1
-rw-r--r--ui-plain.c1
-rw-r--r--ui-shared.c6
4 files changed, 12 insertions, 0 deletions
diff --git a/cgit.c b/cgit.c
index 19adadd..ae20257 100644
--- a/cgit.c
+++ b/cgit.c
@@ -161,96 +161,97 @@ static void querystring_cb(const char *name, const char *value)
161 ctx.qry.name = xstrdup(value); 161 ctx.qry.name = xstrdup(value);
162 } else if (!strcmp(name, "mimetype")) { 162 } else if (!strcmp(name, "mimetype")) {
163 ctx.qry.mimetype = xstrdup(value); 163 ctx.qry.mimetype = xstrdup(value);
164 } else if (!strcmp(name, "s")){ 164 } else if (!strcmp(name, "s")){
165 ctx.qry.sort = xstrdup(value); 165 ctx.qry.sort = xstrdup(value);
166 } else if (!strcmp(name, "showmsg")) { 166 } else if (!strcmp(name, "showmsg")) {
167 ctx.qry.showmsg = atoi(value); 167 ctx.qry.showmsg = atoi(value);
168 } else if (!strcmp(name, "period")) { 168 } else if (!strcmp(name, "period")) {
169 ctx.qry.period = xstrdup(value); 169 ctx.qry.period = xstrdup(value);
170 } 170 }
171} 171}
172 172
173static void prepare_context(struct cgit_context *ctx) 173static void prepare_context(struct cgit_context *ctx)
174{ 174{
175 memset(ctx, 0, sizeof(ctx)); 175 memset(ctx, 0, sizeof(ctx));
176 ctx->cfg.agefile = "info/web/last-modified"; 176 ctx->cfg.agefile = "info/web/last-modified";
177 ctx->cfg.nocache = 0; 177 ctx->cfg.nocache = 0;
178 ctx->cfg.cache_size = 0; 178 ctx->cfg.cache_size = 0;
179 ctx->cfg.cache_dynamic_ttl = 5; 179 ctx->cfg.cache_dynamic_ttl = 5;
180 ctx->cfg.cache_max_create_time = 5; 180 ctx->cfg.cache_max_create_time = 5;
181 ctx->cfg.cache_repo_ttl = 5; 181 ctx->cfg.cache_repo_ttl = 5;
182 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 182 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
183 ctx->cfg.cache_root_ttl = 5; 183 ctx->cfg.cache_root_ttl = 5;
184 ctx->cfg.cache_static_ttl = -1; 184 ctx->cfg.cache_static_ttl = -1;
185 ctx->cfg.css = "/cgit.css"; 185 ctx->cfg.css = "/cgit.css";
186 ctx->cfg.logo = "/git-logo.png"; 186 ctx->cfg.logo = "/git-logo.png";
187 ctx->cfg.local_time = 0; 187 ctx->cfg.local_time = 0;
188 ctx->cfg.max_repo_count = 50; 188 ctx->cfg.max_repo_count = 50;
189 ctx->cfg.max_commit_count = 50; 189 ctx->cfg.max_commit_count = 50;
190 ctx->cfg.max_lock_attempts = 5; 190 ctx->cfg.max_lock_attempts = 5;
191 ctx->cfg.max_msg_len = 80; 191 ctx->cfg.max_msg_len = 80;
192 ctx->cfg.max_repodesc_len = 80; 192 ctx->cfg.max_repodesc_len = 80;
193 ctx->cfg.max_stats = 0; 193 ctx->cfg.max_stats = 0;
194 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 194 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
195 ctx->cfg.renamelimit = -1; 195 ctx->cfg.renamelimit = -1;
196 ctx->cfg.robots = "index, nofollow"; 196 ctx->cfg.robots = "index, nofollow";
197 ctx->cfg.root_title = "Git repository browser"; 197 ctx->cfg.root_title = "Git repository browser";
198 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 198 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
199 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 199 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
200 ctx->cfg.summary_branches = 10; 200 ctx->cfg.summary_branches = 10;
201 ctx->cfg.summary_log = 10; 201 ctx->cfg.summary_log = 10;
202 ctx->cfg.summary_tags = 10; 202 ctx->cfg.summary_tags = 10;
203 ctx->page.mimetype = "text/html"; 203 ctx->page.mimetype = "text/html";
204 ctx->page.charset = PAGE_ENCODING; 204 ctx->page.charset = PAGE_ENCODING;
205 ctx->page.filename = NULL; 205 ctx->page.filename = NULL;
206 ctx->page.size = 0; 206 ctx->page.size = 0;
207 ctx->page.modified = time(NULL); 207 ctx->page.modified = time(NULL);
208 ctx->page.expires = ctx->page.modified; 208 ctx->page.expires = ctx->page.modified;
209 ctx->page.etag = NULL;
209} 210}
210 211
211struct refmatch { 212struct refmatch {
212 char *req_ref; 213 char *req_ref;
213 char *first_ref; 214 char *first_ref;
214 int match; 215 int match;
215}; 216};
216 217
217int find_current_ref(const char *refname, const unsigned char *sha1, 218int find_current_ref(const char *refname, const unsigned char *sha1,
218 int flags, void *cb_data) 219 int flags, void *cb_data)
219{ 220{
220 struct refmatch *info; 221 struct refmatch *info;
221 222
222 info = (struct refmatch *)cb_data; 223 info = (struct refmatch *)cb_data;
223 if (!strcmp(refname, info->req_ref)) 224 if (!strcmp(refname, info->req_ref))
224 info->match = 1; 225 info->match = 1;
225 if (!info->first_ref) 226 if (!info->first_ref)
226 info->first_ref = xstrdup(refname); 227 info->first_ref = xstrdup(refname);
227 return info->match; 228 return info->match;
228} 229}
229 230
230char *find_default_branch(struct cgit_repo *repo) 231char *find_default_branch(struct cgit_repo *repo)
231{ 232{
232 struct refmatch info; 233 struct refmatch info;
233 char *ref; 234 char *ref;
234 235
235 info.req_ref = repo->defbranch; 236 info.req_ref = repo->defbranch;
236 info.first_ref = NULL; 237 info.first_ref = NULL;
237 info.match = 0; 238 info.match = 0;
238 for_each_branch_ref(find_current_ref, &info); 239 for_each_branch_ref(find_current_ref, &info);
239 if (info.match) 240 if (info.match)
240 ref = info.req_ref; 241 ref = info.req_ref;
241 else 242 else
242 ref = info.first_ref; 243 ref = info.first_ref;
243 if (ref) 244 if (ref)
244 ref = xstrdup(ref); 245 ref = xstrdup(ref);
245 return ref; 246 return ref;
246} 247}
247 248
248static int prepare_repo_cmd(struct cgit_context *ctx) 249static int prepare_repo_cmd(struct cgit_context *ctx)
249{ 250{
250 char *tmp; 251 char *tmp;
251 unsigned char sha1[20]; 252 unsigned char sha1[20];
252 int nongit = 0; 253 int nongit = 0;
253 254
254 setenv("GIT_DIR", ctx->repo->path, 1); 255 setenv("GIT_DIR", ctx->repo->path, 1);
255 setup_git_directory_gently(&nongit); 256 setup_git_directory_gently(&nongit);
256 if (nongit) { 257 if (nongit) {
@@ -386,103 +387,106 @@ static void cgit_parse_args(int argc, const char **argv)
386 } 387 }
387 if (!strncmp(argv[i], "--page=", 7)) { 388 if (!strncmp(argv[i], "--page=", 7)) {
388 ctx.qry.page = xstrdup(argv[i]+7); 389 ctx.qry.page = xstrdup(argv[i]+7);
389 } 390 }
390 if (!strncmp(argv[i], "--head=", 7)) { 391 if (!strncmp(argv[i], "--head=", 7)) {
391 ctx.qry.head = xstrdup(argv[i]+7); 392 ctx.qry.head = xstrdup(argv[i]+7);
392 ctx.qry.has_symref = 1; 393 ctx.qry.has_symref = 1;
393 } 394 }
394 if (!strncmp(argv[i], "--sha1=", 7)) { 395 if (!strncmp(argv[i], "--sha1=", 7)) {
395 ctx.qry.sha1 = xstrdup(argv[i]+7); 396 ctx.qry.sha1 = xstrdup(argv[i]+7);
396 ctx.qry.has_sha1 = 1; 397 ctx.qry.has_sha1 = 1;
397 } 398 }
398 if (!strncmp(argv[i], "--ofs=", 6)) { 399 if (!strncmp(argv[i], "--ofs=", 6)) {
399 ctx.qry.ofs = atoi(argv[i]+6); 400 ctx.qry.ofs = atoi(argv[i]+6);
400 } 401 }
401 if (!strncmp(argv[i], "--scan-tree=", 12)) { 402 if (!strncmp(argv[i], "--scan-tree=", 12)) {
402 scan++; 403 scan++;
403 scan_tree(argv[i] + 12); 404 scan_tree(argv[i] + 12);
404 } 405 }
405 } 406 }
406 if (scan) { 407 if (scan) {
407 qsort(cgit_repolist.repos, cgit_repolist.count, 408 qsort(cgit_repolist.repos, cgit_repolist.count,
408 sizeof(struct cgit_repo), cmp_repos); 409 sizeof(struct cgit_repo), cmp_repos);
409 print_repolist(&cgit_repolist); 410 print_repolist(&cgit_repolist);
410 exit(0); 411 exit(0);
411 } 412 }
412} 413}
413 414
414static int calc_ttl() 415static int calc_ttl()
415{ 416{
416 if (!ctx.repo) 417 if (!ctx.repo)
417 return ctx.cfg.cache_root_ttl; 418 return ctx.cfg.cache_root_ttl;
418 419
419 if (!ctx.qry.page) 420 if (!ctx.qry.page)
420 return ctx.cfg.cache_repo_ttl; 421 return ctx.cfg.cache_repo_ttl;
421 422
422 if (ctx.qry.has_symref) 423 if (ctx.qry.has_symref)
423 return ctx.cfg.cache_dynamic_ttl; 424 return ctx.cfg.cache_dynamic_ttl;
424 425
425 if (ctx.qry.has_sha1) 426 if (ctx.qry.has_sha1)
426 return ctx.cfg.cache_static_ttl; 427 return ctx.cfg.cache_static_ttl;
427 428
428 return ctx.cfg.cache_repo_ttl; 429 return ctx.cfg.cache_repo_ttl;
429} 430}
430 431
431int main(int argc, const char **argv) 432int main(int argc, const char **argv)
432{ 433{
433 const char *cgit_config_env = getenv("CGIT_CONFIG"); 434 const char *cgit_config_env = getenv("CGIT_CONFIG");
435 const char *method = getenv("REQUEST_METHOD");
434 const char *path; 436 const char *path;
435 char *qry; 437 char *qry;
436 int err, ttl; 438 int err, ttl;
437 439
438 prepare_context(&ctx); 440 prepare_context(&ctx);
439 cgit_repolist.length = 0; 441 cgit_repolist.length = 0;
440 cgit_repolist.count = 0; 442 cgit_repolist.count = 0;
441 cgit_repolist.repos = NULL; 443 cgit_repolist.repos = NULL;
442 444
443 if (getenv("SCRIPT_NAME")) 445 if (getenv("SCRIPT_NAME"))
444 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME")); 446 ctx.cfg.script_name = xstrdup(getenv("SCRIPT_NAME"));
445 if (getenv("QUERY_STRING")) 447 if (getenv("QUERY_STRING"))
446 ctx.qry.raw = xstrdup(getenv("QUERY_STRING")); 448 ctx.qry.raw = xstrdup(getenv("QUERY_STRING"));
447 cgit_parse_args(argc, argv); 449 cgit_parse_args(argc, argv);
448 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 450 parse_configfile(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
449 config_cb); 451 config_cb);
450 ctx.repo = NULL; 452 ctx.repo = NULL;
451 http_parse_querystring(ctx.qry.raw, querystring_cb); 453 http_parse_querystring(ctx.qry.raw, querystring_cb);
452 454
453 /* If virtual-root isn't specified in cgitrc, lets pretend 455 /* If virtual-root isn't specified in cgitrc, lets pretend
454 * that virtual-root equals SCRIPT_NAME. 456 * that virtual-root equals SCRIPT_NAME.
455 */ 457 */
456 if (!ctx.cfg.virtual_root) 458 if (!ctx.cfg.virtual_root)
457 ctx.cfg.virtual_root = ctx.cfg.script_name; 459 ctx.cfg.virtual_root = ctx.cfg.script_name;
458 460
459 /* If no url parameter is specified on the querystring, lets 461 /* If no url parameter is specified on the querystring, lets
460 * use PATH_INFO as url. This allows cgit to work with virtual 462 * use PATH_INFO as url. This allows cgit to work with virtual
461 * urls without the need for rewriterules in the webserver (as 463 * urls without the need for rewriterules in the webserver (as
462 * long as PATH_INFO is included in the cache lookup key). 464 * long as PATH_INFO is included in the cache lookup key).
463 */ 465 */
464 path = getenv("PATH_INFO"); 466 path = getenv("PATH_INFO");
465 if (!ctx.qry.url && path) { 467 if (!ctx.qry.url && path) {
466 if (path[0] == '/') 468 if (path[0] == '/')
467 path++; 469 path++;
468 ctx.qry.url = xstrdup(path); 470 ctx.qry.url = xstrdup(path);
469 if (ctx.qry.raw) { 471 if (ctx.qry.raw) {
470 qry = ctx.qry.raw; 472 qry = ctx.qry.raw;
471 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 473 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
472 free(qry); 474 free(qry);
473 } else 475 } else
474 ctx.qry.raw = ctx.qry.url; 476 ctx.qry.raw = ctx.qry.url;
475 cgit_parse_url(ctx.qry.url); 477 cgit_parse_url(ctx.qry.url);
476 } 478 }
477 479
478 ttl = calc_ttl(); 480 ttl = calc_ttl();
479 ctx.page.expires += ttl*60; 481 ctx.page.expires += ttl*60;
482 if (method && !strcmp(method, "HEAD"))
483 ctx.cfg.nocache = 1;
480 if (ctx.cfg.nocache) 484 if (ctx.cfg.nocache)
481 ctx.cfg.cache_size = 0; 485 ctx.cfg.cache_size = 0;
482 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 486 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
483 ctx.qry.raw, ttl, process_request, &ctx); 487 ctx.qry.raw, ttl, process_request, &ctx);
484 if (err) 488 if (err)
485 cgit_print_error(fmt("Error processing page: %s (%d)", 489 cgit_print_error(fmt("Error processing page: %s (%d)",
486 strerror(err), err)); 490 strerror(err), err));
487 return err; 491 return err;
488} 492}
diff --git a/cgit.h b/cgit.h
index 00aca4c..07a277a 100644
--- a/cgit.h
+++ b/cgit.h
@@ -135,96 +135,97 @@ struct cgit_config {
135 char *clone_prefix; 135 char *clone_prefix;
136 char *css; 136 char *css;
137 char *favicon; 137 char *favicon;
138 char *footer; 138 char *footer;
139 char *header; 139 char *header;
140 char *index_header; 140 char *index_header;
141 char *index_info; 141 char *index_info;
142 char *logo; 142 char *logo;
143 char *logo_link; 143 char *logo_link;
144 char *module_link; 144 char *module_link;
145 char *repo_group; 145 char *repo_group;
146 char *robots; 146 char *robots;
147 char *root_title; 147 char *root_title;
148 char *root_desc; 148 char *root_desc;
149 char *root_readme; 149 char *root_readme;
150 char *script_name; 150 char *script_name;
151 char *virtual_root; 151 char *virtual_root;
152 int cache_size; 152 int cache_size;
153 int cache_dynamic_ttl; 153 int cache_dynamic_ttl;
154 int cache_max_create_time; 154 int cache_max_create_time;
155 int cache_repo_ttl; 155 int cache_repo_ttl;
156 int cache_root_ttl; 156 int cache_root_ttl;
157 int cache_static_ttl; 157 int cache_static_ttl;
158 int enable_index_links; 158 int enable_index_links;
159 int enable_log_filecount; 159 int enable_log_filecount;
160 int enable_log_linecount; 160 int enable_log_linecount;
161 int local_time; 161 int local_time;
162 int max_repo_count; 162 int max_repo_count;
163 int max_commit_count; 163 int max_commit_count;
164 int max_lock_attempts; 164 int max_lock_attempts;
165 int max_msg_len; 165 int max_msg_len;
166 int max_repodesc_len; 166 int max_repodesc_len;
167 int max_stats; 167 int max_stats;
168 int nocache; 168 int nocache;
169 int renamelimit; 169 int renamelimit;
170 int snapshots; 170 int snapshots;
171 int summary_branches; 171 int summary_branches;
172 int summary_log; 172 int summary_log;
173 int summary_tags; 173 int summary_tags;
174}; 174};
175 175
176struct cgit_page { 176struct cgit_page {
177 time_t modified; 177 time_t modified;
178 time_t expires; 178 time_t expires;
179 size_t size; 179 size_t size;
180 char *mimetype; 180 char *mimetype;
181 char *charset; 181 char *charset;
182 char *filename; 182 char *filename;
183 char *etag;
183 char *title; 184 char *title;
184 int status; 185 int status;
185 char *statusmsg; 186 char *statusmsg;
186}; 187};
187 188
188struct cgit_context { 189struct cgit_context {
189 struct cgit_query qry; 190 struct cgit_query qry;
190 struct cgit_config cfg; 191 struct cgit_config cfg;
191 struct cgit_repo *repo; 192 struct cgit_repo *repo;
192 struct cgit_page page; 193 struct cgit_page page;
193}; 194};
194 195
195struct cgit_snapshot_format { 196struct cgit_snapshot_format {
196 const char *suffix; 197 const char *suffix;
197 const char *mimetype; 198 const char *mimetype;
198 write_archive_fn_t write_func; 199 write_archive_fn_t write_func;
199 int bit; 200 int bit;
200}; 201};
201 202
202extern const char *cgit_version; 203extern const char *cgit_version;
203 204
204extern struct cgit_repolist cgit_repolist; 205extern struct cgit_repolist cgit_repolist;
205extern struct cgit_context ctx; 206extern struct cgit_context ctx;
206extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 207extern const struct cgit_snapshot_format cgit_snapshot_formats[];
207 208
208extern struct cgit_repo *cgit_add_repo(const char *url); 209extern struct cgit_repo *cgit_add_repo(const char *url);
209extern struct cgit_repo *cgit_get_repoinfo(const char *url); 210extern struct cgit_repo *cgit_get_repoinfo(const char *url);
210extern void cgit_repo_config_cb(const char *name, const char *value); 211extern void cgit_repo_config_cb(const char *name, const char *value);
211 212
212extern int chk_zero(int result, char *msg); 213extern int chk_zero(int result, char *msg);
213extern int chk_positive(int result, char *msg); 214extern int chk_positive(int result, char *msg);
214extern int chk_non_negative(int result, char *msg); 215extern int chk_non_negative(int result, char *msg);
215 216
216extern char *trim_end(const char *str, char c); 217extern char *trim_end(const char *str, char c);
217extern char *strlpart(char *txt, int maxlen); 218extern char *strlpart(char *txt, int maxlen);
218extern char *strrpart(char *txt, int maxlen); 219extern char *strrpart(char *txt, int maxlen);
219 220
220extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 221extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
221extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 222extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
222 int flags, void *cb_data); 223 int flags, void *cb_data);
223 224
224extern void *cgit_free_commitinfo(struct commitinfo *info); 225extern void *cgit_free_commitinfo(struct commitinfo *info);
225 226
226extern int cgit_diff_files(const unsigned char *old_sha1, 227extern int cgit_diff_files(const unsigned char *old_sha1,
227 const unsigned char *new_sha1, 228 const unsigned char *new_sha1,
228 unsigned long *old_size, unsigned long *new_size, 229 unsigned long *old_size, unsigned long *new_size,
229 int *binary, linediff_fn fn); 230 int *binary, linediff_fn fn);
230 231
diff --git a/ui-plain.c b/ui-plain.c
index 9a9ae7d..93a3a05 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -1,82 +1,83 @@
1/* ui-plain.c: functions for output of plain blobs by path 1/* ui-plain.c: functions for output of plain blobs by path
2 * 2 *
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13char *curr_rev; 13char *curr_rev;
14char *match_path; 14char *match_path;
15int match; 15int match;
16 16
17static void print_object(const unsigned char *sha1, const char *path) 17static void print_object(const unsigned char *sha1, const char *path)
18{ 18{
19 enum object_type type; 19 enum object_type type;
20 char *buf; 20 char *buf;
21 unsigned long size; 21 unsigned long size;
22 22
23 type = sha1_object_info(sha1, &size); 23 type = sha1_object_info(sha1, &size);
24 if (type == OBJ_BAD) { 24 if (type == OBJ_BAD) {
25 html_status(404, "Not found", 0); 25 html_status(404, "Not found", 0);
26 return; 26 return;
27 } 27 }
28 28
29 buf = read_sha1_file(sha1, &type, &size); 29 buf = read_sha1_file(sha1, &type, &size);
30 if (!buf) { 30 if (!buf) {
31 html_status(404, "Not found", 0); 31 html_status(404, "Not found", 0);
32 return; 32 return;
33 } 33 }
34 if (buffer_is_binary(buf, size)) 34 if (buffer_is_binary(buf, size))
35 ctx.page.mimetype = "application/octet-stream"; 35 ctx.page.mimetype = "application/octet-stream";
36 else 36 else
37 ctx.page.mimetype = "text/plain"; 37 ctx.page.mimetype = "text/plain";
38 ctx.page.filename = fmt("%s", path); 38 ctx.page.filename = fmt("%s", path);
39 ctx.page.size = size; 39 ctx.page.size = size;
40 ctx.page.etag = sha1_to_hex(sha1);
40 cgit_print_http_headers(&ctx); 41 cgit_print_http_headers(&ctx);
41 html_raw(buf, size); 42 html_raw(buf, size);
42 match = 1; 43 match = 1;
43} 44}
44 45
45static int walk_tree(const unsigned char *sha1, const char *base, int baselen, 46static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
46 const char *pathname, unsigned mode, int stage, 47 const char *pathname, unsigned mode, int stage,
47 void *cbdata) 48 void *cbdata)
48{ 49{
49 if (S_ISDIR(mode)) 50 if (S_ISDIR(mode))
50 return READ_TREE_RECURSIVE; 51 return READ_TREE_RECURSIVE;
51 52
52 if (S_ISREG(mode)) 53 if (S_ISREG(mode))
53 print_object(sha1, pathname); 54 print_object(sha1, pathname);
54 55
55 return 0; 56 return 0;
56} 57}
57 58
58void cgit_print_plain(struct cgit_context *ctx) 59void cgit_print_plain(struct cgit_context *ctx)
59{ 60{
60 const char *rev = ctx->qry.sha1; 61 const char *rev = ctx->qry.sha1;
61 unsigned char sha1[20]; 62 unsigned char sha1[20];
62 struct commit *commit; 63 struct commit *commit;
63 const char *paths[] = {ctx->qry.path, NULL}; 64 const char *paths[] = {ctx->qry.path, NULL};
64 65
65 if (!rev) 66 if (!rev)
66 rev = ctx->qry.head; 67 rev = ctx->qry.head;
67 68
68 curr_rev = xstrdup(rev); 69 curr_rev = xstrdup(rev);
69 if (get_sha1(rev, sha1)) { 70 if (get_sha1(rev, sha1)) {
70 html_status(404, "Not found", 0); 71 html_status(404, "Not found", 0);
71 return; 72 return;
72 } 73 }
73 commit = lookup_commit_reference(sha1); 74 commit = lookup_commit_reference(sha1);
74 if (!commit || parse_commit(commit)) { 75 if (!commit || parse_commit(commit)) {
75 html_status(404, "Not found", 0); 76 html_status(404, "Not found", 0);
76 return; 77 return;
77 } 78 }
78 match_path = ctx->qry.path; 79 match_path = ctx->qry.path;
79 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); 80 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL);
80 if (!match) 81 if (!match)
81 html_status(404, "Not found", 0); 82 html_status(404, "Not found", 0);
82} 83}
diff --git a/ui-shared.c b/ui-shared.c
index 29036d0..10be3c0 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -422,111 +422,117 @@ void cgit_print_date(time_t secs, char *format, int local_time)
422 html_txt(buf); 422 html_txt(buf);
423} 423}
424 424
425void cgit_print_age(time_t t, time_t max_relative, char *format) 425void cgit_print_age(time_t t, time_t max_relative, char *format)
426{ 426{
427 time_t now, secs; 427 time_t now, secs;
428 428
429 if (!t) 429 if (!t)
430 return; 430 return;
431 time(&now); 431 time(&now);
432 secs = now - t; 432 secs = now - t;
433 433
434 if (secs > max_relative && max_relative >= 0) { 434 if (secs > max_relative && max_relative >= 0) {
435 cgit_print_date(t, format, ctx.cfg.local_time); 435 cgit_print_date(t, format, ctx.cfg.local_time);
436 return; 436 return;
437 } 437 }
438 438
439 if (secs < TM_HOUR * 2) { 439 if (secs < TM_HOUR * 2) {
440 htmlf("<span class='age-mins'>%.0f min.</span>", 440 htmlf("<span class='age-mins'>%.0f min.</span>",
441 secs * 1.0 / TM_MIN); 441 secs * 1.0 / TM_MIN);
442 return; 442 return;
443 } 443 }
444 if (secs < TM_DAY * 2) { 444 if (secs < TM_DAY * 2) {
445 htmlf("<span class='age-hours'>%.0f hours</span>", 445 htmlf("<span class='age-hours'>%.0f hours</span>",
446 secs * 1.0 / TM_HOUR); 446 secs * 1.0 / TM_HOUR);
447 return; 447 return;
448 } 448 }
449 if (secs < TM_WEEK * 2) { 449 if (secs < TM_WEEK * 2) {
450 htmlf("<span class='age-days'>%.0f days</span>", 450 htmlf("<span class='age-days'>%.0f days</span>",
451 secs * 1.0 / TM_DAY); 451 secs * 1.0 / TM_DAY);
452 return; 452 return;
453 } 453 }
454 if (secs < TM_MONTH * 2) { 454 if (secs < TM_MONTH * 2) {
455 htmlf("<span class='age-weeks'>%.0f weeks</span>", 455 htmlf("<span class='age-weeks'>%.0f weeks</span>",
456 secs * 1.0 / TM_WEEK); 456 secs * 1.0 / TM_WEEK);
457 return; 457 return;
458 } 458 }
459 if (secs < TM_YEAR * 2) { 459 if (secs < TM_YEAR * 2) {
460 htmlf("<span class='age-months'>%.0f months</span>", 460 htmlf("<span class='age-months'>%.0f months</span>",
461 secs * 1.0 / TM_MONTH); 461 secs * 1.0 / TM_MONTH);
462 return; 462 return;
463 } 463 }
464 htmlf("<span class='age-years'>%.0f years</span>", 464 htmlf("<span class='age-years'>%.0f years</span>",
465 secs * 1.0 / TM_YEAR); 465 secs * 1.0 / TM_YEAR);
466} 466}
467 467
468void cgit_print_http_headers(struct cgit_context *ctx) 468void cgit_print_http_headers(struct cgit_context *ctx)
469{ 469{
470 const char *method = getenv("REQUEST_METHOD");
471
470 if (ctx->page.status) 472 if (ctx->page.status)
471 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg); 473 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg);
472 if (ctx->page.mimetype && ctx->page.charset) 474 if (ctx->page.mimetype && ctx->page.charset)
473 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 475 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
474 ctx->page.charset); 476 ctx->page.charset);
475 else if (ctx->page.mimetype) 477 else if (ctx->page.mimetype)
476 htmlf("Content-Type: %s\n", ctx->page.mimetype); 478 htmlf("Content-Type: %s\n", ctx->page.mimetype);
477 if (ctx->page.size) 479 if (ctx->page.size)
478 htmlf("Content-Length: %ld\n", ctx->page.size); 480 htmlf("Content-Length: %ld\n", ctx->page.size);
479 if (ctx->page.filename) 481 if (ctx->page.filename)
480 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 482 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
481 ctx->page.filename); 483 ctx->page.filename);
482 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 484 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
483 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 485 htmlf("Expires: %s\n", http_date(ctx->page.expires));
486 if (ctx->page.etag)
487 htmlf("ETag: \"%s\"\n", ctx->page.etag);
484 html("\n"); 488 html("\n");
489 if (method && !strcmp(method, "HEAD"))
490 exit(0);
485} 491}
486 492
487void cgit_print_docstart(struct cgit_context *ctx) 493void cgit_print_docstart(struct cgit_context *ctx)
488{ 494{
489 char *host = cgit_hosturl(); 495 char *host = cgit_hosturl();
490 html(cgit_doctype); 496 html(cgit_doctype);
491 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 497 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
492 html("<head>\n"); 498 html("<head>\n");
493 html("<title>"); 499 html("<title>");
494 html_txt(ctx->page.title); 500 html_txt(ctx->page.title);
495 html("</title>\n"); 501 html("</title>\n");
496 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 502 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
497 if (ctx->cfg.robots && *ctx->cfg.robots) 503 if (ctx->cfg.robots && *ctx->cfg.robots)
498 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 504 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
499 html("<link rel='stylesheet' type='text/css' href='"); 505 html("<link rel='stylesheet' type='text/css' href='");
500 html_attr(ctx->cfg.css); 506 html_attr(ctx->cfg.css);
501 html("'/>\n"); 507 html("'/>\n");
502 if (ctx->cfg.favicon) { 508 if (ctx->cfg.favicon) {
503 html("<link rel='shortcut icon' href='"); 509 html("<link rel='shortcut icon' href='");
504 html_attr(ctx->cfg.favicon); 510 html_attr(ctx->cfg.favicon);
505 html("'/>\n"); 511 html("'/>\n");
506 } 512 }
507 if (host && ctx->repo) { 513 if (host && ctx->repo) {
508 html("<link rel='alternate' title='Atom feed' href='"); 514 html("<link rel='alternate' title='Atom feed' href='");
509 html(cgit_httpscheme()); 515 html(cgit_httpscheme());
510 html_attr(cgit_hosturl()); 516 html_attr(cgit_hosturl());
511 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path, 517 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path,
512 fmt("h=%s", ctx->qry.head))); 518 fmt("h=%s", ctx->qry.head)));
513 html("' type='application/atom+xml'/>"); 519 html("' type='application/atom+xml'/>");
514 } 520 }
515 html("</head>\n"); 521 html("</head>\n");
516 html("<body>\n"); 522 html("<body>\n");
517 if (ctx->cfg.header) 523 if (ctx->cfg.header)
518 html_include(ctx->cfg.header); 524 html_include(ctx->cfg.header);
519} 525}
520 526
521void cgit_print_docend() 527void cgit_print_docend()
522{ 528{
523 html("</div>"); 529 html("</div>");
524 if (ctx.cfg.footer) 530 if (ctx.cfg.footer)
525 html_include(ctx.cfg.footer); 531 html_include(ctx.cfg.footer);
526 else { 532 else {
527 htmlf("<div class='footer'>generated by cgit %s at ", 533 htmlf("<div class='footer'>generated by cgit %s at ",
528 cgit_version); 534 cgit_version);
529 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 535 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
530 html("</div>\n"); 536 html("</div>\n");
531 } 537 }
532 html("</body>\n</html>\n"); 538 html("</body>\n</html>\n");