summaryrefslogtreecommitdiffabout
path: root/ui-shared.c
authorLars Hjemli <hjemli@gmail.com>2008-11-29 17:39:41 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-11-29 17:39:41 (UTC)
commit0274b57d55a12ed38259757dbfae96b79cfa2e0b (patch) (unidiff)
tree34c7204f88168f791ef48f603bb8ab66e9df523c /ui-shared.c
parent7b5cee65fd9cf31e4f19ce4ff613778cb95512a9 (diff)
downloadcgit-0274b57d55a12ed38259757dbfae96b79cfa2e0b.zip
cgit-0274b57d55a12ed38259757dbfae96b79cfa2e0b.tar.gz
cgit-0274b57d55a12ed38259757dbfae96b79cfa2e0b.tar.bz2
ui-log: add support for showing the full commit message
Some users prefer to see the full message, so to make these users happy the new querystring parameter "showmsg" can be used to print the full commit message per log entry. A link is provided in the log heading to make this function accessible, and all links and forms tries to preserve the users preference. Note: the new link is not displayed on the summary page since the point of the summary page is to be a summary, but it is still obeyed if specified manually. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (limited to 'ui-shared.c') (more/less context) (ignore whitespace changes)
-rw-r--r--ui-shared.c12
1 files changed, 10 insertions, 2 deletions
diff --git a/ui-shared.c b/ui-shared.c
index 224e5f3..dc39e64 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,714 +1,722 @@
1/* ui-shared.c: common web output functions 1/* ui-shared.c: common web output functions
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cmd.h" 10#include "cmd.h"
11#include "html.h" 11#include "html.h"
12 12
13const char cgit_doctype[] = 13const char cgit_doctype[] =
14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
16 16
17static char *http_date(time_t t) 17static char *http_date(time_t t)
18{ 18{
19 static char day[][4] = 19 static char day[][4] =
20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
21 static char month[][4] = 21 static char month[][4] =
22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
23 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; 23 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
24 struct tm *tm = gmtime(&t); 24 struct tm *tm = gmtime(&t);
25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
27 tm->tm_hour, tm->tm_min, tm->tm_sec); 27 tm->tm_hour, tm->tm_min, tm->tm_sec);
28} 28}
29 29
30void cgit_print_error(char *msg) 30void cgit_print_error(char *msg)
31{ 31{
32 html("<div class='error'>"); 32 html("<div class='error'>");
33 html_txt(msg); 33 html_txt(msg);
34 html("</div>\n"); 34 html("</div>\n");
35} 35}
36 36
37char *cgit_hosturl() 37char *cgit_hosturl()
38{ 38{
39 char *host, *port; 39 char *host, *port;
40 40
41 host = getenv("HTTP_HOST"); 41 host = getenv("HTTP_HOST");
42 if (host) { 42 if (host) {
43 host = xstrdup(host); 43 host = xstrdup(host);
44 } else { 44 } else {
45 host = getenv("SERVER_NAME"); 45 host = getenv("SERVER_NAME");
46 if (!host) 46 if (!host)
47 return NULL; 47 return NULL;
48 port = getenv("SERVER_PORT"); 48 port = getenv("SERVER_PORT");
49 if (port && atoi(port) != 80) 49 if (port && atoi(port) != 80)
50 host = xstrdup(fmt("%s:%d", host, atoi(port))); 50 host = xstrdup(fmt("%s:%d", host, atoi(port)));
51 else 51 else
52 host = xstrdup(host); 52 host = xstrdup(host);
53 } 53 }
54 return host; 54 return host;
55} 55}
56 56
57char *cgit_rooturl() 57char *cgit_rooturl()
58{ 58{
59 if (ctx.cfg.virtual_root) 59 if (ctx.cfg.virtual_root)
60 return fmt("%s/", ctx.cfg.virtual_root); 60 return fmt("%s/", ctx.cfg.virtual_root);
61 else 61 else
62 return ctx.cfg.script_name; 62 return ctx.cfg.script_name;
63} 63}
64 64
65char *cgit_repourl(const char *reponame) 65char *cgit_repourl(const char *reponame)
66{ 66{
67 if (ctx.cfg.virtual_root) { 67 if (ctx.cfg.virtual_root) {
68 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); 68 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame);
69 } else { 69 } else {
70 return fmt("?r=%s", reponame); 70 return fmt("?r=%s", reponame);
71 } 71 }
72} 72}
73 73
74char *cgit_fileurl(const char *reponame, const char *pagename, 74char *cgit_fileurl(const char *reponame, const char *pagename,
75 const char *filename, const char *query) 75 const char *filename, const char *query)
76{ 76{
77 char *tmp; 77 char *tmp;
78 char *delim; 78 char *delim;
79 79
80 if (ctx.cfg.virtual_root) { 80 if (ctx.cfg.virtual_root) {
81 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, 81 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame,
82 pagename, (filename ? filename:"")); 82 pagename, (filename ? filename:""));
83 delim = "?"; 83 delim = "?";
84 } else { 84 } else {
85 tmp = fmt("?url=%s/%s/%s", reponame, pagename, 85 tmp = fmt("?url=%s/%s/%s", reponame, pagename,
86 (filename ? filename : "")); 86 (filename ? filename : ""));
87 delim = "&"; 87 delim = "&";
88 } 88 }
89 if (query) 89 if (query)
90 tmp = fmt("%s%s%s", tmp, delim, query); 90 tmp = fmt("%s%s%s", tmp, delim, query);
91 return tmp; 91 return tmp;
92} 92}
93 93
94char *cgit_pageurl(const char *reponame, const char *pagename, 94char *cgit_pageurl(const char *reponame, const char *pagename,
95 const char *query) 95 const char *query)
96{ 96{
97 return cgit_fileurl(reponame,pagename,0,query); 97 return cgit_fileurl(reponame,pagename,0,query);
98} 98}
99 99
100const char *cgit_repobasename(const char *reponame) 100const char *cgit_repobasename(const char *reponame)
101{ 101{
102 /* I assume we don't need to store more than one repo basename */ 102 /* I assume we don't need to store more than one repo basename */
103 static char rvbuf[1024]; 103 static char rvbuf[1024];
104 int p; 104 int p;
105 const char *rv; 105 const char *rv;
106 strncpy(rvbuf,reponame,sizeof(rvbuf)); 106 strncpy(rvbuf,reponame,sizeof(rvbuf));
107 if(rvbuf[sizeof(rvbuf)-1]) 107 if(rvbuf[sizeof(rvbuf)-1])
108 die("cgit_repobasename: truncated repository name '%s'", reponame); 108 die("cgit_repobasename: truncated repository name '%s'", reponame);
109 p = strlen(rvbuf)-1; 109 p = strlen(rvbuf)-1;
110 /* strip trailing slashes */ 110 /* strip trailing slashes */
111 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 111 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
112 /* strip trailing .git */ 112 /* strip trailing .git */
113 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 113 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
114 p -= 3; rvbuf[p--] = 0; 114 p -= 3; rvbuf[p--] = 0;
115 } 115 }
116 /* strip more trailing slashes if any */ 116 /* strip more trailing slashes if any */
117 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 117 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
118 /* find last slash in the remaining string */ 118 /* find last slash in the remaining string */
119 rv = strrchr(rvbuf,'/'); 119 rv = strrchr(rvbuf,'/');
120 if(rv) 120 if(rv)
121 return ++rv; 121 return ++rv;
122 return rvbuf; 122 return rvbuf;
123} 123}
124 124
125char *cgit_currurl() 125char *cgit_currurl()
126{ 126{
127 if (!ctx.cfg.virtual_root) 127 if (!ctx.cfg.virtual_root)
128 return ctx.cfg.script_name; 128 return ctx.cfg.script_name;
129 else if (ctx.qry.page) 129 else if (ctx.qry.page)
130 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); 130 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page);
131 else if (ctx.qry.repo) 131 else if (ctx.qry.repo)
132 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); 132 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo);
133 else 133 else
134 return fmt("%s/", ctx.cfg.virtual_root); 134 return fmt("%s/", ctx.cfg.virtual_root);
135} 135}
136 136
137static void site_url(char *page, char *search, int ofs) 137static void site_url(char *page, char *search, int ofs)
138{ 138{
139 char *delim = "?"; 139 char *delim = "?";
140 140
141 if (ctx.cfg.virtual_root) { 141 if (ctx.cfg.virtual_root) {
142 html_attr(ctx.cfg.virtual_root); 142 html_attr(ctx.cfg.virtual_root);
143 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 143 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
144 html("/"); 144 html("/");
145 } else 145 } else
146 html(ctx.cfg.script_name); 146 html(ctx.cfg.script_name);
147 147
148 if (page) { 148 if (page) {
149 htmlf("?p=%s", page); 149 htmlf("?p=%s", page);
150 delim = "&"; 150 delim = "&";
151 } 151 }
152 if (search) { 152 if (search) {
153 html(delim); 153 html(delim);
154 html("q="); 154 html("q=");
155 html_attr(search); 155 html_attr(search);
156 delim = "&"; 156 delim = "&";
157 } 157 }
158 if (ofs) { 158 if (ofs) {
159 html(delim); 159 html(delim);
160 htmlf("ofs=%d", ofs); 160 htmlf("ofs=%d", ofs);
161 } 161 }
162} 162}
163 163
164static void site_link(char *page, char *name, char *title, char *class, 164static void site_link(char *page, char *name, char *title, char *class,
165 char *search, int ofs) 165 char *search, int ofs)
166{ 166{
167 html("<a"); 167 html("<a");
168 if (title) { 168 if (title) {
169 html(" title='"); 169 html(" title='");
170 html_attr(title); 170 html_attr(title);
171 html("'"); 171 html("'");
172 } 172 }
173 if (class) { 173 if (class) {
174 html(" class='"); 174 html(" class='");
175 html_attr(class); 175 html_attr(class);
176 html("'"); 176 html("'");
177 } 177 }
178 html(" href='"); 178 html(" href='");
179 site_url(page, search, ofs); 179 site_url(page, search, ofs);
180 html("'>"); 180 html("'>");
181 html_txt(name); 181 html_txt(name);
182 html("</a>"); 182 html("</a>");
183} 183}
184 184
185void cgit_index_link(char *name, char *title, char *class, char *pattern, 185void cgit_index_link(char *name, char *title, char *class, char *pattern,
186 int ofs) 186 int ofs)
187{ 187{
188 site_link(NULL, name, title, class, pattern, ofs); 188 site_link(NULL, name, title, class, pattern, ofs);
189} 189}
190 190
191static char *repolink(char *title, char *class, char *page, char *head, 191static char *repolink(char *title, char *class, char *page, char *head,
192 char *path) 192 char *path)
193{ 193{
194 char *delim = "?"; 194 char *delim = "?";
195 195
196 html("<a"); 196 html("<a");
197 if (title) { 197 if (title) {
198 html(" title='"); 198 html(" title='");
199 html_attr(title); 199 html_attr(title);
200 html("'"); 200 html("'");
201 } 201 }
202 if (class) { 202 if (class) {
203 html(" class='"); 203 html(" class='");
204 html_attr(class); 204 html_attr(class);
205 html("'"); 205 html("'");
206 } 206 }
207 html(" href='"); 207 html(" href='");
208 if (ctx.cfg.virtual_root) { 208 if (ctx.cfg.virtual_root) {
209 html_url_path(ctx.cfg.virtual_root); 209 html_url_path(ctx.cfg.virtual_root);
210 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 210 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
211 html("/"); 211 html("/");
212 html_url_path(ctx.repo->url); 212 html_url_path(ctx.repo->url);
213 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 213 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
214 html("/"); 214 html("/");
215 if (page) { 215 if (page) {
216 html_url_path(page); 216 html_url_path(page);
217 html("/"); 217 html("/");
218 if (path) 218 if (path)
219 html_url_path(path); 219 html_url_path(path);
220 } 220 }
221 } else { 221 } else {
222 html(ctx.cfg.script_name); 222 html(ctx.cfg.script_name);
223 html("?url="); 223 html("?url=");
224 html_url_arg(ctx.repo->url); 224 html_url_arg(ctx.repo->url);
225 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 225 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
226 html("/"); 226 html("/");
227 if (page) { 227 if (page) {
228 html_url_arg(page); 228 html_url_arg(page);
229 html("/"); 229 html("/");
230 if (path) 230 if (path)
231 html_url_arg(path); 231 html_url_arg(path);
232 } 232 }
233 delim = "&amp;"; 233 delim = "&amp;";
234 } 234 }
235 if (head && strcmp(head, ctx.repo->defbranch)) { 235 if (head && strcmp(head, ctx.repo->defbranch)) {
236 html(delim); 236 html(delim);
237 html("h="); 237 html("h=");
238 html_url_arg(head); 238 html_url_arg(head);
239 delim = "&amp;"; 239 delim = "&amp;";
240 } 240 }
241 return fmt("%s", delim); 241 return fmt("%s", delim);
242} 242}
243 243
244static void reporevlink(char *page, char *name, char *title, char *class, 244static void reporevlink(char *page, char *name, char *title, char *class,
245 char *head, char *rev, char *path) 245 char *head, char *rev, char *path)
246{ 246{
247 char *delim; 247 char *delim;
248 248
249 delim = repolink(title, class, page, head, path); 249 delim = repolink(title, class, page, head, path);
250 if (rev && strcmp(rev, ctx.qry.head)) { 250 if (rev && strcmp(rev, ctx.qry.head)) {
251 html(delim); 251 html(delim);
252 html("id="); 252 html("id=");
253 html_url_arg(rev); 253 html_url_arg(rev);
254 } 254 }
255 html("'>"); 255 html("'>");
256 html_txt(name); 256 html_txt(name);
257 html("</a>"); 257 html("</a>");
258} 258}
259 259
260void cgit_summary_link(char *name, char *title, char *class, char *head) 260void cgit_summary_link(char *name, char *title, char *class, char *head)
261{ 261{
262 reporevlink(NULL, name, title, class, head, NULL, NULL); 262 reporevlink(NULL, name, title, class, head, NULL, NULL);
263} 263}
264 264
265void cgit_tag_link(char *name, char *title, char *class, char *head, 265void cgit_tag_link(char *name, char *title, char *class, char *head,
266 char *rev) 266 char *rev)
267{ 267{
268 reporevlink("tag", name, title, class, head, rev, NULL); 268 reporevlink("tag", name, title, class, head, rev, NULL);
269} 269}
270 270
271void cgit_tree_link(char *name, char *title, char *class, char *head, 271void cgit_tree_link(char *name, char *title, char *class, char *head,
272 char *rev, char *path) 272 char *rev, char *path)
273{ 273{
274 reporevlink("tree", name, title, class, head, rev, path); 274 reporevlink("tree", name, title, class, head, rev, path);
275} 275}
276 276
277void cgit_plain_link(char *name, char *title, char *class, char *head, 277void cgit_plain_link(char *name, char *title, char *class, char *head,
278 char *rev, char *path) 278 char *rev, char *path)
279{ 279{
280 reporevlink("plain", name, title, class, head, rev, path); 280 reporevlink("plain", name, title, class, head, rev, path);
281} 281}
282 282
283void cgit_log_link(char *name, char *title, char *class, char *head, 283void cgit_log_link(char *name, char *title, char *class, char *head,
284 char *rev, char *path, int ofs, char *grep, char *pattern) 284 char *rev, char *path, int ofs, char *grep, char *pattern,
285 int showmsg)
285{ 286{
286 char *delim; 287 char *delim;
287 288
288 delim = repolink(title, class, "log", head, path); 289 delim = repolink(title, class, "log", head, path);
289 if (rev && strcmp(rev, ctx.qry.head)) { 290 if (rev && strcmp(rev, ctx.qry.head)) {
290 html(delim); 291 html(delim);
291 html("id="); 292 html("id=");
292 html_url_arg(rev); 293 html_url_arg(rev);
293 delim = "&"; 294 delim = "&";
294 } 295 }
295 if (grep && pattern) { 296 if (grep && pattern) {
296 html(delim); 297 html(delim);
297 html("qt="); 298 html("qt=");
298 html_url_arg(grep); 299 html_url_arg(grep);
299 delim = "&"; 300 delim = "&";
300 html(delim); 301 html(delim);
301 html("q="); 302 html("q=");
302 html_url_arg(pattern); 303 html_url_arg(pattern);
303 } 304 }
304 if (ofs > 0) { 305 if (ofs > 0) {
305 html(delim); 306 html(delim);
306 html("ofs="); 307 html("ofs=");
307 htmlf("%d", ofs); 308 htmlf("%d", ofs);
309 delim = "&";
310 }
311 if (showmsg) {
312 html(delim);
313 html("showmsg=1");
308 } 314 }
309 html("'>"); 315 html("'>");
310 html_txt(name); 316 html_txt(name);
311 html("</a>"); 317 html("</a>");
312} 318}
313 319
314void cgit_commit_link(char *name, char *title, char *class, char *head, 320void cgit_commit_link(char *name, char *title, char *class, char *head,
315 char *rev) 321 char *rev)
316{ 322{
317 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { 323 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
318 name[ctx.cfg.max_msg_len] = '\0'; 324 name[ctx.cfg.max_msg_len] = '\0';
319 name[ctx.cfg.max_msg_len - 1] = '.'; 325 name[ctx.cfg.max_msg_len - 1] = '.';
320 name[ctx.cfg.max_msg_len - 2] = '.'; 326 name[ctx.cfg.max_msg_len - 2] = '.';
321 name[ctx.cfg.max_msg_len - 3] = '.'; 327 name[ctx.cfg.max_msg_len - 3] = '.';
322 } 328 }
323 reporevlink("commit", name, title, class, head, rev, NULL); 329 reporevlink("commit", name, title, class, head, rev, NULL);
324} 330}
325 331
326void cgit_refs_link(char *name, char *title, char *class, char *head, 332void cgit_refs_link(char *name, char *title, char *class, char *head,
327 char *rev, char *path) 333 char *rev, char *path)
328{ 334{
329 reporevlink("refs", name, title, class, head, rev, path); 335 reporevlink("refs", name, title, class, head, rev, path);
330} 336}
331 337
332void cgit_snapshot_link(char *name, char *title, char *class, char *head, 338void cgit_snapshot_link(char *name, char *title, char *class, char *head,
333 char *rev, char *archivename) 339 char *rev, char *archivename)
334{ 340{
335 reporevlink("snapshot", name, title, class, head, rev, archivename); 341 reporevlink("snapshot", name, title, class, head, rev, archivename);
336} 342}
337 343
338void cgit_diff_link(char *name, char *title, char *class, char *head, 344void cgit_diff_link(char *name, char *title, char *class, char *head,
339 char *new_rev, char *old_rev, char *path) 345 char *new_rev, char *old_rev, char *path)
340{ 346{
341 char *delim; 347 char *delim;
342 348
343 delim = repolink(title, class, "diff", head, path); 349 delim = repolink(title, class, "diff", head, path);
344 if (new_rev && strcmp(new_rev, ctx.qry.head)) { 350 if (new_rev && strcmp(new_rev, ctx.qry.head)) {
345 html(delim); 351 html(delim);
346 html("id="); 352 html("id=");
347 html_url_arg(new_rev); 353 html_url_arg(new_rev);
348 delim = "&amp;"; 354 delim = "&amp;";
349 } 355 }
350 if (old_rev) { 356 if (old_rev) {
351 html(delim); 357 html(delim);
352 html("id2="); 358 html("id2=");
353 html_url_arg(old_rev); 359 html_url_arg(old_rev);
354 } 360 }
355 html("'>"); 361 html("'>");
356 html_txt(name); 362 html_txt(name);
357 html("</a>"); 363 html("</a>");
358} 364}
359 365
360void cgit_patch_link(char *name, char *title, char *class, char *head, 366void cgit_patch_link(char *name, char *title, char *class, char *head,
361 char *rev) 367 char *rev)
362{ 368{
363 reporevlink("patch", name, title, class, head, rev, NULL); 369 reporevlink("patch", name, title, class, head, rev, NULL);
364} 370}
365 371
366void cgit_object_link(struct object *obj) 372void cgit_object_link(struct object *obj)
367{ 373{
368 char *page, *rev, *name; 374 char *page, *rev, *name;
369 375
370 if (obj->type == OBJ_COMMIT) { 376 if (obj->type == OBJ_COMMIT) {
371 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 377 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
372 ctx.qry.head, sha1_to_hex(obj->sha1)); 378 ctx.qry.head, sha1_to_hex(obj->sha1));
373 return; 379 return;
374 } else if (obj->type == OBJ_TREE) 380 } else if (obj->type == OBJ_TREE)
375 page = "tree"; 381 page = "tree";
376 else if (obj->type == OBJ_TAG) 382 else if (obj->type == OBJ_TAG)
377 page = "tag"; 383 page = "tag";
378 else 384 else
379 page = "blob"; 385 page = "blob";
380 rev = sha1_to_hex(obj->sha1); 386 rev = sha1_to_hex(obj->sha1);
381 name = fmt("%s %s", typename(obj->type), rev); 387 name = fmt("%s %s", typename(obj->type), rev);
382 reporevlink(page, name, NULL, NULL, ctx.qry.head, rev, NULL); 388 reporevlink(page, name, NULL, NULL, ctx.qry.head, rev, NULL);
383} 389}
384 390
385void cgit_print_date(time_t secs, char *format, int local_time) 391void cgit_print_date(time_t secs, char *format, int local_time)
386{ 392{
387 char buf[64]; 393 char buf[64];
388 struct tm *time; 394 struct tm *time;
389 395
390 if (!secs) 396 if (!secs)
391 return; 397 return;
392 if(local_time) 398 if(local_time)
393 time = localtime(&secs); 399 time = localtime(&secs);
394 else 400 else
395 time = gmtime(&secs); 401 time = gmtime(&secs);
396 strftime(buf, sizeof(buf)-1, format, time); 402 strftime(buf, sizeof(buf)-1, format, time);
397 html_txt(buf); 403 html_txt(buf);
398} 404}
399 405
400void cgit_print_age(time_t t, time_t max_relative, char *format) 406void cgit_print_age(time_t t, time_t max_relative, char *format)
401{ 407{
402 time_t now, secs; 408 time_t now, secs;
403 409
404 if (!t) 410 if (!t)
405 return; 411 return;
406 time(&now); 412 time(&now);
407 secs = now - t; 413 secs = now - t;
408 414
409 if (secs > max_relative && max_relative >= 0) { 415 if (secs > max_relative && max_relative >= 0) {
410 cgit_print_date(t, format, ctx.cfg.local_time); 416 cgit_print_date(t, format, ctx.cfg.local_time);
411 return; 417 return;
412 } 418 }
413 419
414 if (secs < TM_HOUR * 2) { 420 if (secs < TM_HOUR * 2) {
415 htmlf("<span class='age-mins'>%.0f min.</span>", 421 htmlf("<span class='age-mins'>%.0f min.</span>",
416 secs * 1.0 / TM_MIN); 422 secs * 1.0 / TM_MIN);
417 return; 423 return;
418 } 424 }
419 if (secs < TM_DAY * 2) { 425 if (secs < TM_DAY * 2) {
420 htmlf("<span class='age-hours'>%.0f hours</span>", 426 htmlf("<span class='age-hours'>%.0f hours</span>",
421 secs * 1.0 / TM_HOUR); 427 secs * 1.0 / TM_HOUR);
422 return; 428 return;
423 } 429 }
424 if (secs < TM_WEEK * 2) { 430 if (secs < TM_WEEK * 2) {
425 htmlf("<span class='age-days'>%.0f days</span>", 431 htmlf("<span class='age-days'>%.0f days</span>",
426 secs * 1.0 / TM_DAY); 432 secs * 1.0 / TM_DAY);
427 return; 433 return;
428 } 434 }
429 if (secs < TM_MONTH * 2) { 435 if (secs < TM_MONTH * 2) {
430 htmlf("<span class='age-weeks'>%.0f weeks</span>", 436 htmlf("<span class='age-weeks'>%.0f weeks</span>",
431 secs * 1.0 / TM_WEEK); 437 secs * 1.0 / TM_WEEK);
432 return; 438 return;
433 } 439 }
434 if (secs < TM_YEAR * 2) { 440 if (secs < TM_YEAR * 2) {
435 htmlf("<span class='age-months'>%.0f months</span>", 441 htmlf("<span class='age-months'>%.0f months</span>",
436 secs * 1.0 / TM_MONTH); 442 secs * 1.0 / TM_MONTH);
437 return; 443 return;
438 } 444 }
439 htmlf("<span class='age-years'>%.0f years</span>", 445 htmlf("<span class='age-years'>%.0f years</span>",
440 secs * 1.0 / TM_YEAR); 446 secs * 1.0 / TM_YEAR);
441} 447}
442 448
443void cgit_print_http_headers(struct cgit_context *ctx) 449void cgit_print_http_headers(struct cgit_context *ctx)
444{ 450{
445 if (ctx->page.mimetype && ctx->page.charset) 451 if (ctx->page.mimetype && ctx->page.charset)
446 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 452 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
447 ctx->page.charset); 453 ctx->page.charset);
448 else if (ctx->page.mimetype) 454 else if (ctx->page.mimetype)
449 htmlf("Content-Type: %s\n", ctx->page.mimetype); 455 htmlf("Content-Type: %s\n", ctx->page.mimetype);
450 if (ctx->page.size) 456 if (ctx->page.size)
451 htmlf("Content-Length: %ld\n", ctx->page.size); 457 htmlf("Content-Length: %ld\n", ctx->page.size);
452 if (ctx->page.filename) 458 if (ctx->page.filename)
453 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 459 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
454 ctx->page.filename); 460 ctx->page.filename);
455 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 461 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
456 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 462 htmlf("Expires: %s\n", http_date(ctx->page.expires));
457 html("\n"); 463 html("\n");
458} 464}
459 465
460void cgit_print_docstart(struct cgit_context *ctx) 466void cgit_print_docstart(struct cgit_context *ctx)
461{ 467{
462 char *host = cgit_hosturl(); 468 char *host = cgit_hosturl();
463 html(cgit_doctype); 469 html(cgit_doctype);
464 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 470 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
465 html("<head>\n"); 471 html("<head>\n");
466 html("<title>"); 472 html("<title>");
467 html_txt(ctx->page.title); 473 html_txt(ctx->page.title);
468 html("</title>\n"); 474 html("</title>\n");
469 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 475 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
470 if (ctx->cfg.robots && *ctx->cfg.robots) 476 if (ctx->cfg.robots && *ctx->cfg.robots)
471 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 477 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
472 html("<link rel='stylesheet' type='text/css' href='"); 478 html("<link rel='stylesheet' type='text/css' href='");
473 html_attr(ctx->cfg.css); 479 html_attr(ctx->cfg.css);
474 html("'/>\n"); 480 html("'/>\n");
475 if (ctx->cfg.favicon) { 481 if (ctx->cfg.favicon) {
476 html("<link rel='shortcut icon' href='"); 482 html("<link rel='shortcut icon' href='");
477 html_attr(ctx->cfg.favicon); 483 html_attr(ctx->cfg.favicon);
478 html("'/>\n"); 484 html("'/>\n");
479 } 485 }
480 if (host && ctx->repo) { 486 if (host && ctx->repo) {
481 html("<link rel='alternate' title='Atom feed' href='http://"); 487 html("<link rel='alternate' title='Atom feed' href='http://");
482 html_attr(cgit_hosturl()); 488 html_attr(cgit_hosturl());
483 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path, 489 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path,
484 fmt("h=%s", ctx->qry.head))); 490 fmt("h=%s", ctx->qry.head)));
485 html("' type='application/atom+xml'/>"); 491 html("' type='application/atom+xml'/>");
486 } 492 }
487 html("</head>\n"); 493 html("</head>\n");
488 html("<body>\n"); 494 html("<body>\n");
489} 495}
490 496
491void cgit_print_docend() 497void cgit_print_docend()
492{ 498{
493 html("</div>"); 499 html("</div>");
494 if (ctx.cfg.footer) 500 if (ctx.cfg.footer)
495 html_include(ctx.cfg.footer); 501 html_include(ctx.cfg.footer);
496 else { 502 else {
497 htmlf("<div class='footer'>generated by cgit %s at ", 503 htmlf("<div class='footer'>generated by cgit %s at ",
498 cgit_version); 504 cgit_version);
499 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 505 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
500 html("</div>\n"); 506 html("</div>\n");
501 } 507 }
502 html("</body>\n</html>\n"); 508 html("</body>\n</html>\n");
503} 509}
504 510
505int print_branch_option(const char *refname, const unsigned char *sha1, 511int print_branch_option(const char *refname, const unsigned char *sha1,
506 int flags, void *cb_data) 512 int flags, void *cb_data)
507{ 513{
508 char *name = (char *)refname; 514 char *name = (char *)refname;
509 html_option(name, name, ctx.qry.head); 515 html_option(name, name, ctx.qry.head);
510 return 0; 516 return 0;
511} 517}
512 518
513int print_archive_ref(const char *refname, const unsigned char *sha1, 519int print_archive_ref(const char *refname, const unsigned char *sha1,
514 int flags, void *cb_data) 520 int flags, void *cb_data)
515{ 521{
516 struct tag *tag; 522 struct tag *tag;
517 struct taginfo *info; 523 struct taginfo *info;
518 struct object *obj; 524 struct object *obj;
519 char buf[256], *url; 525 char buf[256], *url;
520 unsigned char fileid[20]; 526 unsigned char fileid[20];
521 int *header = (int *)cb_data; 527 int *header = (int *)cb_data;
522 528
523 if (prefixcmp(refname, "refs/archives")) 529 if (prefixcmp(refname, "refs/archives"))
524 return 0; 530 return 0;
525 strncpy(buf, refname+14, sizeof(buf)); 531 strncpy(buf, refname+14, sizeof(buf));
526 obj = parse_object(sha1); 532 obj = parse_object(sha1);
527 if (!obj) 533 if (!obj)
528 return 1; 534 return 1;
529 if (obj->type == OBJ_TAG) { 535 if (obj->type == OBJ_TAG) {
530 tag = lookup_tag(sha1); 536 tag = lookup_tag(sha1);
531 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 537 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
532 return 0; 538 return 0;
533 hashcpy(fileid, tag->tagged->sha1); 539 hashcpy(fileid, tag->tagged->sha1);
534 } else if (obj->type != OBJ_BLOB) { 540 } else if (obj->type != OBJ_BLOB) {
535 return 0; 541 return 0;
536 } else { 542 } else {
537 hashcpy(fileid, sha1); 543 hashcpy(fileid, sha1);
538 } 544 }
539 if (!*header) { 545 if (!*header) {
540 html("<h1>download</h1>\n"); 546 html("<h1>download</h1>\n");
541 *header = 1; 547 *header = 1;
542 } 548 }
543 url = cgit_pageurl(ctx.qry.repo, "blob", 549 url = cgit_pageurl(ctx.qry.repo, "blob",
544 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 550 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
545 buf)); 551 buf));
546 html_link_open(url, NULL, "menu"); 552 html_link_open(url, NULL, "menu");
547 html_txt(strlpart(buf, 20)); 553 html_txt(strlpart(buf, 20));
548 html_link_close(); 554 html_link_close();
549 return 0; 555 return 0;
550} 556}
551 557
552void add_hidden_formfields(int incl_head, int incl_search, char *page) 558void add_hidden_formfields(int incl_head, int incl_search, char *page)
553{ 559{
554 char *url; 560 char *url;
555 561
556 if (!ctx.cfg.virtual_root) { 562 if (!ctx.cfg.virtual_root) {
557 url = fmt("%s/%s", ctx.qry.repo, page); 563 url = fmt("%s/%s", ctx.qry.repo, page);
558 if (ctx.qry.path) 564 if (ctx.qry.path)
559 url = fmt("%s/%s", url, ctx.qry.path); 565 url = fmt("%s/%s", url, ctx.qry.path);
560 html_hidden("url", url); 566 html_hidden("url", url);
561 } 567 }
562 568
563 if (incl_head && ctx.qry.head && ctx.repo->defbranch && 569 if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
564 strcmp(ctx.qry.head, ctx.repo->defbranch)) 570 strcmp(ctx.qry.head, ctx.repo->defbranch))
565 html_hidden("h", ctx.qry.head); 571 html_hidden("h", ctx.qry.head);
566 572
567 if (ctx.qry.sha1) 573 if (ctx.qry.sha1)
568 html_hidden("id", ctx.qry.sha1); 574 html_hidden("id", ctx.qry.sha1);
569 if (ctx.qry.sha2) 575 if (ctx.qry.sha2)
570 html_hidden("id2", ctx.qry.sha2); 576 html_hidden("id2", ctx.qry.sha2);
577 if (ctx.qry.showmsg)
578 html_hidden("showmsg", "1");
571 579
572 if (incl_search) { 580 if (incl_search) {
573 if (ctx.qry.grep) 581 if (ctx.qry.grep)
574 html_hidden("qt", ctx.qry.grep); 582 html_hidden("qt", ctx.qry.grep);
575 if (ctx.qry.search) 583 if (ctx.qry.search)
576 html_hidden("q", ctx.qry.search); 584 html_hidden("q", ctx.qry.search);
577 } 585 }
578} 586}
579 587
580char *hc(struct cgit_cmd *cmd, const char *page) 588char *hc(struct cgit_cmd *cmd, const char *page)
581{ 589{
582 return (strcmp(cmd->name, page) ? NULL : "active"); 590 return (strcmp(cmd->name, page) ? NULL : "active");
583} 591}
584 592
585void cgit_print_pageheader(struct cgit_context *ctx) 593void cgit_print_pageheader(struct cgit_context *ctx)
586{ 594{
587 struct cgit_cmd *cmd = cgit_get_cmd(ctx); 595 struct cgit_cmd *cmd = cgit_get_cmd(ctx);
588 596
589 html("<table id='header'>\n"); 597 html("<table id='header'>\n");
590 html("<tr>\n"); 598 html("<tr>\n");
591 html("<td class='logo' rowspan='2'><a href='"); 599 html("<td class='logo' rowspan='2'><a href='");
592 if (ctx->cfg.logo_link) 600 if (ctx->cfg.logo_link)
593 html_attr(ctx->cfg.logo_link); 601 html_attr(ctx->cfg.logo_link);
594 else 602 else
595 html_attr(cgit_rooturl()); 603 html_attr(cgit_rooturl());
596 html("'><img src='"); 604 html("'><img src='");
597 html_attr(ctx->cfg.logo); 605 html_attr(ctx->cfg.logo);
598 html("' alt='cgit logo'/></a></td>\n"); 606 html("' alt='cgit logo'/></a></td>\n");
599 607
600 html("<td class='main'>"); 608 html("<td class='main'>");
601 if (ctx->repo) { 609 if (ctx->repo) {
602 cgit_index_link("index", NULL, NULL, NULL, 0); 610 cgit_index_link("index", NULL, NULL, NULL, 0);
603 html(" : "); 611 html(" : ");
604 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL); 612 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL);
605 html("</td><td class='form'>"); 613 html("</td><td class='form'>");
606 html("<form method='get' action=''>\n"); 614 html("<form method='get' action=''>\n");
607 add_hidden_formfields(0, 1, ctx->qry.page); 615 add_hidden_formfields(0, 1, ctx->qry.page);
608 html("<select name='h' onchange='this.form.submit();'>\n"); 616 html("<select name='h' onchange='this.form.submit();'>\n");
609 for_each_branch_ref(print_branch_option, ctx->qry.head); 617 for_each_branch_ref(print_branch_option, ctx->qry.head);
610 html("</select> "); 618 html("</select> ");
611 html("<input type='submit' name='' value='switch'/>"); 619 html("<input type='submit' name='' value='switch'/>");
612 html("</form>"); 620 html("</form>");
613 } else 621 } else
614 html_txt(ctx->cfg.root_title); 622 html_txt(ctx->cfg.root_title);
615 html("</td></tr>\n"); 623 html("</td></tr>\n");
616 624
617 html("<tr><td class='sub'>"); 625 html("<tr><td class='sub'>");
618 if (ctx->repo) { 626 if (ctx->repo) {
619 html_txt(ctx->repo->desc); 627 html_txt(ctx->repo->desc);
620 html("</td><td class='sub right'>"); 628 html("</td><td class='sub right'>");
621 html_txt(ctx->repo->owner); 629 html_txt(ctx->repo->owner);
622 } else { 630 } else {
623 if (ctx->cfg.root_desc) 631 if (ctx->cfg.root_desc)
624 html_txt(ctx->cfg.root_desc); 632 html_txt(ctx->cfg.root_desc);
625 else if (ctx->cfg.index_info) 633 else if (ctx->cfg.index_info)
626 html_include(ctx->cfg.index_info); 634 html_include(ctx->cfg.index_info);
627 } 635 }
628 html("</td></tr></table>\n"); 636 html("</td></tr></table>\n");
629 637
630 html("<table class='tabs'><tr><td>\n"); 638 html("<table class='tabs'><tr><td>\n");
631 if (ctx->repo) { 639 if (ctx->repo) {
632 cgit_summary_link("summary", NULL, hc(cmd, "summary"), 640 cgit_summary_link("summary", NULL, hc(cmd, "summary"),
633 ctx->qry.head); 641 ctx->qry.head);
634 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head, 642 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head,
635 ctx->qry.sha1, NULL); 643 ctx->qry.sha1, NULL);
636 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head, 644 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head,
637 NULL, NULL, 0, NULL, NULL); 645 NULL, NULL, 0, NULL, NULL, ctx->qry.showmsg);
638 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, 646 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head,
639 ctx->qry.sha1, NULL); 647 ctx->qry.sha1, NULL);
640 cgit_commit_link("commit", NULL, hc(cmd, "commit"), 648 cgit_commit_link("commit", NULL, hc(cmd, "commit"),
641 ctx->qry.head, ctx->qry.sha1); 649 ctx->qry.head, ctx->qry.sha1);
642 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, 650 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head,
643 ctx->qry.sha1, ctx->qry.sha2, NULL); 651 ctx->qry.sha1, ctx->qry.sha2, NULL);
644 if (ctx->repo->readme) 652 if (ctx->repo->readme)
645 reporevlink("about", "about", NULL, 653 reporevlink("about", "about", NULL,
646 hc(cmd, "about"), ctx->qry.head, NULL, 654 hc(cmd, "about"), ctx->qry.head, NULL,
647 NULL); 655 NULL);
648 html("</td><td class='form'>"); 656 html("</td><td class='form'>");
649 html("<form class='right' method='get' action='"); 657 html("<form class='right' method='get' action='");
650 if (ctx->cfg.virtual_root) 658 if (ctx->cfg.virtual_root)
651 html_url_path(cgit_fileurl(ctx->qry.repo, "log", 659 html_url_path(cgit_fileurl(ctx->qry.repo, "log",
652 ctx->qry.path, NULL)); 660 ctx->qry.path, NULL));
653 html("'>\n"); 661 html("'>\n");
654 add_hidden_formfields(1, 0, "log"); 662 add_hidden_formfields(1, 0, "log");
655 html("<select name='qt'>\n"); 663 html("<select name='qt'>\n");
656 html_option("grep", "log msg", ctx->qry.grep); 664 html_option("grep", "log msg", ctx->qry.grep);
657 html_option("author", "author", ctx->qry.grep); 665 html_option("author", "author", ctx->qry.grep);
658 html_option("committer", "committer", ctx->qry.grep); 666 html_option("committer", "committer", ctx->qry.grep);
659 html("</select>\n"); 667 html("</select>\n");
660 html("<input class='txt' type='text' size='10' name='q' value='"); 668 html("<input class='txt' type='text' size='10' name='q' value='");
661 html_attr(ctx->qry.search); 669 html_attr(ctx->qry.search);
662 html("'/>\n"); 670 html("'/>\n");
663 html("<input type='submit' value='search'/>\n"); 671 html("<input type='submit' value='search'/>\n");
664 html("</form>\n"); 672 html("</form>\n");
665 } else { 673 } else {
666 site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0); 674 site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0);
667 if (ctx->cfg.root_readme) 675 if (ctx->cfg.root_readme)
668 site_link("about", "about", NULL, hc(cmd, "about"), 676 site_link("about", "about", NULL, hc(cmd, "about"),
669 NULL, 0); 677 NULL, 0);
670 html("</td><td class='form'>"); 678 html("</td><td class='form'>");
671 html("<form method='get' action='"); 679 html("<form method='get' action='");
672 html_attr(cgit_rooturl()); 680 html_attr(cgit_rooturl());
673 html("'>\n"); 681 html("'>\n");
674 html("<input type='text' name='q' size='10' value='"); 682 html("<input type='text' name='q' size='10' value='");
675 html_attr(ctx->qry.search); 683 html_attr(ctx->qry.search);
676 html("'/>\n"); 684 html("'/>\n");
677 html("<input type='submit' value='search'/>\n"); 685 html("<input type='submit' value='search'/>\n");
678 html("</form>"); 686 html("</form>");
679 } 687 }
680 html("</td></tr></table>\n"); 688 html("</td></tr></table>\n");
681 html("<div class='content'>"); 689 html("<div class='content'>");
682} 690}
683 691
684void cgit_print_filemode(unsigned short mode) 692void cgit_print_filemode(unsigned short mode)
685{ 693{
686 if (S_ISDIR(mode)) 694 if (S_ISDIR(mode))
687 html("d"); 695 html("d");
688 else if (S_ISLNK(mode)) 696 else if (S_ISLNK(mode))
689 html("l"); 697 html("l");
690 else if (S_ISGITLINK(mode)) 698 else if (S_ISGITLINK(mode))
691 html("m"); 699 html("m");
692 else 700 else
693 html("-"); 701 html("-");
694 html_fileperm(mode >> 6); 702 html_fileperm(mode >> 6);
695 html_fileperm(mode >> 3); 703 html_fileperm(mode >> 3);
696 html_fileperm(mode); 704 html_fileperm(mode);
697} 705}
698 706
699void cgit_print_snapshot_links(const char *repo, const char *head, 707void cgit_print_snapshot_links(const char *repo, const char *head,
700 const char *hex, int snapshots) 708 const char *hex, int snapshots)
701{ 709{
702 const struct cgit_snapshot_format* f; 710 const struct cgit_snapshot_format* f;
703 char *filename; 711 char *filename;
704 712
705 for (f = cgit_snapshot_formats; f->suffix; f++) { 713 for (f = cgit_snapshot_formats; f->suffix; f++) {
706 if (!(snapshots & f->bit)) 714 if (!(snapshots & f->bit))
707 continue; 715 continue;
708 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, 716 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
709 f->suffix); 717 f->suffix);
710 cgit_snapshot_link(filename, NULL, NULL, (char *)head, 718 cgit_snapshot_link(filename, NULL, NULL, (char *)head,
711 (char *)hex, filename); 719 (char *)hex, filename);
712 html("<br/>"); 720 html("<br/>");
713 } 721 }
714} 722}