summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-09-01 20:40:55 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-09-01 20:40:55 (UTC)
commitd532c4d1612c94347427fa1afda6afb7c34e512a (patch) (unidiff)
tree53f3f86ba8e78051bee96cb65a6219ef43d9adab
parent288d502b3d8e7fa916104b486bbb146521e5c716 (diff)
parent885096c189574b1cf2e0897cc05aadd7b092a677 (diff)
downloadcgit-d532c4d1612c94347427fa1afda6afb7c34e512a.zip
cgit-d532c4d1612c94347427fa1afda6afb7c34e512a.tar.gz
cgit-d532c4d1612c94347427fa1afda6afb7c34e512a.tar.bz2
Merge branch 'lh/plain'
* lh/plain: Supply status description to html_status() ui-tree: link to plain view instead of blob view Implement plain view
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile1
-rw-r--r--cgit.c1
-rw-r--r--cgit.h1
-rw-r--r--cmd.c7
-rw-r--r--html.c9
-rw-r--r--html.h3
-rw-r--r--ui-clone.c10
-rw-r--r--ui-plain.c82
-rw-r--r--ui-plain.h6
-rw-r--r--ui-shared.c8
-rw-r--r--ui-shared.h2
-rw-r--r--ui-tree.c8
12 files changed, 125 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index b002d44..e4265f7 100644
--- a/Makefile
+++ b/Makefile
@@ -53,24 +53,25 @@ OBJECTS += cgit.o
53OBJECTS += cmd.o 53OBJECTS += cmd.o
54OBJECTS += configfile.o 54OBJECTS += configfile.o
55OBJECTS += html.o 55OBJECTS += html.o
56OBJECTS += parsing.o 56OBJECTS += parsing.o
57OBJECTS += shared.o 57OBJECTS += shared.o
58OBJECTS += ui-atom.o 58OBJECTS += ui-atom.o
59OBJECTS += ui-blob.o 59OBJECTS += ui-blob.o
60OBJECTS += ui-clone.o 60OBJECTS += ui-clone.o
61OBJECTS += ui-commit.o 61OBJECTS += ui-commit.o
62OBJECTS += ui-diff.o 62OBJECTS += ui-diff.o
63OBJECTS += ui-log.o 63OBJECTS += ui-log.o
64OBJECTS += ui-patch.o 64OBJECTS += ui-patch.o
65OBJECTS += ui-plain.o
65OBJECTS += ui-refs.o 66OBJECTS += ui-refs.o
66OBJECTS += ui-repolist.o 67OBJECTS += ui-repolist.o
67OBJECTS += ui-shared.o 68OBJECTS += ui-shared.o
68OBJECTS += ui-snapshot.o 69OBJECTS += ui-snapshot.o
69OBJECTS += ui-summary.o 70OBJECTS += ui-summary.o
70OBJECTS += ui-tag.o 71OBJECTS += ui-tag.o
71OBJECTS += ui-tree.o 72OBJECTS += ui-tree.o
72 73
73ifdef NEEDS_LIBICONV 74ifdef NEEDS_LIBICONV
74 EXTLIBS += -liconv 75 EXTLIBS += -liconv
75endif 76endif
76 77
diff --git a/cgit.c b/cgit.c
index f49fffa..497337b 100644
--- a/cgit.c
+++ b/cgit.c
@@ -178,24 +178,25 @@ static void prepare_context(struct cgit_context *ctx)
178 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 178 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
179 ctx->cfg.renamelimit = -1; 179 ctx->cfg.renamelimit = -1;
180 ctx->cfg.robots = "index, nofollow"; 180 ctx->cfg.robots = "index, nofollow";
181 ctx->cfg.root_title = "Git repository browser"; 181 ctx->cfg.root_title = "Git repository browser";
182 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 182 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
183 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 183 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
184 ctx->cfg.summary_branches = 10; 184 ctx->cfg.summary_branches = 10;
185 ctx->cfg.summary_log = 10; 185 ctx->cfg.summary_log = 10;
186 ctx->cfg.summary_tags = 10; 186 ctx->cfg.summary_tags = 10;
187 ctx->page.mimetype = "text/html"; 187 ctx->page.mimetype = "text/html";
188 ctx->page.charset = PAGE_ENCODING; 188 ctx->page.charset = PAGE_ENCODING;
189 ctx->page.filename = NULL; 189 ctx->page.filename = NULL;
190 ctx->page.size = 0;
190 ctx->page.modified = time(NULL); 191 ctx->page.modified = time(NULL);
191 ctx->page.expires = ctx->page.modified; 192 ctx->page.expires = ctx->page.modified;
192} 193}
193 194
194struct refmatch { 195struct refmatch {
195 char *req_ref; 196 char *req_ref;
196 char *first_ref; 197 char *first_ref;
197 int match; 198 int match;
198}; 199};
199 200
200int find_current_ref(const char *refname, const unsigned char *sha1, 201int find_current_ref(const char *refname, const unsigned char *sha1,
201 int flags, void *cb_data) 202 int flags, void *cb_data)
diff --git a/cgit.h b/cgit.h
index a1fa841..1615616 100644
--- a/cgit.h
+++ b/cgit.h
@@ -157,24 +157,25 @@ struct cgit_config {
157 int max_repodesc_len; 157 int max_repodesc_len;
158 int nocache; 158 int nocache;
159 int renamelimit; 159 int renamelimit;
160 int snapshots; 160 int snapshots;
161 int summary_branches; 161 int summary_branches;
162 int summary_log; 162 int summary_log;
163 int summary_tags; 163 int summary_tags;
164}; 164};
165 165
166struct cgit_page { 166struct cgit_page {
167 time_t modified; 167 time_t modified;
168 time_t expires; 168 time_t expires;
169 size_t size;
169 char *mimetype; 170 char *mimetype;
170 char *charset; 171 char *charset;
171 char *filename; 172 char *filename;
172 char *title; 173 char *title;
173}; 174};
174 175
175struct cgit_context { 176struct cgit_context {
176 struct cgit_query qry; 177 struct cgit_query qry;
177 struct cgit_config cfg; 178 struct cgit_config cfg;
178 struct cgit_repo *repo; 179 struct cgit_repo *repo;
179 struct cgit_page page; 180 struct cgit_page page;
180}; 181};
diff --git a/cmd.c b/cmd.c
index 40ac53e..a989220 100644
--- a/cmd.c
+++ b/cmd.c
@@ -8,24 +8,25 @@
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cmd.h" 10#include "cmd.h"
11#include "cache.h" 11#include "cache.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13#include "ui-atom.h" 13#include "ui-atom.h"
14#include "ui-blob.h" 14#include "ui-blob.h"
15#include "ui-clone.h" 15#include "ui-clone.h"
16#include "ui-commit.h" 16#include "ui-commit.h"
17#include "ui-diff.h" 17#include "ui-diff.h"
18#include "ui-log.h" 18#include "ui-log.h"
19#include "ui-patch.h" 19#include "ui-patch.h"
20#include "ui-plain.h"
20#include "ui-refs.h" 21#include "ui-refs.h"
21#include "ui-repolist.h" 22#include "ui-repolist.h"
22#include "ui-snapshot.h" 23#include "ui-snapshot.h"
23#include "ui-summary.h" 24#include "ui-summary.h"
24#include "ui-tag.h" 25#include "ui-tag.h"
25#include "ui-tree.h" 26#include "ui-tree.h"
26 27
27static void HEAD_fn(struct cgit_context *ctx) 28static void HEAD_fn(struct cgit_context *ctx)
28{ 29{
29 cgit_clone_head(ctx); 30 cgit_clone_head(ctx);
30} 31}
31 32
@@ -82,24 +83,29 @@ static void objects_fn(struct cgit_context *ctx)
82} 83}
83 84
84static void repolist_fn(struct cgit_context *ctx) 85static void repolist_fn(struct cgit_context *ctx)
85{ 86{
86 cgit_print_repolist(); 87 cgit_print_repolist();
87} 88}
88 89
89static void patch_fn(struct cgit_context *ctx) 90static void patch_fn(struct cgit_context *ctx)
90{ 91{
91 cgit_print_patch(ctx->qry.sha1); 92 cgit_print_patch(ctx->qry.sha1);
92} 93}
93 94
95static void plain_fn(struct cgit_context *ctx)
96{
97 cgit_print_plain(ctx);
98}
99
94static void refs_fn(struct cgit_context *ctx) 100static void refs_fn(struct cgit_context *ctx)
95{ 101{
96 cgit_print_refs(); 102 cgit_print_refs();
97} 103}
98 104
99static void snapshot_fn(struct cgit_context *ctx) 105static void snapshot_fn(struct cgit_context *ctx)
100{ 106{
101 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, 107 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1,
102 cgit_repobasename(ctx->repo->url), ctx->qry.path, 108 cgit_repobasename(ctx->repo->url), ctx->qry.path,
103 ctx->repo->snapshots); 109 ctx->repo->snapshots);
104} 110}
105 111
@@ -126,24 +132,25 @@ struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
126 static struct cgit_cmd cmds[] = { 132 static struct cgit_cmd cmds[] = {
127 def_cmd(HEAD, 1, 0), 133 def_cmd(HEAD, 1, 0),
128 def_cmd(atom, 1, 0), 134 def_cmd(atom, 1, 0),
129 def_cmd(about, 0, 1), 135 def_cmd(about, 0, 1),
130 def_cmd(blob, 1, 0), 136 def_cmd(blob, 1, 0),
131 def_cmd(commit, 1, 1), 137 def_cmd(commit, 1, 1),
132 def_cmd(diff, 1, 1), 138 def_cmd(diff, 1, 1),
133 def_cmd(info, 1, 0), 139 def_cmd(info, 1, 0),
134 def_cmd(log, 1, 1), 140 def_cmd(log, 1, 1),
135 def_cmd(ls_cache, 0, 0), 141 def_cmd(ls_cache, 0, 0),
136 def_cmd(objects, 1, 0), 142 def_cmd(objects, 1, 0),
137 def_cmd(patch, 1, 0), 143 def_cmd(patch, 1, 0),
144 def_cmd(plain, 1, 0),
138 def_cmd(refs, 1, 1), 145 def_cmd(refs, 1, 1),
139 def_cmd(repolist, 0, 0), 146 def_cmd(repolist, 0, 0),
140 def_cmd(snapshot, 1, 0), 147 def_cmd(snapshot, 1, 0),
141 def_cmd(summary, 1, 1), 148 def_cmd(summary, 1, 1),
142 def_cmd(tag, 1, 1), 149 def_cmd(tag, 1, 1),
143 def_cmd(tree, 1, 1), 150 def_cmd(tree, 1, 1),
144 }; 151 };
145 int i; 152 int i;
146 153
147 if (ctx->qry.page == NULL) { 154 if (ctx->qry.page == NULL) {
148 if (ctx->repo) 155 if (ctx->repo)
149 ctx->qry.page = "summary"; 156 ctx->qry.page = "summary";
diff --git a/html.c b/html.c
index 1237076..36e9a2f 100644
--- a/html.c
+++ b/html.c
@@ -26,43 +26,48 @@ char *fmt(const char *format, ...)
26 bufidx &= 7; 26 bufidx &= 7;
27 27
28 va_start(args, format); 28 va_start(args, format);
29 len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args); 29 len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
30 va_end(args); 30 va_end(args);
31 if (len>sizeof(buf[bufidx])) { 31 if (len>sizeof(buf[bufidx])) {
32 fprintf(stderr, "[html.c] string truncated: %s\n", format); 32 fprintf(stderr, "[html.c] string truncated: %s\n", format);
33 exit(1); 33 exit(1);
34 } 34 }
35 return buf[bufidx]; 35 return buf[bufidx];
36} 36}
37 37
38void html_raw(const char *data, size_t size)
39{
40 write(htmlfd, data, size);
41}
42
38void html(const char *txt) 43void html(const char *txt)
39{ 44{
40 write(htmlfd, txt, strlen(txt)); 45 write(htmlfd, txt, strlen(txt));
41} 46}
42 47
43void htmlf(const char *format, ...) 48void htmlf(const char *format, ...)
44{ 49{
45 static char buf[65536]; 50 static char buf[65536];
46 va_list args; 51 va_list args;
47 52
48 va_start(args, format); 53 va_start(args, format);
49 vsnprintf(buf, sizeof(buf), format, args); 54 vsnprintf(buf, sizeof(buf), format, args);
50 va_end(args); 55 va_end(args);
51 html(buf); 56 html(buf);
52} 57}
53 58
54void html_status(int code, int more_headers) 59void html_status(int code, const char *msg, int more_headers)
55{ 60{
56 htmlf("Status: %d\n", code); 61 htmlf("Status: %d %s\n", code, msg);
57 if (!more_headers) 62 if (!more_headers)
58 html("\n"); 63 html("\n");
59} 64}
60 65
61void html_txt(char *txt) 66void html_txt(char *txt)
62{ 67{
63 char *t = txt; 68 char *t = txt;
64 while(t && *t){ 69 while(t && *t){
65 int c = *t; 70 int c = *t;
66 if (c=='<' || c=='>' || c=='&') { 71 if (c=='<' || c=='>' || c=='&') {
67 write(htmlfd, txt, t - txt); 72 write(htmlfd, txt, t - txt);
68 if (c=='>') 73 if (c=='>')
diff --git a/html.h b/html.h
index 2bde28d..3c32935 100644
--- a/html.h
+++ b/html.h
@@ -1,20 +1,21 @@
1#ifndef HTML_H 1#ifndef HTML_H
2#define HTML_H 2#define HTML_H
3 3
4extern int htmlfd; 4extern int htmlfd;
5 5
6extern void html_raw(const char *txt, size_t size);
6extern void html(const char *txt); 7extern void html(const char *txt);
7extern void htmlf(const char *format,...); 8extern void htmlf(const char *format,...);
8extern void html_status(int code, int more_headers); 9extern void html_status(int code, const char *msg, int more_headers);
9extern void html_txt(char *txt); 10extern void html_txt(char *txt);
10extern void html_ntxt(int len, char *txt); 11extern void html_ntxt(int len, char *txt);
11extern void html_attr(char *txt); 12extern void html_attr(char *txt);
12extern void html_hidden(char *name, char *value); 13extern void html_hidden(char *name, char *value);
13extern void html_option(char *value, char *text, char *selected_value); 14extern void html_option(char *value, char *text, char *selected_value);
14extern void html_link_open(char *url, char *title, char *class); 15extern void html_link_open(char *url, char *title, char *class);
15extern void html_link_close(void); 16extern void html_link_close(void);
16extern void html_fileperm(unsigned short mode); 17extern void html_fileperm(unsigned short mode);
17extern int html_include(const char *filename); 18extern int html_include(const char *filename);
18 19
19extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value)); 20extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value));
20 21
diff --git a/ui-clone.c b/ui-clone.c
index 3a037ad..81e7a4e 100644
--- a/ui-clone.c
+++ b/ui-clone.c
@@ -39,63 +39,61 @@ static void print_pack_info(struct cgit_context *ctx)
39 ctx->page.filename = "objects/info/packs"; 39 ctx->page.filename = "objects/info/packs";
40 cgit_print_http_headers(ctx); 40 cgit_print_http_headers(ctx);
41 ofs = strlen(ctx->repo->path) + strlen("/objects/pack/"); 41 ofs = strlen(ctx->repo->path) + strlen("/objects/pack/");
42 prepare_packed_git(); 42 prepare_packed_git();
43 for (pack = packed_git; pack; pack = pack->next) 43 for (pack = packed_git; pack; pack = pack->next)
44 if (pack->pack_local) 44 if (pack->pack_local)
45 htmlf("P %s\n", pack->pack_name + ofs); 45 htmlf("P %s\n", pack->pack_name + ofs);
46} 46}
47 47
48static void send_file(struct cgit_context *ctx, char *path) 48static void send_file(struct cgit_context *ctx, char *path)
49{ 49{
50 struct stat st; 50 struct stat st;
51 int err;
52 51
53 if (stat(path, &st)) { 52 if (stat(path, &st)) {
54 switch (errno) { 53 switch (errno) {
55 case ENOENT: 54 case ENOENT:
56 err = 404; 55 html_status(404, "Not found", 0);
57 break; 56 break;
58 case EACCES: 57 case EACCES:
59 err = 403; 58 html_status(403, "Forbidden", 0);
60 break; 59 break;
61 default: 60 default:
62 err = 400; 61 html_status(400, "Bad request", 0);
63 } 62 }
64 html_status(err, 0);
65 return; 63 return;
66 } 64 }
67 ctx->page.mimetype = "application/octet-stream"; 65 ctx->page.mimetype = "application/octet-stream";
68 ctx->page.filename = path; 66 ctx->page.filename = path;
69 if (prefixcmp(ctx->repo->path, path)) 67 if (prefixcmp(ctx->repo->path, path))
70 ctx->page.filename += strlen(ctx->repo->path) + 1; 68 ctx->page.filename += strlen(ctx->repo->path) + 1;
71 cgit_print_http_headers(ctx); 69 cgit_print_http_headers(ctx);
72 html_include(path); 70 html_include(path);
73} 71}
74 72
75void cgit_clone_info(struct cgit_context *ctx) 73void cgit_clone_info(struct cgit_context *ctx)
76{ 74{
77 if (!ctx->qry.path || strcmp(ctx->qry.path, "refs")) 75 if (!ctx->qry.path || strcmp(ctx->qry.path, "refs"))
78 return; 76 return;
79 77
80 ctx->page.mimetype = "text/plain"; 78 ctx->page.mimetype = "text/plain";
81 ctx->page.filename = "info/refs"; 79 ctx->page.filename = "info/refs";
82 cgit_print_http_headers(ctx); 80 cgit_print_http_headers(ctx);
83 for_each_ref(print_ref_info, ctx); 81 for_each_ref(print_ref_info, ctx);
84} 82}
85 83
86void cgit_clone_objects(struct cgit_context *ctx) 84void cgit_clone_objects(struct cgit_context *ctx)
87{ 85{
88 if (!ctx->qry.path) { 86 if (!ctx->qry.path) {
89 html_status(400, 0); 87 html_status(400, "Bad request", 0);
90 return; 88 return;
91 } 89 }
92 90
93 if (!strcmp(ctx->qry.path, "info/packs")) { 91 if (!strcmp(ctx->qry.path, "info/packs")) {
94 print_pack_info(ctx); 92 print_pack_info(ctx);
95 return; 93 return;
96 } 94 }
97 95
98 send_file(ctx, git_path("objects/%s", ctx->qry.path)); 96 send_file(ctx, git_path("objects/%s", ctx->qry.path));
99} 97}
100 98
101void cgit_clone_head(struct cgit_context *ctx) 99void cgit_clone_head(struct cgit_context *ctx)
diff --git a/ui-plain.c b/ui-plain.c
new file mode 100644
index 0000000..35888a0
--- a/dev/null
+++ b/ui-plain.c
@@ -0,0 +1,82 @@
1/* ui-plain.c: functions for output of plain blobs by path
2 *
3 * Copyright (C) 2008 Lars Hjemli
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9#include "cgit.h"
10#include "html.h"
11#include "ui-shared.h"
12
13char *curr_rev;
14char *match_path;
15int match;
16
17static void print_object(const unsigned char *sha1, const char *path)
18{
19 enum object_type type;
20 char *buf;
21 size_t size;
22
23 type = sha1_object_info(sha1, &size);
24 if (type == OBJ_BAD) {
25 html_status(404, "Not found", 0);
26 return;
27 }
28
29 buf = read_sha1_file(sha1, &type, &size);
30 if (!buf) {
31 html_status(404, "Not found", 0);
32 return;
33 }
34 ctx.page.mimetype = "text/plain";
35 ctx.page.filename = fmt("%s", path);
36 ctx.page.size = size;
37 cgit_print_http_headers(&ctx);
38 html_raw(buf, size);
39 match = 1;
40}
41
42static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
43 const char *pathname, unsigned mode, int stage,
44 void *cbdata)
45{
46 fprintf(stderr, "[cgit] walk_tree.pathname=%s", pathname);
47
48 if (!pathname || strcmp(match_path, pathname))
49 return READ_TREE_RECURSIVE;
50
51 if (S_ISREG(mode))
52 print_object(sha1, pathname);
53
54 return 0;
55}
56
57void cgit_print_plain(struct cgit_context *ctx)
58{
59 const char *rev = ctx->qry.sha1;
60 unsigned char sha1[20];
61 struct commit *commit;
62 const char *paths[] = {ctx->qry.path, NULL};
63
64 if (!rev)
65 rev = ctx->qry.head;
66
67 curr_rev = xstrdup(rev);
68 if (get_sha1(rev, sha1)) {
69 html_status(404, "Not found", 0);
70 return;
71 }
72 commit = lookup_commit_reference(sha1);
73 if (!commit || parse_commit(commit)) {
74 html_status(404, "Not found", 0);
75 return;
76 }
77 match_path = ctx->qry.path;
78 fprintf(stderr, "[cgit] match_path=%s", match_path);
79 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL);
80 if (!match)
81 html_status(404, "Not found", 0);
82}
diff --git a/ui-plain.h b/ui-plain.h
new file mode 100644
index 0000000..4373118
--- a/dev/null
+++ b/ui-plain.h
@@ -0,0 +1,6 @@
1#ifndef UI_PLAIN_H
2#define UI_PLAIN_H
3
4extern void cgit_print_plain(struct cgit_context *ctx);
5
6#endif /* UI_PLAIN_H */
diff --git a/ui-shared.c b/ui-shared.c
index 37c60b2..4818e70 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -249,24 +249,30 @@ static void reporevlink(char *page, char *name, char *title, char *class,
249 } 249 }
250 html("'>"); 250 html("'>");
251 html_txt(name); 251 html_txt(name);
252 html("</a>"); 252 html("</a>");
253} 253}
254 254
255void cgit_tree_link(char *name, char *title, char *class, char *head, 255void cgit_tree_link(char *name, char *title, char *class, char *head,
256 char *rev, char *path) 256 char *rev, char *path)
257{ 257{
258 reporevlink("tree", name, title, class, head, rev, path); 258 reporevlink("tree", name, title, class, head, rev, path);
259} 259}
260 260
261void cgit_plain_link(char *name, char *title, char *class, char *head,
262 char *rev, char *path)
263{
264 reporevlink("plain", name, title, class, head, rev, path);
265}
266
261void cgit_log_link(char *name, char *title, char *class, char *head, 267void cgit_log_link(char *name, char *title, char *class, char *head,
262 char *rev, char *path, int ofs, char *grep, char *pattern) 268 char *rev, char *path, int ofs, char *grep, char *pattern)
263{ 269{
264 char *delim; 270 char *delim;
265 271
266 delim = repolink(title, class, "log", head, path); 272 delim = repolink(title, class, "log", head, path);
267 if (rev && strcmp(rev, ctx.qry.head)) { 273 if (rev && strcmp(rev, ctx.qry.head)) {
268 html(delim); 274 html(delim);
269 html("id="); 275 html("id=");
270 html_attr(rev); 276 html_attr(rev);
271 delim = "&"; 277 delim = "&";
272 } 278 }
@@ -424,24 +430,26 @@ void cgit_print_age(time_t t, time_t max_relative, char *format)
424 } 430 }
425 htmlf("<span class='age-years'>%.0f years</span>", 431 htmlf("<span class='age-years'>%.0f years</span>",
426 secs * 1.0 / TM_YEAR); 432 secs * 1.0 / TM_YEAR);
427} 433}
428 434
429void cgit_print_http_headers(struct cgit_context *ctx) 435void cgit_print_http_headers(struct cgit_context *ctx)
430{ 436{
431 if (ctx->page.mimetype && ctx->page.charset) 437 if (ctx->page.mimetype && ctx->page.charset)
432 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 438 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
433 ctx->page.charset); 439 ctx->page.charset);
434 else if (ctx->page.mimetype) 440 else if (ctx->page.mimetype)
435 htmlf("Content-Type: %s\n", ctx->page.mimetype); 441 htmlf("Content-Type: %s\n", ctx->page.mimetype);
442 if (ctx->page.size)
443 htmlf("Content-Length: %ld\n", ctx->page.size);
436 if (ctx->page.filename) 444 if (ctx->page.filename)
437 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 445 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
438 ctx->page.filename); 446 ctx->page.filename);
439 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 447 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
440 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 448 htmlf("Expires: %s\n", http_date(ctx->page.expires));
441 html("\n"); 449 html("\n");
442} 450}
443 451
444void cgit_print_docstart(struct cgit_context *ctx) 452void cgit_print_docstart(struct cgit_context *ctx)
445{ 453{
446 char *host = cgit_hosturl(); 454 char *host = cgit_hosturl();
447 html(cgit_doctype); 455 html(cgit_doctype);
diff --git a/ui-shared.h b/ui-shared.h
index f4123d3..747f092 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -3,24 +3,26 @@
3 3
4extern char *cgit_hosturl(); 4extern char *cgit_hosturl();
5extern char *cgit_repourl(const char *reponame); 5extern char *cgit_repourl(const char *reponame);
6extern char *cgit_fileurl(const char *reponame, const char *pagename, 6extern char *cgit_fileurl(const char *reponame, const char *pagename,
7 const char *filename, const char *query); 7 const char *filename, const char *query);
8extern char *cgit_pageurl(const char *reponame, const char *pagename, 8extern char *cgit_pageurl(const char *reponame, const char *pagename,
9 const char *query); 9 const char *query);
10 10
11extern void cgit_index_link(char *name, char *title, char *class, 11extern void cgit_index_link(char *name, char *title, char *class,
12 char *pattern, int ofs); 12 char *pattern, int ofs);
13extern void cgit_tree_link(char *name, char *title, char *class, char *head, 13extern void cgit_tree_link(char *name, char *title, char *class, char *head,
14 char *rev, char *path); 14 char *rev, char *path);
15extern void cgit_plain_link(char *name, char *title, char *class, char *head,
16 char *rev, char *path);
15extern void cgit_log_link(char *name, char *title, char *class, char *head, 17extern void cgit_log_link(char *name, char *title, char *class, char *head,
16 char *rev, char *path, int ofs, char *grep, 18 char *rev, char *path, int ofs, char *grep,
17 char *pattern); 19 char *pattern);
18extern void cgit_commit_link(char *name, char *title, char *class, char *head, 20extern void cgit_commit_link(char *name, char *title, char *class, char *head,
19 char *rev); 21 char *rev);
20extern void cgit_patch_link(char *name, char *title, char *class, char *head, 22extern void cgit_patch_link(char *name, char *title, char *class, char *head,
21 char *rev); 23 char *rev);
22extern void cgit_refs_link(char *name, char *title, char *class, char *head, 24extern void cgit_refs_link(char *name, char *title, char *class, char *head,
23 char *rev, char *path); 25 char *rev, char *path);
24extern void cgit_snapshot_link(char *name, char *title, char *class, 26extern void cgit_snapshot_link(char *name, char *title, char *class,
25 char *head, char *rev, char *archivename); 27 char *head, char *rev, char *archivename);
26extern void cgit_diff_link(char *name, char *title, char *class, char *head, 28extern void cgit_diff_link(char *name, char *title, char *class, char *head,
diff --git a/ui-tree.c b/ui-tree.c
index 9a837e2..79332fc 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -26,28 +26,28 @@ static void print_object(const unsigned char *sha1, char *path)
26 cgit_print_error(fmt("Bad object name: %s", 26 cgit_print_error(fmt("Bad object name: %s",
27 sha1_to_hex(sha1))); 27 sha1_to_hex(sha1)));
28 return; 28 return;
29 } 29 }
30 30
31 buf = read_sha1_file(sha1, &type, &size); 31 buf = read_sha1_file(sha1, &type, &size);
32 if (!buf) { 32 if (!buf) {
33 cgit_print_error(fmt("Error reading object %s", 33 cgit_print_error(fmt("Error reading object %s",
34 sha1_to_hex(sha1))); 34 sha1_to_hex(sha1)));
35 return; 35 return;
36 } 36 }
37 37
38 html(" blob: <a href='"); 38 html(" (");
39 html_attr(cgit_pageurl(ctx.qry.repo, "blob", 39 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
40 fmt("id=%s&path=%s", sha1_to_hex(sha1), path))); 40 curr_rev, path);
41 htmlf("'>%s</a>",sha1_to_hex(sha1)); 41 htmlf(")<br/>blob: %s", sha1_to_hex(sha1));
42 42
43 html("<table summary='blob content' class='blob'>\n"); 43 html("<table summary='blob content' class='blob'>\n");
44 idx = 0; 44 idx = 0;
45 start = 0; 45 start = 0;
46 lineno = 0; 46 lineno = 0;
47 while(idx < size) { 47 while(idx < size) {
48 if (buf[idx] == '\n') { 48 if (buf[idx] == '\n') {
49 buf[idx] = '\0'; 49 buf[idx] = '\0';
50 htmlf(linefmt, ++lineno); 50 htmlf(linefmt, ++lineno);
51 html_txt(buf + start); 51 html_txt(buf + start);
52 html("</td></tr>\n"); 52 html("</td></tr>\n");
53 start = idx + 1; 53 start = idx + 1;