summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2007-05-22 21:08:46 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2007-05-22 21:12:41 (UTC)
commit5db39170b6c979655a0238dcd627e206febed88b (patch) (unidiff)
tree2c79691bde31f9db2861dc76010691e9dbdde1cb
parent3b86b44fc761cfa8b97c44bbbdd63c9fbf1127ed (diff)
downloadcgit-5db39170b6c979655a0238dcd627e206febed88b.zip
cgit-5db39170b6c979655a0238dcd627e206febed88b.tar.gz
cgit-5db39170b6c979655a0238dcd627e206febed88b.tar.bz2
Add cgit_print_age() function
This function can be used to print relative dates, just as in gitweb. Next step will be to actually use the new function. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.css25
-rw-r--r--cgit.h22
-rw-r--r--ui-commit.c4
-rw-r--r--ui-shared.c47
-rw-r--r--ui-summary.c4
5 files changed, 94 insertions, 8 deletions
diff --git a/cgit.css b/cgit.css
index 95c3e40..327eaba 100644
--- a/cgit.css
+++ b/cgit.css
@@ -295,96 +295,121 @@ table.diffstat td.graph {
295table.diffstat td.graph table { 295table.diffstat td.graph table {
296 border: none; 296 border: none;
297} 297}
298 298
299table.diffstat td.graph td { 299table.diffstat td.graph td {
300 padding: 0px; 300 padding: 0px;
301 border: 0px; 301 border: 0px;
302 height: 7pt; 302 height: 7pt;
303} 303}
304 304
305table.diffstat td.graph td.add { 305table.diffstat td.graph td.add {
306 background-color: #5c5; 306 background-color: #5c5;
307} 307}
308 308
309table.diffstat td.graph td.rem { 309table.diffstat td.graph td.rem {
310 background-color: #c55; 310 background-color: #c55;
311} 311}
312 312
313table.diffstat td.graph td.none { 313table.diffstat td.graph td.none {
314 background-color: none; 314 background-color: none;
315} 315}
316 316
317div.diffstat-summary { 317div.diffstat-summary {
318 color: #888; 318 color: #888;
319 padding-top: 0.5em; 319 padding-top: 0.5em;
320} 320}
321 321
322table.diff td { 322table.diff td {
323 font-family: monospace; 323 font-family: monospace;
324 white-space: pre; 324 white-space: pre;
325} 325}
326 326
327table.diff td div.head { 327table.diff td div.head {
328 font-weight: bold; 328 font-weight: bold;
329 padding-top: 1em; 329 padding-top: 1em;
330} 330}
331 331
332table.diff td div.hunk { 332table.diff td div.hunk {
333 color: #009; 333 color: #009;
334} 334}
335 335
336table.diff td div.add { 336table.diff td div.add {
337 color: green; 337 color: green;
338} 338}
339 339
340table.diff td div.del { 340table.diff td div.del {
341 color: red; 341 color: red;
342} 342}
343 343
344.sha1 { 344.sha1 {
345 font-family: courier; 345 font-family: courier;
346 font-size: 90%; 346 font-size: 90%;
347} 347}
348 348
349.left { 349.left {
350 text-align: left; 350 text-align: left;
351} 351}
352 352
353.right { 353.right {
354 text-align: right; 354 text-align: right;
355} 355}
356 356
357table.list td.repogroup { 357table.list td.repogroup {
358 font-style: italic; 358 font-style: italic;
359 color: #888; 359 color: #888;
360} 360}
361 361
362a.button { 362a.button {
363 font-size: 80%; 363 font-size: 80%;
364 color: #333; 364 color: #333;
365 background-color: #ccc; 365 background-color: #ccc;
366 border: solid 1px #999; 366 border: solid 1px #999;
367 padding: 0em 0.5em; 367 padding: 0em 0.5em;
368 margin: 0.1em 0.25em; 368 margin: 0.1em 0.25em;
369} 369}
370 370
371a.button:hover { 371a.button:hover {
372 text-decoration: none; 372 text-decoration: none;
373 background-color: #eee; 373 background-color: #eee;
374} 374}
375 375
376a.primary { 376a.primary {
377 font-size: 100%; 377 font-size: 100%;
378} 378}
379 379
380a.secondary { 380a.secondary {
381 font-size: 90%; 381 font-size: 90%;
382} 382}
383 383
384td.toplevel-repo { 384td.toplevel-repo {
385 385
386} 386}
387 387
388table.list td.sublevel-repo { 388table.list td.sublevel-repo {
389 padding-left: 1.5em; 389 padding-left: 1.5em;
390} 390}
391
392span.age-mins {
393 font-weight: bold;
394 color: #080;
395}
396
397span.age-hours {
398 color: #080;
399}
400
401span.age-days {
402 color: #040;
403}
404
405span.age-weeks {
406 color: #444;
407}
408
409span.age-months {
410 color: #888;
411}
412
413span.age-years {
414 color: #bbb;
415}
diff --git a/cgit.h b/cgit.h
index 8927236..4da2d3d 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,205 +1,225 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19 19
20 20
21/* 21/*
22 * The valid cgit repo-commands 22 * The valid cgit repo-commands
23 */ 23 */
24#define CMD_LOG 1 24#define CMD_LOG 1
25#define CMD_COMMIT 2 25#define CMD_COMMIT 2
26#define CMD_DIFF 3 26#define CMD_DIFF 3
27#define CMD_TREE 4 27#define CMD_TREE 4
28#define CMD_VIEW 5 28#define CMD_VIEW 5
29#define CMD_BLOB 6 29#define CMD_BLOB 6
30#define CMD_SNAPSHOT 7 30#define CMD_SNAPSHOT 7
31 31
32
33/*
34 * Dateformats used on misc. pages
35 */
36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
37#define FMT_SHORTDATE "%Y-%m-%d"
38
39
40/*
41 * Limits used for relative dates
42 */
43#define TM_MIN 60
44#define TM_HOUR (TM_MIN * 60)
45#define TM_DAY (TM_HOUR * 24)
46#define TM_WEEK (TM_DAY * 7)
47#define TM_YEAR (TM_DAY * 365)
48#define TM_MONTH (TM_YEAR / 12.0)
49
50
32typedef void (*configfn)(const char *name, const char *value); 51typedef void (*configfn)(const char *name, const char *value);
33typedef void (*filepair_fn)(struct diff_filepair *pair); 52typedef void (*filepair_fn)(struct diff_filepair *pair);
34typedef void (*linediff_fn)(char *line, int len); 53typedef void (*linediff_fn)(char *line, int len);
35 54
36struct cacheitem { 55struct cacheitem {
37 char *name; 56 char *name;
38 struct stat st; 57 struct stat st;
39 int ttl; 58 int ttl;
40 int fd; 59 int fd;
41}; 60};
42 61
43struct repoinfo { 62struct repoinfo {
44 char *url; 63 char *url;
45 char *name; 64 char *name;
46 char *path; 65 char *path;
47 char *desc; 66 char *desc;
48 char *owner; 67 char *owner;
49 char *defbranch; 68 char *defbranch;
50 char *group; 69 char *group;
51 char *module_link; 70 char *module_link;
52 int snapshots; 71 int snapshots;
53 int enable_log_filecount; 72 int enable_log_filecount;
54 int enable_log_linecount; 73 int enable_log_linecount;
55}; 74};
56 75
57struct repolist { 76struct repolist {
58 int length; 77 int length;
59 int count; 78 int count;
60 struct repoinfo *repos; 79 struct repoinfo *repos;
61}; 80};
62 81
63struct commitinfo { 82struct commitinfo {
64 struct commit *commit; 83 struct commit *commit;
65 char *author; 84 char *author;
66 char *author_email; 85 char *author_email;
67 unsigned long author_date; 86 unsigned long author_date;
68 char *committer; 87 char *committer;
69 char *committer_email; 88 char *committer_email;
70 unsigned long committer_date; 89 unsigned long committer_date;
71 char *subject; 90 char *subject;
72 char *msg; 91 char *msg;
73}; 92};
74 93
75struct taginfo { 94struct taginfo {
76 char *tagger; 95 char *tagger;
77 char *tagger_email; 96 char *tagger_email;
78 int tagger_date; 97 int tagger_date;
79 char *msg; 98 char *msg;
80}; 99};
81 100
82extern const char cgit_version[]; 101extern const char cgit_version[];
83 102
84extern struct repolist cgit_repolist; 103extern struct repolist cgit_repolist;
85extern struct repoinfo *cgit_repo; 104extern struct repoinfo *cgit_repo;
86extern int cgit_cmd; 105extern int cgit_cmd;
87 106
88extern char *cgit_root_title; 107extern char *cgit_root_title;
89extern char *cgit_css; 108extern char *cgit_css;
90extern char *cgit_logo; 109extern char *cgit_logo;
91extern char *cgit_index_header; 110extern char *cgit_index_header;
92extern char *cgit_logo_link; 111extern char *cgit_logo_link;
93extern char *cgit_module_link; 112extern char *cgit_module_link;
94extern char *cgit_virtual_root; 113extern char *cgit_virtual_root;
95extern char *cgit_script_name; 114extern char *cgit_script_name;
96extern char *cgit_cache_root; 115extern char *cgit_cache_root;
97extern char *cgit_repo_group; 116extern char *cgit_repo_group;
98 117
99extern int cgit_nocache; 118extern int cgit_nocache;
100extern int cgit_snapshots; 119extern int cgit_snapshots;
101extern int cgit_enable_log_filecount; 120extern int cgit_enable_log_filecount;
102extern int cgit_enable_log_linecount; 121extern int cgit_enable_log_linecount;
103extern int cgit_max_lock_attempts; 122extern int cgit_max_lock_attempts;
104extern int cgit_cache_root_ttl; 123extern int cgit_cache_root_ttl;
105extern int cgit_cache_repo_ttl; 124extern int cgit_cache_repo_ttl;
106extern int cgit_cache_dynamic_ttl; 125extern int cgit_cache_dynamic_ttl;
107extern int cgit_cache_static_ttl; 126extern int cgit_cache_static_ttl;
108extern int cgit_cache_max_create_time; 127extern int cgit_cache_max_create_time;
109 128
110extern int cgit_max_msg_len; 129extern int cgit_max_msg_len;
111extern int cgit_max_repodesc_len; 130extern int cgit_max_repodesc_len;
112extern int cgit_max_commit_count; 131extern int cgit_max_commit_count;
113 132
114extern int cgit_query_has_symref; 133extern int cgit_query_has_symref;
115extern int cgit_query_has_sha1; 134extern int cgit_query_has_sha1;
116 135
117extern char *cgit_querystring; 136extern char *cgit_querystring;
118extern char *cgit_query_repo; 137extern char *cgit_query_repo;
119extern char *cgit_query_page; 138extern char *cgit_query_page;
120extern char *cgit_query_search; 139extern char *cgit_query_search;
121extern char *cgit_query_head; 140extern char *cgit_query_head;
122extern char *cgit_query_sha1; 141extern char *cgit_query_sha1;
123extern char *cgit_query_sha2; 142extern char *cgit_query_sha2;
124extern char *cgit_query_path; 143extern char *cgit_query_path;
125extern char *cgit_query_name; 144extern char *cgit_query_name;
126extern int cgit_query_ofs; 145extern int cgit_query_ofs;
127 146
128extern int htmlfd; 147extern int htmlfd;
129 148
130extern int cgit_get_cmd_index(const char *cmd); 149extern int cgit_get_cmd_index(const char *cmd);
131extern struct repoinfo *cgit_get_repoinfo(const char *url); 150extern struct repoinfo *cgit_get_repoinfo(const char *url);
132extern void cgit_global_config_cb(const char *name, const char *value); 151extern void cgit_global_config_cb(const char *name, const char *value);
133extern void cgit_repo_config_cb(const char *name, const char *value); 152extern void cgit_repo_config_cb(const char *name, const char *value);
134extern void cgit_querystring_cb(const char *name, const char *value); 153extern void cgit_querystring_cb(const char *name, const char *value);
135 154
136extern int chk_zero(int result, char *msg); 155extern int chk_zero(int result, char *msg);
137extern int chk_positive(int result, char *msg); 156extern int chk_positive(int result, char *msg);
138 157
139extern int hextoint(char c); 158extern int hextoint(char c);
140 159
141extern void *cgit_free_commitinfo(struct commitinfo *info); 160extern void *cgit_free_commitinfo(struct commitinfo *info);
142 161
143extern int cgit_diff_files(const unsigned char *old_sha1, 162extern int cgit_diff_files(const unsigned char *old_sha1,
144 const unsigned char *new_sha1, 163 const unsigned char *new_sha1,
145 linediff_fn fn); 164 linediff_fn fn);
146 165
147extern void cgit_diff_tree(const unsigned char *old_sha1, 166extern void cgit_diff_tree(const unsigned char *old_sha1,
148 const unsigned char *new_sha1, 167 const unsigned char *new_sha1,
149 filepair_fn fn); 168 filepair_fn fn);
150 169
151extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 170extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
152 171
153extern char *fmt(const char *format,...); 172extern char *fmt(const char *format,...);
154 173
155extern void html(const char *txt); 174extern void html(const char *txt);
156extern void htmlf(const char *format,...); 175extern void htmlf(const char *format,...);
157extern void html_txt(char *txt); 176extern void html_txt(char *txt);
158extern void html_ntxt(int len, char *txt); 177extern void html_ntxt(int len, char *txt);
159extern void html_attr(char *txt); 178extern void html_attr(char *txt);
160extern void html_hidden(char *name, char *value); 179extern void html_hidden(char *name, char *value);
161extern void html_link_open(char *url, char *title, char *class); 180extern void html_link_open(char *url, char *title, char *class);
162extern void html_link_close(void); 181extern void html_link_close(void);
163extern void html_filemode(unsigned short mode); 182extern void html_filemode(unsigned short mode);
164extern int html_include(const char *filename); 183extern int html_include(const char *filename);
165 184
166extern int cgit_read_config(const char *filename, configfn fn); 185extern int cgit_read_config(const char *filename, configfn fn);
167extern int cgit_parse_query(char *txt, configfn fn); 186extern int cgit_parse_query(char *txt, configfn fn);
168extern struct commitinfo *cgit_parse_commit(struct commit *commit); 187extern struct commitinfo *cgit_parse_commit(struct commit *commit);
169extern struct taginfo *cgit_parse_tag(struct tag *tag); 188extern struct taginfo *cgit_parse_tag(struct tag *tag);
170extern void cgit_parse_url(const char *url); 189extern void cgit_parse_url(const char *url);
171 190
172extern char *cache_safe_filename(const char *unsafe); 191extern char *cache_safe_filename(const char *unsafe);
173extern int cache_lock(struct cacheitem *item); 192extern int cache_lock(struct cacheitem *item);
174extern int cache_unlock(struct cacheitem *item); 193extern int cache_unlock(struct cacheitem *item);
175extern int cache_cancel_lock(struct cacheitem *item); 194extern int cache_cancel_lock(struct cacheitem *item);
176extern int cache_exist(struct cacheitem *item); 195extern int cache_exist(struct cacheitem *item);
177extern int cache_expired(struct cacheitem *item); 196extern int cache_expired(struct cacheitem *item);
178 197
179extern char *cgit_repourl(const char *reponame); 198extern char *cgit_repourl(const char *reponame);
180extern char *cgit_pageurl(const char *reponame, const char *pagename, 199extern char *cgit_pageurl(const char *reponame, const char *pagename,
181 const char *query); 200 const char *query);
182 201
183extern void cgit_print_error(char *msg); 202extern void cgit_print_error(char *msg);
184extern void cgit_print_date(unsigned long secs); 203extern void cgit_print_date(time_t secs, char *format);
204extern void cgit_print_age(time_t t, time_t max_relative, char *format);
185extern void cgit_print_docstart(char *title, struct cacheitem *item); 205extern void cgit_print_docstart(char *title, struct cacheitem *item);
186extern void cgit_print_docend(); 206extern void cgit_print_docend();
187extern void cgit_print_pageheader(char *title, int show_search); 207extern void cgit_print_pageheader(char *title, int show_search);
188extern void cgit_print_snapshot_start(const char *mimetype, 208extern void cgit_print_snapshot_start(const char *mimetype,
189 const char *filename, 209 const char *filename,
190 struct cacheitem *item); 210 struct cacheitem *item);
191 211
192extern void cgit_print_repolist(struct cacheitem *item); 212extern void cgit_print_repolist(struct cacheitem *item);
193extern void cgit_print_summary(); 213extern void cgit_print_summary();
194extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path); 214extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path);
195extern void cgit_print_view(const char *hex, char *path); 215extern void cgit_print_view(const char *hex, char *path);
196extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 216extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
197extern void cgit_print_tree(const char *rev, const char *hex, char *path); 217extern void cgit_print_tree(const char *rev, const char *hex, char *path);
198extern void cgit_print_commit(const char *hex); 218extern void cgit_print_commit(const char *hex);
199extern void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex, 219extern void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex,
200 char *path); 220 char *path);
201extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 221extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
202 const char *format, const char *prefix, 222 const char *format, const char *prefix,
203 const char *filename); 223 const char *filename);
204 224
205#endif /* CGIT_H */ 225#endif /* CGIT_H */
diff --git a/ui-commit.c b/ui-commit.c
index ff1fad3..59eeb1d 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -79,162 +79,162 @@ void print_fileinfo(struct fileinfo *info)
79 sha1_to_hex(info->new_sha1), info->new_path); 79 sha1_to_hex(info->new_sha1), info->new_path);
80 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), 80 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query),
81 NULL, NULL); 81 NULL, NULL);
82 if (info->status == DIFF_STATUS_COPIED || 82 if (info->status == DIFF_STATUS_COPIED ||
83 info->status == DIFF_STATUS_RENAMED) { 83 info->status == DIFF_STATUS_RENAMED) {
84 html_txt(info->new_path); 84 html_txt(info->new_path);
85 htmlf("</a> (%s from ", info->status == DIFF_STATUS_COPIED ? 85 htmlf("</a> (%s from ", info->status == DIFF_STATUS_COPIED ?
86 "copied" : "renamed"); 86 "copied" : "renamed");
87 query2 = fmt("id=%s", sha1_to_hex(info->old_sha1)); 87 query2 = fmt("id=%s", sha1_to_hex(info->old_sha1));
88 html_link_open(cgit_pageurl(cgit_query_repo, "view", query2), 88 html_link_open(cgit_pageurl(cgit_query_repo, "view", query2),
89 NULL, NULL); 89 NULL, NULL);
90 html_txt(info->old_path); 90 html_txt(info->old_path);
91 html("</a>)"); 91 html("</a>)");
92 } else { 92 } else {
93 html_txt(info->new_path); 93 html_txt(info->new_path);
94 html("</a>"); 94 html("</a>");
95 } 95 }
96 html("</td><td class='right'>"); 96 html("</td><td class='right'>");
97 htmlf("%d", info->added + info->removed); 97 htmlf("%d", info->added + info->removed);
98 html("</td><td class='graph'>"); 98 html("</td><td class='graph'>");
99 htmlf("<table width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); 99 htmlf("<table width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
100 htmlf("<td class='add' style='width: %.1f%%;'/>", 100 htmlf("<td class='add' style='width: %.1f%%;'/>",
101 info->added * 100.0 / max_changes); 101 info->added * 100.0 / max_changes);
102 htmlf("<td class='rem' style='width: %.1f%%;'/>", 102 htmlf("<td class='rem' style='width: %.1f%%;'/>",
103 info->removed * 100.0 / max_changes); 103 info->removed * 100.0 / max_changes);
104 htmlf("<td class='none' style='width: %.1f%%;'/>", 104 htmlf("<td class='none' style='width: %.1f%%;'/>",
105 (max_changes - info->removed - info->added) * 100.0 / max_changes); 105 (max_changes - info->removed - info->added) * 100.0 / max_changes);
106 html("</tr></table></a></td></tr>\n"); 106 html("</tr></table></a></td></tr>\n");
107} 107}
108 108
109void cgit_count_diff_lines(char *line, int len) 109void cgit_count_diff_lines(char *line, int len)
110{ 110{
111 if (line && (len > 0)) { 111 if (line && (len > 0)) {
112 if (line[0] == '+') 112 if (line[0] == '+')
113 lines_added++; 113 lines_added++;
114 else if (line[0] == '-') 114 else if (line[0] == '-')
115 lines_removed++; 115 lines_removed++;
116 } 116 }
117} 117}
118 118
119void inspect_filepair(struct diff_filepair *pair) 119void inspect_filepair(struct diff_filepair *pair)
120{ 120{
121 files++; 121 files++;
122 lines_added = 0; 122 lines_added = 0;
123 lines_removed = 0; 123 lines_removed = 0;
124 cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines); 124 cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines);
125 if (files >= slots) { 125 if (files >= slots) {
126 if (slots == 0) 126 if (slots == 0)
127 slots = 4; 127 slots = 4;
128 else 128 else
129 slots = slots * 2; 129 slots = slots * 2;
130 items = xrealloc(items, slots * sizeof(struct fileinfo)); 130 items = xrealloc(items, slots * sizeof(struct fileinfo));
131 } 131 }
132 items[files-1].status = pair->status; 132 items[files-1].status = pair->status;
133 hashcpy(items[files-1].old_sha1, pair->one->sha1); 133 hashcpy(items[files-1].old_sha1, pair->one->sha1);
134 hashcpy(items[files-1].new_sha1, pair->two->sha1); 134 hashcpy(items[files-1].new_sha1, pair->two->sha1);
135 items[files-1].old_mode = pair->one->mode; 135 items[files-1].old_mode = pair->one->mode;
136 items[files-1].new_mode = pair->two->mode; 136 items[files-1].new_mode = pair->two->mode;
137 items[files-1].old_path = xstrdup(pair->one->path); 137 items[files-1].old_path = xstrdup(pair->one->path);
138 items[files-1].new_path = xstrdup(pair->two->path); 138 items[files-1].new_path = xstrdup(pair->two->path);
139 items[files-1].added = lines_added; 139 items[files-1].added = lines_added;
140 items[files-1].removed = lines_removed; 140 items[files-1].removed = lines_removed;
141 if (lines_added + lines_removed > max_changes) 141 if (lines_added + lines_removed > max_changes)
142 max_changes = lines_added + lines_removed; 142 max_changes = lines_added + lines_removed;
143 total_adds += lines_added; 143 total_adds += lines_added;
144 total_rems += lines_removed; 144 total_rems += lines_removed;
145} 145}
146 146
147 147
148void cgit_print_commit(const char *hex) 148void cgit_print_commit(const char *hex)
149{ 149{
150 struct commit *commit, *parent; 150 struct commit *commit, *parent;
151 struct commitinfo *info; 151 struct commitinfo *info;
152 struct commit_list *p; 152 struct commit_list *p;
153 unsigned char sha1[20]; 153 unsigned char sha1[20];
154 char *query; 154 char *query;
155 char *filename; 155 char *filename;
156 int i; 156 int i;
157 157
158 if (get_sha1(hex, sha1)) { 158 if (get_sha1(hex, sha1)) {
159 cgit_print_error(fmt("Bad object id: %s", hex)); 159 cgit_print_error(fmt("Bad object id: %s", hex));
160 return; 160 return;
161 } 161 }
162 commit = lookup_commit_reference(sha1); 162 commit = lookup_commit_reference(sha1);
163 if (!commit) { 163 if (!commit) {
164 cgit_print_error(fmt("Bad commit reference: %s", hex)); 164 cgit_print_error(fmt("Bad commit reference: %s", hex));
165 return; 165 return;
166 } 166 }
167 info = cgit_parse_commit(commit); 167 info = cgit_parse_commit(commit);
168 168
169 html("<table class='commit-info'>\n"); 169 html("<table class='commit-info'>\n");
170 html("<tr><th>author</th><td>"); 170 html("<tr><th>author</th><td>");
171 html_txt(info->author); 171 html_txt(info->author);
172 html(" "); 172 html(" ");
173 html_txt(info->author_email); 173 html_txt(info->author_email);
174 html("</td><td class='right'>"); 174 html("</td><td class='right'>");
175 cgit_print_date(info->author_date); 175 cgit_print_date(info->author_date, FMT_LONGDATE);
176 html("</td></tr>\n"); 176 html("</td></tr>\n");
177 html("<tr><th>committer</th><td>"); 177 html("<tr><th>committer</th><td>");
178 html_txt(info->committer); 178 html_txt(info->committer);
179 html(" "); 179 html(" ");
180 html_txt(info->committer_email); 180 html_txt(info->committer_email);
181 html("</td><td class='right'>"); 181 html("</td><td class='right'>");
182 cgit_print_date(info->committer_date); 182 cgit_print_date(info->committer_date, FMT_LONGDATE);
183 html("</td></tr>\n"); 183 html("</td></tr>\n");
184 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='"); 184 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='");
185 query = fmt("h=%s&id=%s", sha1_to_hex(commit->object.sha1), 185 query = fmt("h=%s&id=%s", sha1_to_hex(commit->object.sha1),
186 sha1_to_hex(commit->tree->object.sha1)); 186 sha1_to_hex(commit->tree->object.sha1));
187 html_attr(cgit_pageurl(cgit_query_repo, "tree", query)); 187 html_attr(cgit_pageurl(cgit_query_repo, "tree", query));
188 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1)); 188 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1));
189 for (p = commit->parents; p ; p = p->next) { 189 for (p = commit->parents; p ; p = p->next) {
190 parent = lookup_commit_reference(p->item->object.sha1); 190 parent = lookup_commit_reference(p->item->object.sha1);
191 if (!parent) { 191 if (!parent) {
192 html("<tr><td colspan='3'>"); 192 html("<tr><td colspan='3'>");
193 cgit_print_error("Error reading parent commit"); 193 cgit_print_error("Error reading parent commit");
194 html("</td></tr>"); 194 html("</td></tr>");
195 continue; 195 continue;
196 } 196 }
197 html("<tr><th>parent</th>" 197 html("<tr><th>parent</th>"
198 "<td colspan='2' class='sha1'>" 198 "<td colspan='2' class='sha1'>"
199 "<a href='"); 199 "<a href='");
200 query = fmt("h=%s", sha1_to_hex(p->item->object.sha1)); 200 query = fmt("h=%s", sha1_to_hex(p->item->object.sha1));
201 html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); 201 html_attr(cgit_pageurl(cgit_query_repo, "commit", query));
202 htmlf("'>%s</a> (<a href='", 202 htmlf("'>%s</a> (<a href='",
203 sha1_to_hex(p->item->object.sha1)); 203 sha1_to_hex(p->item->object.sha1));
204 query = fmt("id=%s&id2=%s", sha1_to_hex(parent->tree->object.sha1), 204 query = fmt("id=%s&id2=%s", sha1_to_hex(parent->tree->object.sha1),
205 sha1_to_hex(commit->tree->object.sha1)); 205 sha1_to_hex(commit->tree->object.sha1));
206 html_attr(cgit_pageurl(cgit_query_repo, "diff", query)); 206 html_attr(cgit_pageurl(cgit_query_repo, "diff", query));
207 html("'>diff</a>)</td></tr>"); 207 html("'>diff</a>)</td></tr>");
208 } 208 }
209 if (cgit_repo->snapshots) { 209 if (cgit_repo->snapshots) {
210 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='"); 210 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='");
211 filename = fmt("%s-%s.zip", cgit_query_repo, hex); 211 filename = fmt("%s-%s.zip", cgit_query_repo, hex);
212 html_attr(cgit_pageurl(cgit_query_repo, "snapshot", 212 html_attr(cgit_pageurl(cgit_query_repo, "snapshot",
213 fmt("id=%s&name=%s", hex, filename))); 213 fmt("id=%s&name=%s", hex, filename)));
214 htmlf("'>%s</a></td></tr>", filename); 214 htmlf("'>%s</a></td></tr>", filename);
215 } 215 }
216 html("</table>\n"); 216 html("</table>\n");
217 html("<div class='commit-subject'>"); 217 html("<div class='commit-subject'>");
218 html_txt(info->subject); 218 html_txt(info->subject);
219 html("</div>"); 219 html("</div>");
220 html("<div class='commit-msg'>"); 220 html("<div class='commit-msg'>");
221 html_txt(info->msg); 221 html_txt(info->msg);
222 html("</div>"); 222 html("</div>");
223 if (!(commit->parents && commit->parents->next && commit->parents->next->next)) { 223 if (!(commit->parents && commit->parents->next && commit->parents->next->next)) {
224 html("<div class='diffstat-header'>Diffstat</div>"); 224 html("<div class='diffstat-header'>Diffstat</div>");
225 html("<table class='diffstat'>"); 225 html("<table class='diffstat'>");
226 max_changes = 0; 226 max_changes = 0;
227 cgit_diff_commit(commit, inspect_filepair); 227 cgit_diff_commit(commit, inspect_filepair);
228 for(i = 0; i<files; i++) 228 for(i = 0; i<files; i++)
229 print_fileinfo(&items[i]); 229 print_fileinfo(&items[i]);
230 html("</table>"); 230 html("</table>");
231 html("<div class='diffstat-summary'>"); 231 html("<div class='diffstat-summary'>");
232 htmlf("%d files changed, %d insertions, %d deletions (", 232 htmlf("%d files changed, %d insertions, %d deletions (",
233 files, total_adds, total_rems); 233 files, total_adds, total_rems);
234 query = fmt("h=%s", hex); 234 query = fmt("h=%s", hex);
235 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), NULL, NULL); 235 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), NULL, NULL);
236 html("show diff</a>)"); 236 html("show diff</a>)");
237 html("</div>"); 237 html("</div>");
238 } 238 }
239 cgit_free_commitinfo(info); 239 cgit_free_commitinfo(info);
240} 240}
diff --git a/ui-shared.c b/ui-shared.c
index c7fbc5e..acc771b 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,180 +1,221 @@
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 10
11const char cgit_doctype[] = 11const char cgit_doctype[] =
12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
14 14
15static char *http_date(time_t t) 15static char *http_date(time_t t)
16{ 16{
17 static char day[][4] = 17 static char day[][4] =
18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
19 static char month[][4] = 19 static char month[][4] =
20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; 21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
22 struct tm *tm = gmtime(&t); 22 struct tm *tm = gmtime(&t);
23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
25 tm->tm_hour, tm->tm_min, tm->tm_sec); 25 tm->tm_hour, tm->tm_min, tm->tm_sec);
26} 26}
27 27
28static long ttl_seconds(long ttl) 28static long ttl_seconds(long ttl)
29{ 29{
30 if (ttl<0) 30 if (ttl<0)
31 return 60 * 60 * 24 * 365; 31 return 60 * 60 * 24 * 365;
32 else 32 else
33 return ttl * 60; 33 return ttl * 60;
34} 34}
35 35
36void cgit_print_error(char *msg) 36void cgit_print_error(char *msg)
37{ 37{
38 html("<div class='error'>"); 38 html("<div class='error'>");
39 html_txt(msg); 39 html_txt(msg);
40 html("</div>\n"); 40 html("</div>\n");
41} 41}
42 42
43char *cgit_rooturl() 43char *cgit_rooturl()
44{ 44{
45 if (cgit_virtual_root) 45 if (cgit_virtual_root)
46 return fmt("%s/", cgit_virtual_root); 46 return fmt("%s/", cgit_virtual_root);
47 else 47 else
48 return cgit_script_name; 48 return cgit_script_name;
49} 49}
50 50
51char *cgit_repourl(const char *reponame) 51char *cgit_repourl(const char *reponame)
52{ 52{
53 if (cgit_virtual_root) { 53 if (cgit_virtual_root) {
54 return fmt("%s/%s/", cgit_virtual_root, reponame); 54 return fmt("%s/%s/", cgit_virtual_root, reponame);
55 } else { 55 } else {
56 return fmt("?r=%s", reponame); 56 return fmt("?r=%s", reponame);
57 } 57 }
58} 58}
59 59
60char *cgit_pageurl(const char *reponame, const char *pagename, 60char *cgit_pageurl(const char *reponame, const char *pagename,
61 const char *query) 61 const char *query)
62{ 62{
63 if (cgit_virtual_root) { 63 if (cgit_virtual_root) {
64 if (query) 64 if (query)
65 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame,
66 pagename, query); 66 pagename, query);
67 else 67 else
68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame, 68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame,
69 pagename); 69 pagename);
70 } else { 70 } else {
71 if (query) 71 if (query)
72 return fmt("?r=%s&p=%s&%s", reponame, pagename, query); 72 return fmt("?r=%s&p=%s&%s", reponame, pagename, query);
73 else 73 else
74 return fmt("?r=%s&p=%s", reponame, pagename); 74 return fmt("?r=%s&p=%s", reponame, pagename);
75 } 75 }
76} 76}
77 77
78char *cgit_currurl() 78char *cgit_currurl()
79{ 79{
80 if (!cgit_virtual_root) 80 if (!cgit_virtual_root)
81 return cgit_script_name; 81 return cgit_script_name;
82 else if (cgit_query_page) 82 else if (cgit_query_page)
83 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page); 83 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page);
84 else if (cgit_query_repo) 84 else if (cgit_query_repo)
85 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); 85 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
86 else 86 else
87 return fmt("%s/", cgit_virtual_root); 87 return fmt("%s/", cgit_virtual_root);
88} 88}
89 89
90 90
91void cgit_print_date(unsigned long secs) 91void cgit_print_date(time_t secs, char *format)
92{ 92{
93 char buf[32]; 93 char buf[64];
94 struct tm *time; 94 struct tm *time;
95 95
96 time = gmtime(&secs); 96 time = gmtime(&secs);
97 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", time); 97 strftime(buf, sizeof(buf)-1, format, time);
98 html_txt(buf); 98 html_txt(buf);
99} 99}
100 100
101void cgit_print_age(time_t t, time_t max_relative, char *format)
102{
103 time_t now, secs;
104
105 time(&now);
106 secs = now - t;
107
108 if (secs > max_relative && max_relative >= 0) {
109 cgit_print_date(t, format);
110 return;
111 }
112
113 if (secs < TM_HOUR * 2) {
114 htmlf("<span class='age-mins'>%.0f min.</span>",
115 secs * 1.0 / TM_MIN);
116 return;
117 }
118 if (secs < TM_DAY * 2) {
119 htmlf("<span class='age-hours'>%.0f hours</span>",
120 secs * 1.0 / TM_HOUR);
121 return;
122 }
123 if (secs < TM_WEEK * 2) {
124 htmlf("<span class='age-days'>%.0f days</span>",
125 secs * 1.0 / TM_DAY);
126 return;
127 }
128 if (secs < TM_MONTH * 2) {
129 htmlf("<span class='age-weeks'>%.0f weeks</span>",
130 secs * 1.0 / TM_WEEK);
131 return;
132 }
133 if (secs < TM_YEAR * 2) {
134 htmlf("<span class='age-months'>%.0f months</span>",
135 secs * 1.0 / TM_MONTH);
136 return;
137 }
138 htmlf("<span class='age-years'>%.0f years</span>",
139 secs * 1.0 / TM_YEAR);
140}
141
101void cgit_print_docstart(char *title, struct cacheitem *item) 142void cgit_print_docstart(char *title, struct cacheitem *item)
102{ 143{
103 html("Content-Type: text/html; charset=utf-8\n"); 144 html("Content-Type: text/html; charset=utf-8\n");
104 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 145 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
105 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 146 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
106 ttl_seconds(item->ttl))); 147 ttl_seconds(item->ttl)));
107 html("\n"); 148 html("\n");
108 html(cgit_doctype); 149 html(cgit_doctype);
109 html("<html>\n"); 150 html("<html>\n");
110 html("<head>\n"); 151 html("<head>\n");
111 html("<title>"); 152 html("<title>");
112 html_txt(title); 153 html_txt(title);
113 html("</title>\n"); 154 html("</title>\n");
114 htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version); 155 htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version);
115 html("<link rel='stylesheet' type='text/css' href='"); 156 html("<link rel='stylesheet' type='text/css' href='");
116 html_attr(cgit_css); 157 html_attr(cgit_css);
117 html("'/>\n"); 158 html("'/>\n");
118 html("</head>\n"); 159 html("</head>\n");
119 html("<body>\n"); 160 html("<body>\n");
120} 161}
121 162
122void cgit_print_docend() 163void cgit_print_docend()
123{ 164{
124 html("</td></tr></table>"); 165 html("</td></tr></table>");
125 html("</body>\n</html>\n"); 166 html("</body>\n</html>\n");
126} 167}
127 168
128void cgit_print_pageheader(char *title, int show_search) 169void cgit_print_pageheader(char *title, int show_search)
129{ 170{
130 html("<table id='layout'>"); 171 html("<table id='layout'>");
131 html("<tr><td id='header'>"); 172 html("<tr><td id='header'>");
132 html(cgit_root_title); 173 html(cgit_root_title);
133 html("</td><td id='logo'>"); 174 html("</td><td id='logo'>");
134 html("<a href='"); 175 html("<a href='");
135 html_attr(cgit_logo_link); 176 html_attr(cgit_logo_link);
136 htmlf("'><img src='%s'/></a>", cgit_logo); 177 htmlf("'><img src='%s'/></a>", cgit_logo);
137 html("</td></tr>"); 178 html("</td></tr>");
138 html("<tr><td id='crumb'>"); 179 html("<tr><td id='crumb'>");
139 htmlf("<a href='%s'>root</a>", cgit_rooturl()); 180 htmlf("<a href='%s'>root</a>", cgit_rooturl());
140 if (cgit_query_repo) { 181 if (cgit_query_repo) {
141 htmlf(" : <a href='%s'>", cgit_repourl(cgit_repo->url)); 182 htmlf(" : <a href='%s'>", cgit_repourl(cgit_repo->url));
142 html_txt(cgit_repo->name); 183 html_txt(cgit_repo->name);
143 htmlf("</a> : %s", title); 184 htmlf("</a> : %s", title);
144 } 185 }
145 html("</td>"); 186 html("</td>");
146 html("<td id='search'>"); 187 html("<td id='search'>");
147 if (show_search) { 188 if (show_search) {
148 html("<form method='get' href='"); 189 html("<form method='get' href='");
149 html_attr(cgit_currurl()); 190 html_attr(cgit_currurl());
150 html("'>"); 191 html("'>");
151 if (!cgit_virtual_root) { 192 if (!cgit_virtual_root) {
152 if (cgit_query_repo) 193 if (cgit_query_repo)
153 html_hidden("r", cgit_query_repo); 194 html_hidden("r", cgit_query_repo);
154 if (cgit_query_page) 195 if (cgit_query_page)
155 html_hidden("p", cgit_query_page); 196 html_hidden("p", cgit_query_page);
156 } 197 }
157 if (cgit_query_head) 198 if (cgit_query_head)
158 html_hidden("h", cgit_query_head); 199 html_hidden("h", cgit_query_head);
159 if (cgit_query_sha1) 200 if (cgit_query_sha1)
160 html_hidden("id", cgit_query_sha1); 201 html_hidden("id", cgit_query_sha1);
161 if (cgit_query_sha2) 202 if (cgit_query_sha2)
162 html_hidden("id2", cgit_query_sha2); 203 html_hidden("id2", cgit_query_sha2);
163 html("<input type='text' name='q' value='"); 204 html("<input type='text' name='q' value='");
164 html_attr(cgit_query_search); 205 html_attr(cgit_query_search);
165 html("'/></form>"); 206 html("'/></form>");
166 } 207 }
167 html("</td></tr>"); 208 html("</td></tr>");
168 html("<tr><td id='content' colspan='2'>"); 209 html("<tr><td id='content' colspan='2'>");
169} 210}
170 211
171void cgit_print_snapshot_start(const char *mimetype, const char *filename, 212void cgit_print_snapshot_start(const char *mimetype, const char *filename,
172 struct cacheitem *item) 213 struct cacheitem *item)
173{ 214{
174 htmlf("Content-Type: %s\n", mimetype); 215 htmlf("Content-Type: %s\n", mimetype);
175 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 216 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
176 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 217 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
177 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 218 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
178 ttl_seconds(item->ttl))); 219 ttl_seconds(item->ttl)));
179 html("\n"); 220 html("\n");
180} 221}
diff --git a/ui-summary.c b/ui-summary.c
index e7158cc..20394de 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -1,207 +1,207 @@
1/* ui-summary.c: functions for generating repo summary page 1/* ui-summary.c: functions for generating repo summary page
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11static int header; 11static int header;
12 12
13static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1, 13static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
14 int flags, void *cb_data) 14 int flags, void *cb_data)
15{ 15{
16 struct commit *commit; 16 struct commit *commit;
17 struct commitinfo *info; 17 struct commitinfo *info;
18 char buf[256], *url; 18 char buf[256], *url;
19 19
20 strncpy(buf, refname, sizeof(buf)); 20 strncpy(buf, refname, sizeof(buf));
21 commit = lookup_commit(sha1); 21 commit = lookup_commit(sha1);
22 if (commit && !parse_commit(commit)){ 22 if (commit && !parse_commit(commit)){
23 info = cgit_parse_commit(commit); 23 info = cgit_parse_commit(commit);
24 html("<tr><td>"); 24 html("<tr><td>");
25 url = cgit_pageurl(cgit_query_repo, "log", 25 url = cgit_pageurl(cgit_query_repo, "log",
26 fmt("h=%s", refname)); 26 fmt("h=%s", refname));
27 html_link_open(url, NULL, NULL); 27 html_link_open(url, NULL, NULL);
28 html_txt(buf); 28 html_txt(buf);
29 html_link_close(); 29 html_link_close();
30 html("</td><td>"); 30 html("</td><td>");
31 cgit_print_date(commit->date); 31 cgit_print_date(commit->date, FMT_LONGDATE);
32 html("</td><td>"); 32 html("</td><td>");
33 html_txt(info->author); 33 html_txt(info->author);
34 html("</td><td>"); 34 html("</td><td>");
35 url = cgit_pageurl(cgit_query_repo, "commit", 35 url = cgit_pageurl(cgit_query_repo, "commit",
36 fmt("h=%s", sha1_to_hex(sha1))); 36 fmt("h=%s", sha1_to_hex(sha1)));
37 html_link_open(url, NULL, NULL); 37 html_link_open(url, NULL, NULL);
38 html_ntxt(cgit_max_msg_len, info->subject); 38 html_ntxt(cgit_max_msg_len, info->subject);
39 html_link_close(); 39 html_link_close();
40 html("</td></tr>\n"); 40 html("</td></tr>\n");
41 cgit_free_commitinfo(info); 41 cgit_free_commitinfo(info);
42 } else { 42 } else {
43 html("<tr><td>"); 43 html("<tr><td>");
44 html_txt(buf); 44 html_txt(buf);
45 html("</td><td colspan='3'>"); 45 html("</td><td colspan='3'>");
46 htmlf("*** bad ref %s ***", sha1_to_hex(sha1)); 46 htmlf("*** bad ref %s ***", sha1_to_hex(sha1));
47 html("</td></tr>\n"); 47 html("</td></tr>\n");
48 } 48 }
49 return 0; 49 return 0;
50} 50}
51 51
52 52
53static void cgit_print_object_ref(struct object *obj) 53static void cgit_print_object_ref(struct object *obj)
54{ 54{
55 char *page, *arg, *url; 55 char *page, *arg, *url;
56 56
57 if (obj->type == OBJ_COMMIT) { 57 if (obj->type == OBJ_COMMIT) {
58 page = "commit"; 58 page = "commit";
59 arg = "h"; 59 arg = "h";
60 } else if (obj->type == OBJ_TREE) { 60 } else if (obj->type == OBJ_TREE) {
61 page = "tree"; 61 page = "tree";
62 arg = "id"; 62 arg = "id";
63 } else { 63 } else {
64 page = "view"; 64 page = "view";
65 arg = "id"; 65 arg = "id";
66 } 66 }
67 67
68 url = cgit_pageurl(cgit_query_repo, page, 68 url = cgit_pageurl(cgit_query_repo, page,
69 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 69 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
70 html_link_open(url, NULL, NULL); 70 html_link_open(url, NULL, NULL);
71 htmlf("%s %s", typename(obj->type), 71 htmlf("%s %s", typename(obj->type),
72 sha1_to_hex(obj->sha1)); 72 sha1_to_hex(obj->sha1));
73 html_link_close(); 73 html_link_close();
74} 74}
75 75
76static void print_tag_header() 76static void print_tag_header()
77{ 77{
78 html("<tr class='nohover'><th class='left'>Tag</th>" 78 html("<tr class='nohover'><th class='left'>Tag</th>"
79 "<th class='left'>Created</th>" 79 "<th class='left'>Created</th>"
80 "<th class='left'>Author</th>" 80 "<th class='left'>Author</th>"
81 "<th class='left'>Reference</th></tr>\n"); 81 "<th class='left'>Reference</th></tr>\n");
82 header = 1; 82 header = 1;
83} 83}
84 84
85static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1, 85static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
86 int flags, void *cb_data) 86 int flags, void *cb_data)
87{ 87{
88 struct tag *tag; 88 struct tag *tag;
89 struct taginfo *info; 89 struct taginfo *info;
90 struct object *obj; 90 struct object *obj;
91 char buf[256], *url; 91 char buf[256], *url;
92 92
93 strncpy(buf, refname, sizeof(buf)); 93 strncpy(buf, refname, sizeof(buf));
94 obj = parse_object(sha1); 94 obj = parse_object(sha1);
95 if (!obj) 95 if (!obj)
96 return 1; 96 return 1;
97 if (obj->type == OBJ_TAG) { 97 if (obj->type == OBJ_TAG) {
98 tag = lookup_tag(sha1); 98 tag = lookup_tag(sha1);
99 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 99 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
100 return 2; 100 return 2;
101 if (!header) 101 if (!header)
102 print_tag_header(); 102 print_tag_header();
103 html("<tr><td>"); 103 html("<tr><td>");
104 url = cgit_pageurl(cgit_query_repo, "view", 104 url = cgit_pageurl(cgit_query_repo, "view",
105 fmt("id=%s", sha1_to_hex(sha1))); 105 fmt("id=%s", sha1_to_hex(sha1)));
106 html_link_open(url, NULL, NULL); 106 html_link_open(url, NULL, NULL);
107 html_txt(buf); 107 html_txt(buf);
108 html_link_close(); 108 html_link_close();
109 html("</td><td>"); 109 html("</td><td>");
110 if (info->tagger_date > 0) 110 if (info->tagger_date > 0)
111 cgit_print_date(info->tagger_date); 111 cgit_print_date(info->tagger_date, FMT_LONGDATE);
112 html("</td><td>"); 112 html("</td><td>");
113 if (info->tagger) 113 if (info->tagger)
114 html(info->tagger); 114 html(info->tagger);
115 html("</td><td>"); 115 html("</td><td>");
116 cgit_print_object_ref(tag->tagged); 116 cgit_print_object_ref(tag->tagged);
117 html("</td></tr>\n"); 117 html("</td></tr>\n");
118 } else { 118 } else {
119 if (!header) 119 if (!header)
120 print_tag_header(); 120 print_tag_header();
121 html("<tr><td>"); 121 html("<tr><td>");
122 html_txt(buf); 122 html_txt(buf);
123 html("</td><td colspan='2'/><td>"); 123 html("</td><td colspan='2'/><td>");
124 cgit_print_object_ref(obj); 124 cgit_print_object_ref(obj);
125 html("</td></tr>\n"); 125 html("</td></tr>\n");
126 } 126 }
127 return 0; 127 return 0;
128} 128}
129 129
130static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1, 130static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
131 int flags, void *cb_data) 131 int flags, void *cb_data)
132{ 132{
133 struct tag *tag; 133 struct tag *tag;
134 struct taginfo *info; 134 struct taginfo *info;
135 struct object *obj; 135 struct object *obj;
136 char buf[256], *url; 136 char buf[256], *url;
137 unsigned char fileid[20]; 137 unsigned char fileid[20];
138 138
139 if (prefixcmp(refname, "refs/archives")) 139 if (prefixcmp(refname, "refs/archives"))
140 return 0; 140 return 0;
141 strncpy(buf, refname+14, sizeof(buf)); 141 strncpy(buf, refname+14, sizeof(buf));
142 obj = parse_object(sha1); 142 obj = parse_object(sha1);
143 if (!obj) 143 if (!obj)
144 return 1; 144 return 1;
145 if (obj->type == OBJ_TAG) { 145 if (obj->type == OBJ_TAG) {
146 tag = lookup_tag(sha1); 146 tag = lookup_tag(sha1);
147 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 147 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
148 return 0; 148 return 0;
149 hashcpy(fileid, tag->tagged->sha1); 149 hashcpy(fileid, tag->tagged->sha1);
150 } else if (obj->type != OBJ_BLOB) { 150 } else if (obj->type != OBJ_BLOB) {
151 return 0; 151 return 0;
152 } else { 152 } else {
153 hashcpy(fileid, sha1); 153 hashcpy(fileid, sha1);
154 } 154 }
155 if (!header) { 155 if (!header) {
156 html("<table>"); 156 html("<table>");
157 html("<tr><th>Downloads</th></tr>"); 157 html("<tr><th>Downloads</th></tr>");
158 header = 1; 158 header = 1;
159 } 159 }
160 html("<tr><td>"); 160 html("<tr><td>");
161 url = cgit_pageurl(cgit_query_repo, "blob", 161 url = cgit_pageurl(cgit_query_repo, "blob",
162 fmt("id=%s&path=%s", sha1_to_hex(fileid), 162 fmt("id=%s&path=%s", sha1_to_hex(fileid),
163 buf)); 163 buf));
164 html_link_open(url, NULL, NULL); 164 html_link_open(url, NULL, NULL);
165 html_txt(buf); 165 html_txt(buf);
166 html_link_close(); 166 html_link_close();
167 html("</td><tr>"); 167 html("</td><tr>");
168 return 0; 168 return 0;
169} 169}
170 170
171static void cgit_print_branches() 171static void cgit_print_branches()
172{ 172{
173 html("<tr class='nohover'><th class='left'>Branch</th>" 173 html("<tr class='nohover'><th class='left'>Branch</th>"
174 "<th class='left'>Updated</th>" 174 "<th class='left'>Updated</th>"
175 "<th class='left'>Author</th>" 175 "<th class='left'>Author</th>"
176 "<th class='left'>Head commit</th></tr>\n"); 176 "<th class='left'>Head commit</th></tr>\n");
177 for_each_branch_ref(cgit_print_branch_cb, NULL); 177 for_each_branch_ref(cgit_print_branch_cb, NULL);
178} 178}
179 179
180static void cgit_print_tags() 180static void cgit_print_tags()
181{ 181{
182 header = 0; 182 header = 0;
183 for_each_tag_ref(cgit_print_tag_cb, NULL); 183 for_each_tag_ref(cgit_print_tag_cb, NULL);
184} 184}
185 185
186static void cgit_print_archives() 186static void cgit_print_archives()
187{ 187{
188 header = 0; 188 header = 0;
189 for_each_ref(cgit_print_archive_cb, NULL); 189 for_each_ref(cgit_print_archive_cb, NULL);
190 if (header) 190 if (header)
191 html("</table>"); 191 html("</table>");
192} 192}
193 193
194void cgit_print_summary() 194void cgit_print_summary()
195{ 195{
196 html("<table class='list nowrap'>"); 196 html("<table class='list nowrap'>");
197 html("<tr class='nohover'><td id='summary' colspan='3'>"); 197 html("<tr class='nohover'><td id='summary' colspan='3'>");
198 html("<h2>"); 198 html("<h2>");
199 html_txt(cgit_repo->name); 199 html_txt(cgit_repo->name);
200 html(" - "); 200 html(" - ");
201 html_txt(cgit_repo->desc); 201 html_txt(cgit_repo->desc);
202 html("</h2>"); 202 html("</h2>");
203 html("</td><td id='archivelist'>"); 203 html("</td><td id='archivelist'>");
204 cgit_print_archives(); 204 cgit_print_archives();
205 html("</td></tr>"); 205 html("</td></tr>");
206 cgit_print_branches(); 206 cgit_print_branches();
207 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 207 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");