summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2007-07-22 21:42:55 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2007-07-22 21:44:57 (UTC)
commit4e9107abfe8d3edff17826875b417bcf40dc7390 (patch) (unidiff)
tree96cfafa9b8838f34e1363ee3019eae64a6229b30
parent71ebcbe23ab548e5c0ad40aa8be5741654ed3201 (diff)
downloadcgit-4e9107abfe8d3edff17826875b417bcf40dc7390.zip
cgit-4e9107abfe8d3edff17826875b417bcf40dc7390.tar.gz
cgit-4e9107abfe8d3edff17826875b417bcf40dc7390.tar.bz2
Add ui-tag.c
This file implements the tag-command, i.e. printing of annotated tags. Signed-off-by: Lars Hjemli <hjemli@gmail.com> Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c3
-rw-r--r--cgit.h5
-rw-r--r--shared.c2
-rw-r--r--ui-shared.c24
-rw-r--r--ui-summary.c33
-rw-r--r--ui-tag.c74
7 files changed, 111 insertions, 32 deletions
diff --git a/Makefile b/Makefile
index cea09f1..50d0011 100644
--- a/Makefile
+++ b/Makefile
@@ -1,94 +1,94 @@
1prefix = /var/www/htdocs/cgit 1prefix = /var/www/htdocs/cgit
2SHA1_HEADER = <openssl/sha.h> 2SHA1_HEADER = <openssl/sha.h>
3CACHE_ROOT = /var/cache/cgit 3CACHE_ROOT = /var/cache/cgit
4CGIT_CONFIG = /etc/cgitrc 4CGIT_CONFIG = /etc/cgitrc
5CGIT_SCRIPT_NAME = cgit.cgi 5CGIT_SCRIPT_NAME = cgit.cgi
6 6
7# 7#
8# Let the user override the above settings. 8# Let the user override the above settings.
9# 9#
10-include cgit.conf 10-include cgit.conf
11 11
12 12
13CGIT_VERSION = 0.5 13CGIT_VERSION = 0.5
14 14
15all: cgit 15all: cgit
16 16
17VERSION: 17VERSION:
18 sh gen-version.sh 18 sh gen-version.sh
19 19
20-include VERSION 20-include VERSION
21 21
22 22
23EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 23EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
24OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 24OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
25 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ 25 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
26 ui-snapshot.o ui-blob.o 26 ui-snapshot.o ui-blob.o ui-tag.o
27 27
28CFLAGS += -Wall 28CFLAGS += -Wall
29 29
30ifdef DEBUG 30ifdef DEBUG
31 CFLAGS += -g 31 CFLAGS += -g
32endif 32endif
33 33
34CFLAGS += -Igit 34CFLAGS += -Igit
35CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 35CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
36CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 36CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
37CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 37CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
38CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 38CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
39 39
40 40
41# 41#
42# If make is run on a nongit platform, get the git sources as a tarball. 42# If make is run on a nongit platform, get the git sources as a tarball.
43# 43#
44GITVER = $(shell git version 2>/dev/null || echo nogit) 44GITVER = $(shell git version 2>/dev/null || echo nogit)
45ifeq ($(GITVER),nogit) 45ifeq ($(GITVER),nogit)
46GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2 46GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2
47INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git) 47INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git)
48else 48else
49INITGIT = ./submodules.sh -i 49INITGIT = ./submodules.sh -i
50endif 50endif
51 51
52 52
53cgit: cgit.c cgit.h VERSION $(OBJECTS) 53cgit: cgit.c cgit.h VERSION $(OBJECTS)
54 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 54 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
55 55
56$(OBJECTS): cgit.h git/libgit.a 56$(OBJECTS): cgit.h git/libgit.a
57 57
58git/libgit.a: 58git/libgit.a:
59 $(INITGIT) 59 $(INITGIT)
60 $(MAKE) -C git 60 $(MAKE) -C git
61 61
62# 62#
63# phony targets 63# phony targets
64# 64#
65install: all clean-cache 65install: all clean-cache
66 mkdir -p $(prefix) 66 mkdir -p $(prefix)
67 install cgit $(prefix)/$(CGIT_SCRIPT_NAME) 67 install cgit $(prefix)/$(CGIT_SCRIPT_NAME)
68 install cgit.css $(prefix)/cgit.css 68 install cgit.css $(prefix)/cgit.css
69 69
70clean-cgit: 70clean-cgit:
71 rm -f cgit VERSION *.o 71 rm -f cgit VERSION *.o
72 72
73distclean-cgit: clean-cgit 73distclean-cgit: clean-cgit
74 git clean -d -x 74 git clean -d -x
75 75
76clean-sub: 76clean-sub:
77 $(MAKE) -C git clean 77 $(MAKE) -C git clean
78 78
79distclean-sub: clean-sub 79distclean-sub: clean-sub
80 $(shell cd git && git clean -d -x) 80 $(shell cd git && git clean -d -x)
81 81
82clean-cache: 82clean-cache:
83 rm -rf $(CACHE_ROOT)/* 83 rm -rf $(CACHE_ROOT)/*
84 84
85clean: clean-cgit clean-sub 85clean: clean-cgit clean-sub
86 86
87distclean: distclean-cgit distclean-sub 87distclean: distclean-cgit distclean-sub
88 88
89version: clean-cgit 89version: clean-cgit
90 ./gen-version.sh 90 ./gen-version.sh
91 make 91 make
92 92
93.PHONY: all install clean clean-cgit clean-sub clean-cache \ 93.PHONY: all install clean clean-cgit clean-sub clean-cache \
94 distclean distclean-cgit distclean-sub release version 94 distclean distclean-cgit distclean-sub release version
diff --git a/cgit.c b/cgit.c
index 1281bfa..4b91829 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,251 +1,254 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
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 cgit_prepare_cache(struct cacheitem *item) 11static int cgit_prepare_cache(struct cacheitem *item)
12{ 12{
13 if (!cgit_repo && cgit_query_repo) { 13 if (!cgit_repo && cgit_query_repo) {
14 char *title = fmt("%s - %s", cgit_root_title, "Bad request"); 14 char *title = fmt("%s - %s", cgit_root_title, "Bad request");
15 cgit_print_docstart(title, item); 15 cgit_print_docstart(title, item);
16 cgit_print_pageheader(title, 0); 16 cgit_print_pageheader(title, 0);
17 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo)); 17 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo));
18 cgit_print_docend(); 18 cgit_print_docend();
19 return 0; 19 return 0;
20 } 20 }
21 21
22 if (!cgit_repo) { 22 if (!cgit_repo) {
23 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root)); 23 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root));
24 item->ttl = cgit_cache_root_ttl; 24 item->ttl = cgit_cache_root_ttl;
25 return 1; 25 return 1;
26 } 26 }
27 27
28 if (!cgit_cmd) { 28 if (!cgit_cmd) {
29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root, 29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
30 cache_safe_filename(cgit_repo->url), 30 cache_safe_filename(cgit_repo->url),
31 cache_safe_filename(cgit_querystring))); 31 cache_safe_filename(cgit_querystring)));
32 item->ttl = cgit_cache_repo_ttl; 32 item->ttl = cgit_cache_repo_ttl;
33 } else { 33 } else {
34 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root, 34 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
35 cache_safe_filename(cgit_repo->url), 35 cache_safe_filename(cgit_repo->url),
36 cgit_query_page, 36 cgit_query_page,
37 cache_safe_filename(cgit_querystring))); 37 cache_safe_filename(cgit_querystring)));
38 if (cgit_query_has_symref) 38 if (cgit_query_has_symref)
39 item->ttl = cgit_cache_dynamic_ttl; 39 item->ttl = cgit_cache_dynamic_ttl;
40 else if (cgit_query_has_sha1) 40 else if (cgit_query_has_sha1)
41 item->ttl = cgit_cache_static_ttl; 41 item->ttl = cgit_cache_static_ttl;
42 else 42 else
43 item->ttl = cgit_cache_repo_ttl; 43 item->ttl = cgit_cache_repo_ttl;
44 } 44 }
45 return 1; 45 return 1;
46} 46}
47 47
48static void cgit_print_repo_page(struct cacheitem *item) 48static void cgit_print_repo_page(struct cacheitem *item)
49{ 49{
50 char *title; 50 char *title;
51 int show_search; 51 int show_search;
52 52
53 if (!cgit_query_head) 53 if (!cgit_query_head)
54 cgit_query_head = cgit_repo->defbranch; 54 cgit_query_head = cgit_repo->defbranch;
55 55
56 if (chdir(cgit_repo->path)) { 56 if (chdir(cgit_repo->path)) {
57 title = fmt("%s - %s", cgit_root_title, "Bad request"); 57 title = fmt("%s - %s", cgit_root_title, "Bad request");
58 cgit_print_docstart(title, item); 58 cgit_print_docstart(title, item);
59 cgit_print_pageheader(title, 0); 59 cgit_print_pageheader(title, 0);
60 cgit_print_error(fmt("Unable to scan repository: %s", 60 cgit_print_error(fmt("Unable to scan repository: %s",
61 strerror(errno))); 61 strerror(errno)));
62 cgit_print_docend(); 62 cgit_print_docend();
63 return; 63 return;
64 } 64 }
65 65
66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
67 show_search = 0; 67 show_search = 0;
68 setenv("GIT_DIR", cgit_repo->path, 1); 68 setenv("GIT_DIR", cgit_repo->path, 1);
69 69
70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
71 cgit_print_snapshot(item, cgit_query_sha1, "zip", 71 cgit_print_snapshot(item, cgit_query_sha1, "zip",
72 cgit_repo->url, cgit_query_name); 72 cgit_repo->url, cgit_query_name);
73 return; 73 return;
74 } 74 }
75 75
76 if (cgit_cmd == CMD_BLOB) { 76 if (cgit_cmd == CMD_BLOB) {
77 cgit_print_blob(item, cgit_query_sha1, cgit_query_path); 77 cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
78 return; 78 return;
79 } 79 }
80 80
81 show_search = (cgit_cmd == CMD_LOG); 81 show_search = (cgit_cmd == CMD_LOG);
82 cgit_print_docstart(title, item); 82 cgit_print_docstart(title, item);
83 if (!cgit_cmd) { 83 if (!cgit_cmd) {
84 cgit_print_pageheader("summary", show_search); 84 cgit_print_pageheader("summary", show_search);
85 cgit_print_summary(); 85 cgit_print_summary();
86 cgit_print_docend(); 86 cgit_print_docend();
87 return; 87 return;
88 } 88 }
89 89
90 cgit_print_pageheader(cgit_query_page, show_search); 90 cgit_print_pageheader(cgit_query_page, show_search);
91 91
92 switch(cgit_cmd) { 92 switch(cgit_cmd) {
93 case CMD_LOG: 93 case CMD_LOG:
94 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 94 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
95 cgit_max_commit_count, cgit_query_search, 95 cgit_max_commit_count, cgit_query_search,
96 cgit_query_path, 1); 96 cgit_query_path, 1);
97 break; 97 break;
98 case CMD_TREE: 98 case CMD_TREE:
99 cgit_print_tree(cgit_query_sha1, cgit_query_path); 99 cgit_print_tree(cgit_query_sha1, cgit_query_path);
100 break; 100 break;
101 case CMD_COMMIT: 101 case CMD_COMMIT:
102 cgit_print_commit(cgit_query_sha1); 102 cgit_print_commit(cgit_query_sha1);
103 break; 103 break;
104 case CMD_TAG:
105 cgit_print_tag(cgit_query_sha1);
106 break;
104 case CMD_DIFF: 107 case CMD_DIFF:
105 cgit_print_diff(cgit_query_sha1, cgit_query_sha2); 108 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
106 break; 109 break;
107 default: 110 default:
108 cgit_print_error("Invalid request"); 111 cgit_print_error("Invalid request");
109 } 112 }
110 cgit_print_docend(); 113 cgit_print_docend();
111} 114}
112 115
113static void cgit_fill_cache(struct cacheitem *item, int use_cache) 116static void cgit_fill_cache(struct cacheitem *item, int use_cache)
114{ 117{
115 static char buf[PATH_MAX]; 118 static char buf[PATH_MAX];
116 int stdout2; 119 int stdout2;
117 120
118 getcwd(buf, sizeof(buf)); 121 getcwd(buf, sizeof(buf));
119 item->st.st_mtime = time(NULL); 122 item->st.st_mtime = time(NULL);
120 123
121 if (use_cache) { 124 if (use_cache) {
122 stdout2 = chk_positive(dup(STDOUT_FILENO), 125 stdout2 = chk_positive(dup(STDOUT_FILENO),
123 "Preserving STDOUT"); 126 "Preserving STDOUT");
124 chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); 127 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
125 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); 128 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
126 } 129 }
127 130
128 if (cgit_repo) 131 if (cgit_repo)
129 cgit_print_repo_page(item); 132 cgit_print_repo_page(item);
130 else 133 else
131 cgit_print_repolist(item); 134 cgit_print_repolist(item);
132 135
133 if (use_cache) { 136 if (use_cache) {
134 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT"); 137 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
135 chk_positive(dup2(stdout2, STDOUT_FILENO), 138 chk_positive(dup2(stdout2, STDOUT_FILENO),
136 "Restoring original STDOUT"); 139 "Restoring original STDOUT");
137 chk_zero(close(stdout2), "Closing temporary STDOUT"); 140 chk_zero(close(stdout2), "Closing temporary STDOUT");
138 } 141 }
139 142
140 chdir(buf); 143 chdir(buf);
141} 144}
142 145
143static void cgit_check_cache(struct cacheitem *item) 146static void cgit_check_cache(struct cacheitem *item)
144{ 147{
145 int i = 0; 148 int i = 0;
146 149
147 top: 150 top:
148 if (++i > cgit_max_lock_attempts) { 151 if (++i > cgit_max_lock_attempts) {
149 die("cgit_refresh_cache: unable to lock %s: %s", 152 die("cgit_refresh_cache: unable to lock %s: %s",
150 item->name, strerror(errno)); 153 item->name, strerror(errno));
151 } 154 }
152 if (!cache_exist(item)) { 155 if (!cache_exist(item)) {
153 if (!cache_lock(item)) { 156 if (!cache_lock(item)) {
154 sleep(1); 157 sleep(1);
155 goto top; 158 goto top;
156 } 159 }
157 if (!cache_exist(item)) { 160 if (!cache_exist(item)) {
158 cgit_fill_cache(item, 1); 161 cgit_fill_cache(item, 1);
159 cache_unlock(item); 162 cache_unlock(item);
160 } else { 163 } else {
161 cache_cancel_lock(item); 164 cache_cancel_lock(item);
162 } 165 }
163 } else if (cache_expired(item) && cache_lock(item)) { 166 } else if (cache_expired(item) && cache_lock(item)) {
164 if (cache_expired(item)) { 167 if (cache_expired(item)) {
165 cgit_fill_cache(item, 1); 168 cgit_fill_cache(item, 1);
166 cache_unlock(item); 169 cache_unlock(item);
167 } else { 170 } else {
168 cache_cancel_lock(item); 171 cache_cancel_lock(item);
169 } 172 }
170 } 173 }
171} 174}
172 175
173static void cgit_print_cache(struct cacheitem *item) 176static void cgit_print_cache(struct cacheitem *item)
174{ 177{
175 static char buf[4096]; 178 static char buf[4096];
176 ssize_t i; 179 ssize_t i;
177 180
178 int fd = open(item->name, O_RDONLY); 181 int fd = open(item->name, O_RDONLY);
179 if (fd<0) 182 if (fd<0)
180 die("Unable to open cached file %s", item->name); 183 die("Unable to open cached file %s", item->name);
181 184
182 while((i=read(fd, buf, sizeof(buf))) > 0) 185 while((i=read(fd, buf, sizeof(buf))) > 0)
183 write(STDOUT_FILENO, buf, i); 186 write(STDOUT_FILENO, buf, i);
184 187
185 close(fd); 188 close(fd);
186} 189}
187 190
188static void cgit_parse_args(int argc, const char **argv) 191static void cgit_parse_args(int argc, const char **argv)
189{ 192{
190 int i; 193 int i;
191 194
192 for (i = 1; i < argc; i++) { 195 for (i = 1; i < argc; i++) {
193 if (!strncmp(argv[i], "--cache=", 8)) { 196 if (!strncmp(argv[i], "--cache=", 8)) {
194 cgit_cache_root = xstrdup(argv[i]+8); 197 cgit_cache_root = xstrdup(argv[i]+8);
195 } 198 }
196 if (!strcmp(argv[i], "--nocache")) { 199 if (!strcmp(argv[i], "--nocache")) {
197 cgit_nocache = 1; 200 cgit_nocache = 1;
198 } 201 }
199 if (!strncmp(argv[i], "--query=", 8)) { 202 if (!strncmp(argv[i], "--query=", 8)) {
200 cgit_querystring = xstrdup(argv[i]+8); 203 cgit_querystring = xstrdup(argv[i]+8);
201 } 204 }
202 if (!strncmp(argv[i], "--repo=", 7)) { 205 if (!strncmp(argv[i], "--repo=", 7)) {
203 cgit_query_repo = xstrdup(argv[i]+7); 206 cgit_query_repo = xstrdup(argv[i]+7);
204 } 207 }
205 if (!strncmp(argv[i], "--page=", 7)) { 208 if (!strncmp(argv[i], "--page=", 7)) {
206 cgit_query_page = xstrdup(argv[i]+7); 209 cgit_query_page = xstrdup(argv[i]+7);
207 } 210 }
208 if (!strncmp(argv[i], "--head=", 7)) { 211 if (!strncmp(argv[i], "--head=", 7)) {
209 cgit_query_head = xstrdup(argv[i]+7); 212 cgit_query_head = xstrdup(argv[i]+7);
210 cgit_query_has_symref = 1; 213 cgit_query_has_symref = 1;
211 } 214 }
212 if (!strncmp(argv[i], "--sha1=", 7)) { 215 if (!strncmp(argv[i], "--sha1=", 7)) {
213 cgit_query_sha1 = xstrdup(argv[i]+7); 216 cgit_query_sha1 = xstrdup(argv[i]+7);
214 cgit_query_has_sha1 = 1; 217 cgit_query_has_sha1 = 1;
215 } 218 }
216 if (!strncmp(argv[i], "--ofs=", 6)) { 219 if (!strncmp(argv[i], "--ofs=", 6)) {
217 cgit_query_ofs = atoi(argv[i]+6); 220 cgit_query_ofs = atoi(argv[i]+6);
218 } 221 }
219 } 222 }
220} 223}
221 224
222int main(int argc, const char **argv) 225int main(int argc, const char **argv)
223{ 226{
224 struct cacheitem item; 227 struct cacheitem item;
225 const char *cgit_config_env = getenv("CGIT_CONFIG"); 228 const char *cgit_config_env = getenv("CGIT_CONFIG");
226 229
227 htmlfd = STDOUT_FILENO; 230 htmlfd = STDOUT_FILENO;
228 item.st.st_mtime = time(NULL); 231 item.st.st_mtime = time(NULL);
229 cgit_repolist.length = 0; 232 cgit_repolist.length = 0;
230 cgit_repolist.count = 0; 233 cgit_repolist.count = 0;
231 cgit_repolist.repos = NULL; 234 cgit_repolist.repos = NULL;
232 235
233 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 236 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
234 cgit_global_config_cb); 237 cgit_global_config_cb);
235 cgit_repo = NULL; 238 cgit_repo = NULL;
236 if (getenv("SCRIPT_NAME")) 239 if (getenv("SCRIPT_NAME"))
237 cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); 240 cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
238 if (getenv("QUERY_STRING")) 241 if (getenv("QUERY_STRING"))
239 cgit_querystring = xstrdup(getenv("QUERY_STRING")); 242 cgit_querystring = xstrdup(getenv("QUERY_STRING"));
240 cgit_parse_args(argc, argv); 243 cgit_parse_args(argc, argv);
241 cgit_parse_query(cgit_querystring, cgit_querystring_cb); 244 cgit_parse_query(cgit_querystring, cgit_querystring_cb);
242 if (!cgit_prepare_cache(&item)) 245 if (!cgit_prepare_cache(&item))
243 return 0; 246 return 0;
244 if (cgit_nocache) { 247 if (cgit_nocache) {
245 cgit_fill_cache(&item, 0); 248 cgit_fill_cache(&item, 0);
246 } else { 249 } else {
247 cgit_check_cache(&item); 250 cgit_check_cache(&item);
248 cgit_print_cache(&item); 251 cgit_print_cache(&item);
249 } 252 }
250 return 0; 253 return 0;
251} 254}
diff --git a/cgit.h b/cgit.h
index 2ff5340..610a16d 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,236 +1,239 @@
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_BLOB 5 28#define CMD_BLOB 5
29#define CMD_SNAPSHOT 6 29#define CMD_SNAPSHOT 6
30 30#define CMD_TAG 7
31 31
32/* 32/*
33 * Dateformats used on misc. pages 33 * Dateformats used on misc. pages
34 */ 34 */
35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
36#define FMT_SHORTDATE "%Y-%m-%d" 36#define FMT_SHORTDATE "%Y-%m-%d"
37 37
38 38
39/* 39/*
40 * Limits used for relative dates 40 * Limits used for relative dates
41 */ 41 */
42#define TM_MIN 60 42#define TM_MIN 60
43#define TM_HOUR (TM_MIN * 60) 43#define TM_HOUR (TM_MIN * 60)
44#define TM_DAY (TM_HOUR * 24) 44#define TM_DAY (TM_HOUR * 24)
45#define TM_WEEK (TM_DAY * 7) 45#define TM_WEEK (TM_DAY * 7)
46#define TM_YEAR (TM_DAY * 365) 46#define TM_YEAR (TM_DAY * 365)
47#define TM_MONTH (TM_YEAR / 12.0) 47#define TM_MONTH (TM_YEAR / 12.0)
48 48
49 49
50typedef void (*configfn)(const char *name, const char *value); 50typedef void (*configfn)(const char *name, const char *value);
51typedef void (*filepair_fn)(struct diff_filepair *pair); 51typedef void (*filepair_fn)(struct diff_filepair *pair);
52typedef void (*linediff_fn)(char *line, int len); 52typedef void (*linediff_fn)(char *line, int len);
53 53
54struct cacheitem { 54struct cacheitem {
55 char *name; 55 char *name;
56 struct stat st; 56 struct stat st;
57 int ttl; 57 int ttl;
58 int fd; 58 int fd;
59}; 59};
60 60
61struct repoinfo { 61struct repoinfo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *group; 68 char *group;
69 char *module_link; 69 char *module_link;
70 char *readme; 70 char *readme;
71 int snapshots; 71 int snapshots;
72 int enable_log_filecount; 72 int enable_log_filecount;
73 int enable_log_linecount; 73 int enable_log_linecount;
74}; 74};
75 75
76struct repolist { 76struct repolist {
77 int length; 77 int length;
78 int count; 78 int count;
79 struct repoinfo *repos; 79 struct repoinfo *repos;
80}; 80};
81 81
82struct commitinfo { 82struct commitinfo {
83 struct commit *commit; 83 struct commit *commit;
84 char *author; 84 char *author;
85 char *author_email; 85 char *author_email;
86 unsigned long author_date; 86 unsigned long author_date;
87 char *committer; 87 char *committer;
88 char *committer_email; 88 char *committer_email;
89 unsigned long committer_date; 89 unsigned long committer_date;
90 char *subject; 90 char *subject;
91 char *msg; 91 char *msg;
92}; 92};
93 93
94struct taginfo { 94struct taginfo {
95 char *tagger; 95 char *tagger;
96 char *tagger_email; 96 char *tagger_email;
97 int tagger_date; 97 int tagger_date;
98 char *msg; 98 char *msg;
99}; 99};
100 100
101extern const char *cgit_version; 101extern const char *cgit_version;
102 102
103extern struct repolist cgit_repolist; 103extern struct repolist cgit_repolist;
104extern struct repoinfo *cgit_repo; 104extern struct repoinfo *cgit_repo;
105extern int cgit_cmd; 105extern int cgit_cmd;
106 106
107extern char *cgit_root_title; 107extern char *cgit_root_title;
108extern char *cgit_css; 108extern char *cgit_css;
109extern char *cgit_logo; 109extern char *cgit_logo;
110extern char *cgit_index_header; 110extern char *cgit_index_header;
111extern char *cgit_logo_link; 111extern char *cgit_logo_link;
112extern char *cgit_module_link; 112extern char *cgit_module_link;
113extern char *cgit_agefile; 113extern char *cgit_agefile;
114extern char *cgit_virtual_root; 114extern char *cgit_virtual_root;
115extern char *cgit_script_name; 115extern char *cgit_script_name;
116extern char *cgit_cache_root; 116extern char *cgit_cache_root;
117extern char *cgit_repo_group; 117extern char *cgit_repo_group;
118 118
119extern int cgit_nocache; 119extern int cgit_nocache;
120extern int cgit_snapshots; 120extern int cgit_snapshots;
121extern int cgit_enable_index_links; 121extern int cgit_enable_index_links;
122extern int cgit_enable_log_filecount; 122extern int cgit_enable_log_filecount;
123extern int cgit_enable_log_linecount; 123extern int cgit_enable_log_linecount;
124extern int cgit_max_lock_attempts; 124extern int cgit_max_lock_attempts;
125extern int cgit_cache_root_ttl; 125extern int cgit_cache_root_ttl;
126extern int cgit_cache_repo_ttl; 126extern int cgit_cache_repo_ttl;
127extern int cgit_cache_dynamic_ttl; 127extern int cgit_cache_dynamic_ttl;
128extern int cgit_cache_static_ttl; 128extern int cgit_cache_static_ttl;
129extern int cgit_cache_max_create_time; 129extern int cgit_cache_max_create_time;
130extern int cgit_summary_log; 130extern int cgit_summary_log;
131 131
132extern int cgit_max_msg_len; 132extern int cgit_max_msg_len;
133extern int cgit_max_repodesc_len; 133extern int cgit_max_repodesc_len;
134extern int cgit_max_commit_count; 134extern int cgit_max_commit_count;
135 135
136extern int cgit_query_has_symref; 136extern int cgit_query_has_symref;
137extern int cgit_query_has_sha1; 137extern int cgit_query_has_sha1;
138 138
139extern char *cgit_querystring; 139extern char *cgit_querystring;
140extern char *cgit_query_repo; 140extern char *cgit_query_repo;
141extern char *cgit_query_page; 141extern char *cgit_query_page;
142extern char *cgit_query_search; 142extern char *cgit_query_search;
143extern char *cgit_query_head; 143extern char *cgit_query_head;
144extern char *cgit_query_sha1; 144extern char *cgit_query_sha1;
145extern char *cgit_query_sha2; 145extern char *cgit_query_sha2;
146extern char *cgit_query_path; 146extern char *cgit_query_path;
147extern char *cgit_query_name; 147extern char *cgit_query_name;
148extern int cgit_query_ofs; 148extern int cgit_query_ofs;
149 149
150extern int htmlfd; 150extern int htmlfd;
151 151
152extern int cgit_get_cmd_index(const char *cmd); 152extern int cgit_get_cmd_index(const char *cmd);
153extern struct repoinfo *cgit_get_repoinfo(const char *url); 153extern struct repoinfo *cgit_get_repoinfo(const char *url);
154extern void cgit_global_config_cb(const char *name, const char *value); 154extern void cgit_global_config_cb(const char *name, const char *value);
155extern void cgit_repo_config_cb(const char *name, const char *value); 155extern void cgit_repo_config_cb(const char *name, const char *value);
156extern void cgit_querystring_cb(const char *name, const char *value); 156extern void cgit_querystring_cb(const char *name, const char *value);
157 157
158extern int chk_zero(int result, char *msg); 158extern int chk_zero(int result, char *msg);
159extern int chk_positive(int result, char *msg); 159extern int chk_positive(int result, char *msg);
160 160
161extern int hextoint(char c); 161extern int hextoint(char c);
162extern char *trim_end(const char *str, char c); 162extern char *trim_end(const char *str, char c);
163 163
164extern void *cgit_free_commitinfo(struct commitinfo *info); 164extern void *cgit_free_commitinfo(struct commitinfo *info);
165 165
166extern int cgit_diff_files(const unsigned char *old_sha1, 166extern int cgit_diff_files(const unsigned char *old_sha1,
167 const unsigned char *new_sha1, 167 const unsigned char *new_sha1,
168 linediff_fn fn); 168 linediff_fn fn);
169 169
170extern void cgit_diff_tree(const unsigned char *old_sha1, 170extern void cgit_diff_tree(const unsigned char *old_sha1,
171 const unsigned char *new_sha1, 171 const unsigned char *new_sha1,
172 filepair_fn fn); 172 filepair_fn fn);
173 173
174extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 174extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
175 175
176extern char *fmt(const char *format,...); 176extern char *fmt(const char *format,...);
177 177
178extern void html(const char *txt); 178extern void html(const char *txt);
179extern void htmlf(const char *format,...); 179extern void htmlf(const char *format,...);
180extern void html_txt(char *txt); 180extern void html_txt(char *txt);
181extern void html_ntxt(int len, char *txt); 181extern void html_ntxt(int len, char *txt);
182extern void html_attr(char *txt); 182extern void html_attr(char *txt);
183extern void html_hidden(char *name, char *value); 183extern void html_hidden(char *name, char *value);
184extern void html_link_open(char *url, char *title, char *class); 184extern void html_link_open(char *url, char *title, char *class);
185extern void html_link_close(void); 185extern void html_link_close(void);
186extern void html_filemode(unsigned short mode); 186extern void html_filemode(unsigned short mode);
187extern int html_include(const char *filename); 187extern int html_include(const char *filename);
188 188
189extern int cgit_read_config(const char *filename, configfn fn); 189extern int cgit_read_config(const char *filename, configfn fn);
190extern int cgit_parse_query(char *txt, configfn fn); 190extern int cgit_parse_query(char *txt, configfn fn);
191extern struct commitinfo *cgit_parse_commit(struct commit *commit); 191extern struct commitinfo *cgit_parse_commit(struct commit *commit);
192extern struct taginfo *cgit_parse_tag(struct tag *tag); 192extern struct taginfo *cgit_parse_tag(struct tag *tag);
193extern void cgit_parse_url(const char *url); 193extern void cgit_parse_url(const char *url);
194 194
195extern char *cache_safe_filename(const char *unsafe); 195extern char *cache_safe_filename(const char *unsafe);
196extern int cache_lock(struct cacheitem *item); 196extern int cache_lock(struct cacheitem *item);
197extern int cache_unlock(struct cacheitem *item); 197extern int cache_unlock(struct cacheitem *item);
198extern int cache_cancel_lock(struct cacheitem *item); 198extern int cache_cancel_lock(struct cacheitem *item);
199extern int cache_exist(struct cacheitem *item); 199extern int cache_exist(struct cacheitem *item);
200extern int cache_expired(struct cacheitem *item); 200extern int cache_expired(struct cacheitem *item);
201 201
202extern char *cgit_repourl(const char *reponame); 202extern char *cgit_repourl(const char *reponame);
203extern char *cgit_pageurl(const char *reponame, const char *pagename, 203extern char *cgit_pageurl(const char *reponame, const char *pagename,
204 const char *query); 204 const char *query);
205 205
206extern void cgit_tree_link(char *name, char *title, char *class, char *head, 206extern void cgit_tree_link(char *name, char *title, char *class, char *head,
207 char *rev, char *path); 207 char *rev, char *path);
208extern void cgit_log_link(char *name, char *title, char *class, char *head, 208extern void cgit_log_link(char *name, char *title, char *class, char *head,
209 char *rev, char *path, int ofs); 209 char *rev, char *path, int ofs);
210extern void cgit_commit_link(char *name, char *title, char *class, char *head, 210extern void cgit_commit_link(char *name, char *title, char *class, char *head,
211 char *rev); 211 char *rev);
212extern void cgit_diff_link(char *name, char *title, char *class, char *head, 212extern void cgit_diff_link(char *name, char *title, char *class, char *head,
213 char *new_rev, char *old_rev, char *path); 213 char *new_rev, char *old_rev, char *path);
214 214
215extern void cgit_object_link(struct object *obj);
216
215extern void cgit_print_error(char *msg); 217extern void cgit_print_error(char *msg);
216extern void cgit_print_date(time_t secs, char *format); 218extern void cgit_print_date(time_t secs, char *format);
217extern void cgit_print_age(time_t t, time_t max_relative, char *format); 219extern void cgit_print_age(time_t t, time_t max_relative, char *format);
218extern void cgit_print_docstart(char *title, struct cacheitem *item); 220extern void cgit_print_docstart(char *title, struct cacheitem *item);
219extern void cgit_print_docend(); 221extern void cgit_print_docend();
220extern void cgit_print_pageheader(char *title, int show_search); 222extern void cgit_print_pageheader(char *title, int show_search);
221extern void cgit_print_snapshot_start(const char *mimetype, 223extern void cgit_print_snapshot_start(const char *mimetype,
222 const char *filename, 224 const char *filename,
223 struct cacheitem *item); 225 struct cacheitem *item);
224 226
225extern void cgit_print_repolist(struct cacheitem *item); 227extern void cgit_print_repolist(struct cacheitem *item);
226extern void cgit_print_summary(); 228extern void cgit_print_summary();
227extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 229extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
228extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 230extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
229extern void cgit_print_tree(const char *rev, char *path); 231extern void cgit_print_tree(const char *rev, char *path);
230extern void cgit_print_commit(char *hex); 232extern void cgit_print_commit(char *hex);
233extern void cgit_print_tag(char *revname);
231extern void cgit_print_diff(const char *new_hex, const char *old_hex); 234extern void cgit_print_diff(const char *new_hex, const char *old_hex);
232extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 235extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
233 const char *format, const char *prefix, 236 const char *format, const char *prefix,
234 const char *filename); 237 const char *filename);
235 238
236#endif /* CGIT_H */ 239#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index 1a5b866..06693b0 100644
--- a/shared.c
+++ b/shared.c
@@ -1,405 +1,405 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback 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
11struct repolist cgit_repolist; 11struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13int cgit_cmd; 13int cgit_cmd;
14 14
15const char *cgit_version = CGIT_VERSION; 15const char *cgit_version = CGIT_VERSION;
16 16
17char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
18char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
19char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
20char *cgit_index_header = NULL; 20char *cgit_index_header = NULL;
21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
22char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; 22char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
23char *cgit_agefile = "info/web/last-modified"; 23char *cgit_agefile = "info/web/last-modified";
24char *cgit_virtual_root = NULL; 24char *cgit_virtual_root = NULL;
25char *cgit_script_name = CGIT_SCRIPT_NAME; 25char *cgit_script_name = CGIT_SCRIPT_NAME;
26char *cgit_cache_root = "/var/cache/cgit"; 26char *cgit_cache_root = "/var/cache/cgit";
27char *cgit_repo_group = NULL; 27char *cgit_repo_group = NULL;
28 28
29int cgit_nocache = 0; 29int cgit_nocache = 0;
30int cgit_snapshots = 0; 30int cgit_snapshots = 0;
31int cgit_enable_index_links = 0; 31int cgit_enable_index_links = 0;
32int cgit_enable_log_filecount = 0; 32int cgit_enable_log_filecount = 0;
33int cgit_enable_log_linecount = 0; 33int cgit_enable_log_linecount = 0;
34int cgit_max_lock_attempts = 5; 34int cgit_max_lock_attempts = 5;
35int cgit_cache_root_ttl = 5; 35int cgit_cache_root_ttl = 5;
36int cgit_cache_repo_ttl = 5; 36int cgit_cache_repo_ttl = 5;
37int cgit_cache_dynamic_ttl = 5; 37int cgit_cache_dynamic_ttl = 5;
38int cgit_cache_static_ttl = -1; 38int cgit_cache_static_ttl = -1;
39int cgit_cache_max_create_time = 5; 39int cgit_cache_max_create_time = 5;
40int cgit_summary_log = 0; 40int cgit_summary_log = 0;
41 41
42int cgit_max_msg_len = 60; 42int cgit_max_msg_len = 60;
43int cgit_max_repodesc_len = 60; 43int cgit_max_repodesc_len = 60;
44int cgit_max_commit_count = 50; 44int cgit_max_commit_count = 50;
45 45
46int cgit_query_has_symref = 0; 46int cgit_query_has_symref = 0;
47int cgit_query_has_sha1 = 0; 47int cgit_query_has_sha1 = 0;
48 48
49char *cgit_querystring = NULL; 49char *cgit_querystring = NULL;
50char *cgit_query_repo = NULL; 50char *cgit_query_repo = NULL;
51char *cgit_query_page = NULL; 51char *cgit_query_page = NULL;
52char *cgit_query_head = NULL; 52char *cgit_query_head = NULL;
53char *cgit_query_search = NULL; 53char *cgit_query_search = NULL;
54char *cgit_query_sha1 = NULL; 54char *cgit_query_sha1 = NULL;
55char *cgit_query_sha2 = NULL; 55char *cgit_query_sha2 = NULL;
56char *cgit_query_path = NULL; 56char *cgit_query_path = NULL;
57char *cgit_query_name = NULL; 57char *cgit_query_name = NULL;
58int cgit_query_ofs = 0; 58int cgit_query_ofs = 0;
59 59
60int htmlfd = 0; 60int htmlfd = 0;
61 61
62 62
63int cgit_get_cmd_index(const char *cmd) 63int cgit_get_cmd_index(const char *cmd)
64{ 64{
65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
66 "snapshot", NULL}; 66 "snapshot", "tag", NULL};
67 int i; 67 int i;
68 68
69 for(i = 0; cmds[i]; i++) 69 for(i = 0; cmds[i]; i++)
70 if (!strcmp(cmd, cmds[i])) 70 if (!strcmp(cmd, cmds[i]))
71 return i + 1; 71 return i + 1;
72 return 0; 72 return 0;
73} 73}
74 74
75int chk_zero(int result, char *msg) 75int chk_zero(int result, char *msg)
76{ 76{
77 if (result != 0) 77 if (result != 0)
78 die("%s: %s", msg, strerror(errno)); 78 die("%s: %s", msg, strerror(errno));
79 return result; 79 return result;
80} 80}
81 81
82int chk_positive(int result, char *msg) 82int chk_positive(int result, char *msg)
83{ 83{
84 if (result <= 0) 84 if (result <= 0)
85 die("%s: %s", msg, strerror(errno)); 85 die("%s: %s", msg, strerror(errno));
86 return result; 86 return result;
87} 87}
88 88
89struct repoinfo *add_repo(const char *url) 89struct repoinfo *add_repo(const char *url)
90{ 90{
91 struct repoinfo *ret; 91 struct repoinfo *ret;
92 92
93 if (++cgit_repolist.count > cgit_repolist.length) { 93 if (++cgit_repolist.count > cgit_repolist.length) {
94 if (cgit_repolist.length == 0) 94 if (cgit_repolist.length == 0)
95 cgit_repolist.length = 8; 95 cgit_repolist.length = 8;
96 else 96 else
97 cgit_repolist.length *= 2; 97 cgit_repolist.length *= 2;
98 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 98 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
99 cgit_repolist.length * 99 cgit_repolist.length *
100 sizeof(struct repoinfo)); 100 sizeof(struct repoinfo));
101 } 101 }
102 102
103 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 103 ret = &cgit_repolist.repos[cgit_repolist.count-1];
104 ret->url = xstrdup(url); 104 ret->url = xstrdup(url);
105 ret->name = ret->url; 105 ret->name = ret->url;
106 ret->path = NULL; 106 ret->path = NULL;
107 ret->desc = NULL; 107 ret->desc = NULL;
108 ret->owner = NULL; 108 ret->owner = NULL;
109 ret->group = cgit_repo_group; 109 ret->group = cgit_repo_group;
110 ret->defbranch = "master"; 110 ret->defbranch = "master";
111 ret->snapshots = cgit_snapshots; 111 ret->snapshots = cgit_snapshots;
112 ret->enable_log_filecount = cgit_enable_log_filecount; 112 ret->enable_log_filecount = cgit_enable_log_filecount;
113 ret->enable_log_linecount = cgit_enable_log_linecount; 113 ret->enable_log_linecount = cgit_enable_log_linecount;
114 ret->module_link = cgit_module_link; 114 ret->module_link = cgit_module_link;
115 ret->readme = NULL; 115 ret->readme = NULL;
116 return ret; 116 return ret;
117} 117}
118 118
119struct repoinfo *cgit_get_repoinfo(const char *url) 119struct repoinfo *cgit_get_repoinfo(const char *url)
120{ 120{
121 int i; 121 int i;
122 struct repoinfo *repo; 122 struct repoinfo *repo;
123 123
124 for (i=0; i<cgit_repolist.count; i++) { 124 for (i=0; i<cgit_repolist.count; i++) {
125 repo = &cgit_repolist.repos[i]; 125 repo = &cgit_repolist.repos[i];
126 if (!strcmp(repo->url, url)) 126 if (!strcmp(repo->url, url))
127 return repo; 127 return repo;
128 } 128 }
129 return NULL; 129 return NULL;
130} 130}
131 131
132void cgit_global_config_cb(const char *name, const char *value) 132void cgit_global_config_cb(const char *name, const char *value)
133{ 133{
134 if (!strcmp(name, "root-title")) 134 if (!strcmp(name, "root-title"))
135 cgit_root_title = xstrdup(value); 135 cgit_root_title = xstrdup(value);
136 else if (!strcmp(name, "css")) 136 else if (!strcmp(name, "css"))
137 cgit_css = xstrdup(value); 137 cgit_css = xstrdup(value);
138 else if (!strcmp(name, "logo")) 138 else if (!strcmp(name, "logo"))
139 cgit_logo = xstrdup(value); 139 cgit_logo = xstrdup(value);
140 else if (!strcmp(name, "index-header")) 140 else if (!strcmp(name, "index-header"))
141 cgit_index_header = xstrdup(value); 141 cgit_index_header = xstrdup(value);
142 else if (!strcmp(name, "logo-link")) 142 else if (!strcmp(name, "logo-link"))
143 cgit_logo_link = xstrdup(value); 143 cgit_logo_link = xstrdup(value);
144 else if (!strcmp(name, "module-link")) 144 else if (!strcmp(name, "module-link"))
145 cgit_module_link = xstrdup(value); 145 cgit_module_link = xstrdup(value);
146 else if (!strcmp(name, "virtual-root")) 146 else if (!strcmp(name, "virtual-root"))
147 cgit_virtual_root = xstrdup(value); 147 cgit_virtual_root = xstrdup(value);
148 else if (!strcmp(name, "nocache")) 148 else if (!strcmp(name, "nocache"))
149 cgit_nocache = atoi(value); 149 cgit_nocache = atoi(value);
150 else if (!strcmp(name, "snapshots")) 150 else if (!strcmp(name, "snapshots"))
151 cgit_snapshots = atoi(value); 151 cgit_snapshots = atoi(value);
152 else if (!strcmp(name, "enable-index-links")) 152 else if (!strcmp(name, "enable-index-links"))
153 cgit_enable_index_links = atoi(value); 153 cgit_enable_index_links = atoi(value);
154 else if (!strcmp(name, "enable-log-filecount")) 154 else if (!strcmp(name, "enable-log-filecount"))
155 cgit_enable_log_filecount = atoi(value); 155 cgit_enable_log_filecount = atoi(value);
156 else if (!strcmp(name, "enable-log-linecount")) 156 else if (!strcmp(name, "enable-log-linecount"))
157 cgit_enable_log_linecount = atoi(value); 157 cgit_enable_log_linecount = atoi(value);
158 else if (!strcmp(name, "cache-root")) 158 else if (!strcmp(name, "cache-root"))
159 cgit_cache_root = xstrdup(value); 159 cgit_cache_root = xstrdup(value);
160 else if (!strcmp(name, "cache-root-ttl")) 160 else if (!strcmp(name, "cache-root-ttl"))
161 cgit_cache_root_ttl = atoi(value); 161 cgit_cache_root_ttl = atoi(value);
162 else if (!strcmp(name, "cache-repo-ttl")) 162 else if (!strcmp(name, "cache-repo-ttl"))
163 cgit_cache_repo_ttl = atoi(value); 163 cgit_cache_repo_ttl = atoi(value);
164 else if (!strcmp(name, "cache-static-ttl")) 164 else if (!strcmp(name, "cache-static-ttl"))
165 cgit_cache_static_ttl = atoi(value); 165 cgit_cache_static_ttl = atoi(value);
166 else if (!strcmp(name, "cache-dynamic-ttl")) 166 else if (!strcmp(name, "cache-dynamic-ttl"))
167 cgit_cache_dynamic_ttl = atoi(value); 167 cgit_cache_dynamic_ttl = atoi(value);
168 else if (!strcmp(name, "max-message-length")) 168 else if (!strcmp(name, "max-message-length"))
169 cgit_max_msg_len = atoi(value); 169 cgit_max_msg_len = atoi(value);
170 else if (!strcmp(name, "max-repodesc-length")) 170 else if (!strcmp(name, "max-repodesc-length"))
171 cgit_max_repodesc_len = atoi(value); 171 cgit_max_repodesc_len = atoi(value);
172 else if (!strcmp(name, "max-commit-count")) 172 else if (!strcmp(name, "max-commit-count"))
173 cgit_max_commit_count = atoi(value); 173 cgit_max_commit_count = atoi(value);
174 else if (!strcmp(name, "summary-log")) 174 else if (!strcmp(name, "summary-log"))
175 cgit_summary_log = atoi(value); 175 cgit_summary_log = atoi(value);
176 else if (!strcmp(name, "agefile")) 176 else if (!strcmp(name, "agefile"))
177 cgit_agefile = xstrdup(value); 177 cgit_agefile = xstrdup(value);
178 else if (!strcmp(name, "repo.group")) 178 else if (!strcmp(name, "repo.group"))
179 cgit_repo_group = xstrdup(value); 179 cgit_repo_group = xstrdup(value);
180 else if (!strcmp(name, "repo.url")) 180 else if (!strcmp(name, "repo.url"))
181 cgit_repo = add_repo(value); 181 cgit_repo = add_repo(value);
182 else if (!strcmp(name, "repo.name")) 182 else if (!strcmp(name, "repo.name"))
183 cgit_repo->name = xstrdup(value); 183 cgit_repo->name = xstrdup(value);
184 else if (cgit_repo && !strcmp(name, "repo.path")) 184 else if (cgit_repo && !strcmp(name, "repo.path"))
185 cgit_repo->path = xstrdup(value); 185 cgit_repo->path = xstrdup(value);
186 else if (cgit_repo && !strcmp(name, "repo.desc")) 186 else if (cgit_repo && !strcmp(name, "repo.desc"))
187 cgit_repo->desc = xstrdup(value); 187 cgit_repo->desc = xstrdup(value);
188 else if (cgit_repo && !strcmp(name, "repo.owner")) 188 else if (cgit_repo && !strcmp(name, "repo.owner"))
189 cgit_repo->owner = xstrdup(value); 189 cgit_repo->owner = xstrdup(value);
190 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 190 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
191 cgit_repo->defbranch = xstrdup(value); 191 cgit_repo->defbranch = xstrdup(value);
192 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 192 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
193 cgit_repo->snapshots = cgit_snapshots * atoi(value); 193 cgit_repo->snapshots = cgit_snapshots * atoi(value);
194 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 194 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
195 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 195 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
196 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 196 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
197 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); 197 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
198 else if (cgit_repo && !strcmp(name, "repo.module-link")) 198 else if (cgit_repo && !strcmp(name, "repo.module-link"))
199 cgit_repo->module_link= xstrdup(value); 199 cgit_repo->module_link= xstrdup(value);
200 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { 200 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
201 if (*value == '/') 201 if (*value == '/')
202 cgit_repo->readme = xstrdup(value); 202 cgit_repo->readme = xstrdup(value);
203 else 203 else
204 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); 204 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
205 } else if (!strcmp(name, "include")) 205 } else if (!strcmp(name, "include"))
206 cgit_read_config(value, cgit_global_config_cb); 206 cgit_read_config(value, cgit_global_config_cb);
207} 207}
208 208
209void cgit_querystring_cb(const char *name, const char *value) 209void cgit_querystring_cb(const char *name, const char *value)
210{ 210{
211 if (!strcmp(name,"r")) { 211 if (!strcmp(name,"r")) {
212 cgit_query_repo = xstrdup(value); 212 cgit_query_repo = xstrdup(value);
213 cgit_repo = cgit_get_repoinfo(value); 213 cgit_repo = cgit_get_repoinfo(value);
214 } else if (!strcmp(name, "p")) { 214 } else if (!strcmp(name, "p")) {
215 cgit_query_page = xstrdup(value); 215 cgit_query_page = xstrdup(value);
216 cgit_cmd = cgit_get_cmd_index(value); 216 cgit_cmd = cgit_get_cmd_index(value);
217 } else if (!strcmp(name, "url")) { 217 } else if (!strcmp(name, "url")) {
218 cgit_parse_url(value); 218 cgit_parse_url(value);
219 } else if (!strcmp(name, "q")) { 219 } else if (!strcmp(name, "q")) {
220 cgit_query_search = xstrdup(value); 220 cgit_query_search = xstrdup(value);
221 } else if (!strcmp(name, "h")) { 221 } else if (!strcmp(name, "h")) {
222 cgit_query_head = xstrdup(value); 222 cgit_query_head = xstrdup(value);
223 cgit_query_has_symref = 1; 223 cgit_query_has_symref = 1;
224 } else if (!strcmp(name, "id")) { 224 } else if (!strcmp(name, "id")) {
225 cgit_query_sha1 = xstrdup(value); 225 cgit_query_sha1 = xstrdup(value);
226 cgit_query_has_sha1 = 1; 226 cgit_query_has_sha1 = 1;
227 } else if (!strcmp(name, "id2")) { 227 } else if (!strcmp(name, "id2")) {
228 cgit_query_sha2 = xstrdup(value); 228 cgit_query_sha2 = xstrdup(value);
229 cgit_query_has_sha1 = 1; 229 cgit_query_has_sha1 = 1;
230 } else if (!strcmp(name, "ofs")) { 230 } else if (!strcmp(name, "ofs")) {
231 cgit_query_ofs = atoi(value); 231 cgit_query_ofs = atoi(value);
232 } else if (!strcmp(name, "path")) { 232 } else if (!strcmp(name, "path")) {
233 cgit_query_path = trim_end(value, '/'); 233 cgit_query_path = trim_end(value, '/');
234 } else if (!strcmp(name, "name")) { 234 } else if (!strcmp(name, "name")) {
235 cgit_query_name = xstrdup(value); 235 cgit_query_name = xstrdup(value);
236 } 236 }
237} 237}
238 238
239void *cgit_free_commitinfo(struct commitinfo *info) 239void *cgit_free_commitinfo(struct commitinfo *info)
240{ 240{
241 free(info->author); 241 free(info->author);
242 free(info->author_email); 242 free(info->author_email);
243 free(info->committer); 243 free(info->committer);
244 free(info->committer_email); 244 free(info->committer_email);
245 free(info->subject); 245 free(info->subject);
246 free(info); 246 free(info);
247 return NULL; 247 return NULL;
248} 248}
249 249
250int hextoint(char c) 250int hextoint(char c)
251{ 251{
252 if (c >= 'a' && c <= 'f') 252 if (c >= 'a' && c <= 'f')
253 return 10 + c - 'a'; 253 return 10 + c - 'a';
254 else if (c >= 'A' && c <= 'F') 254 else if (c >= 'A' && c <= 'F')
255 return 10 + c - 'A'; 255 return 10 + c - 'A';
256 else if (c >= '0' && c <= '9') 256 else if (c >= '0' && c <= '9')
257 return c - '0'; 257 return c - '0';
258 else 258 else
259 return -1; 259 return -1;
260} 260}
261 261
262char *trim_end(const char *str, char c) 262char *trim_end(const char *str, char c)
263{ 263{
264 int len; 264 int len;
265 char *s, *t; 265 char *s, *t;
266 266
267 if (str == NULL) 267 if (str == NULL)
268 return NULL; 268 return NULL;
269 t = (char *)str; 269 t = (char *)str;
270 len = strlen(t); 270 len = strlen(t);
271 while(len > 0 && t[len - 1] == c) 271 while(len > 0 && t[len - 1] == c)
272 len--; 272 len--;
273 273
274 if (len == 0) 274 if (len == 0)
275 return NULL; 275 return NULL;
276 276
277 c = t[len]; 277 c = t[len];
278 t[len] = '\0'; 278 t[len] = '\0';
279 s = xstrdup(t); 279 s = xstrdup(t);
280 t[len] = c; 280 t[len] = c;
281 return s; 281 return s;
282} 282}
283 283
284void cgit_diff_tree_cb(struct diff_queue_struct *q, 284void cgit_diff_tree_cb(struct diff_queue_struct *q,
285 struct diff_options *options, void *data) 285 struct diff_options *options, void *data)
286{ 286{
287 int i; 287 int i;
288 288
289 for (i = 0; i < q->nr; i++) { 289 for (i = 0; i < q->nr; i++) {
290 if (q->queue[i]->status == 'U') 290 if (q->queue[i]->status == 'U')
291 continue; 291 continue;
292 ((filepair_fn)data)(q->queue[i]); 292 ((filepair_fn)data)(q->queue[i]);
293 } 293 }
294} 294}
295 295
296static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 296static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
297{ 297{
298 enum object_type type; 298 enum object_type type;
299 299
300 if (is_null_sha1(sha1)) { 300 if (is_null_sha1(sha1)) {
301 file->ptr = (char *)""; 301 file->ptr = (char *)"";
302 file->size = 0; 302 file->size = 0;
303 } else { 303 } else {
304 file->ptr = read_sha1_file(sha1, &type, &file->size); 304 file->ptr = read_sha1_file(sha1, &type, &file->size);
305 } 305 }
306 return 1; 306 return 1;
307} 307}
308 308
309/* 309/*
310 * Receive diff-buffers from xdiff and concatenate them as 310 * Receive diff-buffers from xdiff and concatenate them as
311 * needed across multiple callbacks. 311 * needed across multiple callbacks.
312 * 312 *
313 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 313 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
314 * ripped from git and modified to use globals instead of 314 * ripped from git and modified to use globals instead of
315 * a special callback-struct. 315 * a special callback-struct.
316 */ 316 */
317char *diffbuf = NULL; 317char *diffbuf = NULL;
318int buflen = 0; 318int buflen = 0;
319 319
320int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 320int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
321{ 321{
322 int i; 322 int i;
323 323
324 for (i = 0; i < nbuf; i++) { 324 for (i = 0; i < nbuf; i++) {
325 if (mb[i].ptr[mb[i].size-1] != '\n') { 325 if (mb[i].ptr[mb[i].size-1] != '\n') {
326 /* Incomplete line */ 326 /* Incomplete line */
327 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 327 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
328 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 328 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
329 buflen += mb[i].size; 329 buflen += mb[i].size;
330 continue; 330 continue;
331 } 331 }
332 332
333 /* we have a complete line */ 333 /* we have a complete line */
334 if (!diffbuf) { 334 if (!diffbuf) {
335 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 335 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
336 continue; 336 continue;
337 } 337 }
338 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 338 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
339 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 339 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
340 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 340 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
341 free(diffbuf); 341 free(diffbuf);
342 diffbuf = NULL; 342 diffbuf = NULL;
343 buflen = 0; 343 buflen = 0;
344 } 344 }
345 if (diffbuf) { 345 if (diffbuf) {
346 ((linediff_fn)priv)(diffbuf, buflen); 346 ((linediff_fn)priv)(diffbuf, buflen);
347 free(diffbuf); 347 free(diffbuf);
348 diffbuf = NULL; 348 diffbuf = NULL;
349 buflen = 0; 349 buflen = 0;
350 } 350 }
351 return 0; 351 return 0;
352} 352}
353 353
354int cgit_diff_files(const unsigned char *old_sha1, 354int cgit_diff_files(const unsigned char *old_sha1,
355 const unsigned char *new_sha1, 355 const unsigned char *new_sha1,
356 linediff_fn fn) 356 linediff_fn fn)
357{ 357{
358 mmfile_t file1, file2; 358 mmfile_t file1, file2;
359 xpparam_t diff_params; 359 xpparam_t diff_params;
360 xdemitconf_t emit_params; 360 xdemitconf_t emit_params;
361 xdemitcb_t emit_cb; 361 xdemitcb_t emit_cb;
362 362
363 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 363 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
364 return 1; 364 return 1;
365 365
366 diff_params.flags = XDF_NEED_MINIMAL; 366 diff_params.flags = XDF_NEED_MINIMAL;
367 emit_params.ctxlen = 3; 367 emit_params.ctxlen = 3;
368 emit_params.flags = XDL_EMIT_FUNCNAMES; 368 emit_params.flags = XDL_EMIT_FUNCNAMES;
369 emit_cb.outf = filediff_cb; 369 emit_cb.outf = filediff_cb;
370 emit_cb.priv = fn; 370 emit_cb.priv = fn;
371 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 371 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
372 return 0; 372 return 0;
373} 373}
374 374
375void cgit_diff_tree(const unsigned char *old_sha1, 375void cgit_diff_tree(const unsigned char *old_sha1,
376 const unsigned char *new_sha1, 376 const unsigned char *new_sha1,
377 filepair_fn fn) 377 filepair_fn fn)
378{ 378{
379 struct diff_options opt; 379 struct diff_options opt;
380 int ret; 380 int ret;
381 381
382 diff_setup(&opt); 382 diff_setup(&opt);
383 opt.output_format = DIFF_FORMAT_CALLBACK; 383 opt.output_format = DIFF_FORMAT_CALLBACK;
384 opt.detect_rename = 1; 384 opt.detect_rename = 1;
385 opt.recursive = 1; 385 opt.recursive = 1;
386 opt.format_callback = cgit_diff_tree_cb; 386 opt.format_callback = cgit_diff_tree_cb;
387 opt.format_callback_data = fn; 387 opt.format_callback_data = fn;
388 diff_setup_done(&opt); 388 diff_setup_done(&opt);
389 389
390 if (old_sha1 && !is_null_sha1(old_sha1)) 390 if (old_sha1 && !is_null_sha1(old_sha1))
391 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 391 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
392 else 392 else
393 ret = diff_root_tree_sha1(new_sha1, "", &opt); 393 ret = diff_root_tree_sha1(new_sha1, "", &opt);
394 diffcore_std(&opt); 394 diffcore_std(&opt);
395 diff_flush(&opt); 395 diff_flush(&opt);
396} 396}
397 397
398void cgit_diff_commit(struct commit *commit, filepair_fn fn) 398void cgit_diff_commit(struct commit *commit, filepair_fn fn)
399{ 399{
400 unsigned char *old_sha1 = NULL; 400 unsigned char *old_sha1 = NULL;
401 401
402 if (commit->parents) 402 if (commit->parents)
403 old_sha1 = commit->parents->item->object.sha1; 403 old_sha1 = commit->parents->item->object.sha1;
404 cgit_diff_tree(old_sha1, commit->object.sha1, fn); 404 cgit_diff_tree(old_sha1, commit->object.sha1, fn);
405} 405}
diff --git a/ui-shared.c b/ui-shared.c
index d4376ce..fd71c12 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,370 +1,394 @@
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&amp;p=%s&amp;%s", reponame, pagename, query); 72 return fmt("?r=%s&amp;p=%s&amp;%s", reponame, pagename, query);
73 else 73 else
74 return fmt("?r=%s&amp;p=%s", reponame, pagename); 74 return fmt("?r=%s&amp;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
90static char *repolink(char *title, char *class, char *page, char *head, 90static char *repolink(char *title, char *class, char *page, char *head,
91 char *path) 91 char *path)
92{ 92{
93 char *delim = "?"; 93 char *delim = "?";
94 94
95 html("<a"); 95 html("<a");
96 if (title) { 96 if (title) {
97 html(" title='"); 97 html(" title='");
98 html_attr(title); 98 html_attr(title);
99 html("'"); 99 html("'");
100 } 100 }
101 if (class) { 101 if (class) {
102 html(" class='"); 102 html(" class='");
103 html_attr(class); 103 html_attr(class);
104 html("'"); 104 html("'");
105 } 105 }
106 html(" href='"); 106 html(" href='");
107 if (cgit_virtual_root) { 107 if (cgit_virtual_root) {
108 html_attr(cgit_virtual_root); 108 html_attr(cgit_virtual_root);
109 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/') 109 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
110 html("/"); 110 html("/");
111 html_attr(cgit_repo->url); 111 html_attr(cgit_repo->url);
112 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 112 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
113 html("/"); 113 html("/");
114 if (page) { 114 if (page) {
115 html(page); 115 html(page);
116 html("/"); 116 html("/");
117 if (path) 117 if (path)
118 html_attr(path); 118 html_attr(path);
119 } 119 }
120 } else { 120 } else {
121 html(cgit_script_name); 121 html(cgit_script_name);
122 html("?url="); 122 html("?url=");
123 html_attr(cgit_repo->url); 123 html_attr(cgit_repo->url);
124 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 124 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
125 html("/"); 125 html("/");
126 if (page) { 126 if (page) {
127 html(page); 127 html(page);
128 html("/"); 128 html("/");
129 if (path) 129 if (path)
130 html_attr(path); 130 html_attr(path);
131 } 131 }
132 delim = "&amp;"; 132 delim = "&amp;";
133 } 133 }
134 if (head && strcmp(head, cgit_repo->defbranch)) { 134 if (head && strcmp(head, cgit_repo->defbranch)) {
135 html(delim); 135 html(delim);
136 html("h="); 136 html("h=");
137 html_attr(head); 137 html_attr(head);
138 delim = "&amp;"; 138 delim = "&amp;";
139 } 139 }
140 return fmt("%s", delim); 140 return fmt("%s", delim);
141} 141}
142 142
143static void reporevlink(char *page, char *name, char *title, char *class, 143static void reporevlink(char *page, char *name, char *title, char *class,
144 char *head, char *rev, char *path) 144 char *head, char *rev, char *path)
145{ 145{
146 char *delim; 146 char *delim;
147 147
148 delim = repolink(title, class, page, head, path); 148 delim = repolink(title, class, page, head, path);
149 if (rev && strcmp(rev, cgit_query_head)) { 149 if (rev && strcmp(rev, cgit_query_head)) {
150 html(delim); 150 html(delim);
151 html("id="); 151 html("id=");
152 html_attr(rev); 152 html_attr(rev);
153 } 153 }
154 html("'>"); 154 html("'>");
155 html_txt(name); 155 html_txt(name);
156 html("</a>"); 156 html("</a>");
157} 157}
158 158
159void cgit_tree_link(char *name, char *title, char *class, char *head, 159void cgit_tree_link(char *name, char *title, char *class, char *head,
160 char *rev, char *path) 160 char *rev, char *path)
161{ 161{
162 reporevlink("tree", name, title, class, head, rev, path); 162 reporevlink("tree", name, title, class, head, rev, path);
163} 163}
164 164
165void cgit_log_link(char *name, char *title, char *class, char *head, 165void cgit_log_link(char *name, char *title, char *class, char *head,
166 char *rev, char *path, int ofs) 166 char *rev, char *path, int ofs)
167{ 167{
168 char *delim; 168 char *delim;
169 169
170 delim = repolink(title, class, "log", head, path); 170 delim = repolink(title, class, "log", head, path);
171 if (rev && strcmp(rev, cgit_query_head)) { 171 if (rev && strcmp(rev, cgit_query_head)) {
172 html(delim); 172 html(delim);
173 html("id="); 173 html("id=");
174 html_attr(rev); 174 html_attr(rev);
175 delim = "&"; 175 delim = "&";
176 } 176 }
177 if (ofs > 0) { 177 if (ofs > 0) {
178 html(delim); 178 html(delim);
179 html("ofs="); 179 html("ofs=");
180 htmlf("%d", ofs); 180 htmlf("%d", ofs);
181 } 181 }
182 html("'>"); 182 html("'>");
183 html_txt(name); 183 html_txt(name);
184 html("</a>"); 184 html("</a>");
185} 185}
186 186
187void cgit_commit_link(char *name, char *title, char *class, char *head, 187void cgit_commit_link(char *name, char *title, char *class, char *head,
188 char *rev) 188 char *rev)
189{ 189{
190 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 190 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
191 name[cgit_max_msg_len] = '\0'; 191 name[cgit_max_msg_len] = '\0';
192 name[cgit_max_msg_len - 1] = '.'; 192 name[cgit_max_msg_len - 1] = '.';
193 name[cgit_max_msg_len - 2] = '.'; 193 name[cgit_max_msg_len - 2] = '.';
194 name[cgit_max_msg_len - 3] = '.'; 194 name[cgit_max_msg_len - 3] = '.';
195 } 195 }
196 reporevlink("commit", name, title, class, head, rev, NULL); 196 reporevlink("commit", name, title, class, head, rev, NULL);
197} 197}
198 198
199void cgit_diff_link(char *name, char *title, char *class, char *head, 199void cgit_diff_link(char *name, char *title, char *class, char *head,
200 char *new_rev, char *old_rev, char *path) 200 char *new_rev, char *old_rev, char *path)
201{ 201{
202 char *delim; 202 char *delim;
203 203
204 delim = repolink(title, class, "diff", head, path); 204 delim = repolink(title, class, "diff", head, path);
205 if (new_rev && strcmp(new_rev, cgit_query_head)) { 205 if (new_rev && strcmp(new_rev, cgit_query_head)) {
206 html(delim); 206 html(delim);
207 html("id="); 207 html("id=");
208 html_attr(new_rev); 208 html_attr(new_rev);
209 delim = "&amp;"; 209 delim = "&amp;";
210 } 210 }
211 if (old_rev) { 211 if (old_rev) {
212 html(delim); 212 html(delim);
213 html("id2="); 213 html("id2=");
214 html_attr(old_rev); 214 html_attr(old_rev);
215 } 215 }
216 html("'>"); 216 html("'>");
217 html_txt(name); 217 html_txt(name);
218 html("</a>"); 218 html("</a>");
219} 219}
220 220
221void cgit_object_link(struct object *obj)
222{
223 char *page, *arg, *url;
224
225 if (obj->type == OBJ_COMMIT) {
226 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
227 cgit_query_head, sha1_to_hex(obj->sha1));
228 return;
229 } else if (obj->type == OBJ_TREE) {
230 page = "tree";
231 arg = "id";
232 } else {
233 page = "blob";
234 arg = "id";
235 }
236
237 url = cgit_pageurl(cgit_query_repo, page,
238 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
239 html_link_open(url, NULL, NULL);
240 htmlf("%s %s", typename(obj->type),
241 sha1_to_hex(obj->sha1));
242 html_link_close();
243}
244
221void cgit_print_date(time_t secs, char *format) 245void cgit_print_date(time_t secs, char *format)
222{ 246{
223 char buf[64]; 247 char buf[64];
224 struct tm *time; 248 struct tm *time;
225 249
226 time = gmtime(&secs); 250 time = gmtime(&secs);
227 strftime(buf, sizeof(buf)-1, format, time); 251 strftime(buf, sizeof(buf)-1, format, time);
228 html_txt(buf); 252 html_txt(buf);
229} 253}
230 254
231void cgit_print_age(time_t t, time_t max_relative, char *format) 255void cgit_print_age(time_t t, time_t max_relative, char *format)
232{ 256{
233 time_t now, secs; 257 time_t now, secs;
234 258
235 time(&now); 259 time(&now);
236 secs = now - t; 260 secs = now - t;
237 261
238 if (secs > max_relative && max_relative >= 0) { 262 if (secs > max_relative && max_relative >= 0) {
239 cgit_print_date(t, format); 263 cgit_print_date(t, format);
240 return; 264 return;
241 } 265 }
242 266
243 if (secs < TM_HOUR * 2) { 267 if (secs < TM_HOUR * 2) {
244 htmlf("<span class='age-mins'>%.0f min.</span>", 268 htmlf("<span class='age-mins'>%.0f min.</span>",
245 secs * 1.0 / TM_MIN); 269 secs * 1.0 / TM_MIN);
246 return; 270 return;
247 } 271 }
248 if (secs < TM_DAY * 2) { 272 if (secs < TM_DAY * 2) {
249 htmlf("<span class='age-hours'>%.0f hours</span>", 273 htmlf("<span class='age-hours'>%.0f hours</span>",
250 secs * 1.0 / TM_HOUR); 274 secs * 1.0 / TM_HOUR);
251 return; 275 return;
252 } 276 }
253 if (secs < TM_WEEK * 2) { 277 if (secs < TM_WEEK * 2) {
254 htmlf("<span class='age-days'>%.0f days</span>", 278 htmlf("<span class='age-days'>%.0f days</span>",
255 secs * 1.0 / TM_DAY); 279 secs * 1.0 / TM_DAY);
256 return; 280 return;
257 } 281 }
258 if (secs < TM_MONTH * 2) { 282 if (secs < TM_MONTH * 2) {
259 htmlf("<span class='age-weeks'>%.0f weeks</span>", 283 htmlf("<span class='age-weeks'>%.0f weeks</span>",
260 secs * 1.0 / TM_WEEK); 284 secs * 1.0 / TM_WEEK);
261 return; 285 return;
262 } 286 }
263 if (secs < TM_YEAR * 2) { 287 if (secs < TM_YEAR * 2) {
264 htmlf("<span class='age-months'>%.0f months</span>", 288 htmlf("<span class='age-months'>%.0f months</span>",
265 secs * 1.0 / TM_MONTH); 289 secs * 1.0 / TM_MONTH);
266 return; 290 return;
267 } 291 }
268 htmlf("<span class='age-years'>%.0f years</span>", 292 htmlf("<span class='age-years'>%.0f years</span>",
269 secs * 1.0 / TM_YEAR); 293 secs * 1.0 / TM_YEAR);
270} 294}
271 295
272void cgit_print_docstart(char *title, struct cacheitem *item) 296void cgit_print_docstart(char *title, struct cacheitem *item)
273{ 297{
274 html("Content-Type: text/html; charset=utf-8\n"); 298 html("Content-Type: text/html; charset=utf-8\n");
275 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 299 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
276 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 300 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
277 ttl_seconds(item->ttl))); 301 ttl_seconds(item->ttl)));
278 html("\n"); 302 html("\n");
279 html(cgit_doctype); 303 html(cgit_doctype);
280 html("<html>\n"); 304 html("<html>\n");
281 html("<head>\n"); 305 html("<head>\n");
282 html("<title>"); 306 html("<title>");
283 html_txt(title); 307 html_txt(title);
284 html("</title>\n"); 308 html("</title>\n");
285 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 309 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
286 html("<link rel='stylesheet' type='text/css' href='"); 310 html("<link rel='stylesheet' type='text/css' href='");
287 html_attr(cgit_css); 311 html_attr(cgit_css);
288 html("'/>\n"); 312 html("'/>\n");
289 html("</head>\n"); 313 html("</head>\n");
290 html("<body>\n"); 314 html("<body>\n");
291} 315}
292 316
293void cgit_print_docend() 317void cgit_print_docend()
294{ 318{
295 html("</td></tr></table>"); 319 html("</td></tr></table>");
296 html("</body>\n</html>\n"); 320 html("</body>\n</html>\n");
297} 321}
298 322
299void cgit_print_pageheader(char *title, int show_search) 323void cgit_print_pageheader(char *title, int show_search)
300{ 324{
301 html("<table id='layout'>"); 325 html("<table id='layout'>");
302 html("<tr><td id='header'><a href='"); 326 html("<tr><td id='header'><a href='");
303 html_attr(cgit_rooturl()); 327 html_attr(cgit_rooturl());
304 html("'>"); 328 html("'>");
305 html_txt(cgit_root_title); 329 html_txt(cgit_root_title);
306 html("</a></td><td id='logo'>"); 330 html("</a></td><td id='logo'>");
307 html("<a href='"); 331 html("<a href='");
308 html_attr(cgit_logo_link); 332 html_attr(cgit_logo_link);
309 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 333 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo);
310 html("</td></tr>"); 334 html("</td></tr>");
311 html("<tr><td id='crumb'>"); 335 html("<tr><td id='crumb'>");
312 if (cgit_query_repo) { 336 if (cgit_query_repo) {
313 html_txt(cgit_repo->name); 337 html_txt(cgit_repo->name);
314 html(" ("); 338 html(" (");
315 html_txt(cgit_query_head); 339 html_txt(cgit_query_head);
316 html(") : &nbsp;"); 340 html(") : &nbsp;");
317 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head, 341 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head,
318 NULL, NULL); 342 NULL, NULL);
319 html(" "); 343 html(" ");
320 cgit_log_link("log", NULL, NULL, cgit_query_head, 344 cgit_log_link("log", NULL, NULL, cgit_query_head,
321 cgit_query_sha1, cgit_query_path, 0); 345 cgit_query_sha1, cgit_query_path, 0);
322 html(" "); 346 html(" ");
323 cgit_tree_link("tree", NULL, NULL, cgit_query_head, 347 cgit_tree_link("tree", NULL, NULL, cgit_query_head,
324 cgit_query_sha1, NULL); 348 cgit_query_sha1, NULL);
325 html(" "); 349 html(" ");
326 cgit_commit_link("commit", NULL, NULL, cgit_query_head, 350 cgit_commit_link("commit", NULL, NULL, cgit_query_head,
327 cgit_query_sha1); 351 cgit_query_sha1);
328 html(" "); 352 html(" ");
329 cgit_diff_link("diff", NULL, NULL, cgit_query_head, 353 cgit_diff_link("diff", NULL, NULL, cgit_query_head,
330 cgit_query_sha1, cgit_query_sha2, 354 cgit_query_sha1, cgit_query_sha2,
331 cgit_query_path); 355 cgit_query_path);
332 } else { 356 } else {
333 html_txt("Index of repositories"); 357 html_txt("Index of repositories");
334 } 358 }
335 html("</td>"); 359 html("</td>");
336 html("<td id='search'>"); 360 html("<td id='search'>");
337 if (show_search) { 361 if (show_search) {
338 html("<form method='get' action='"); 362 html("<form method='get' action='");
339 html_attr(cgit_currurl()); 363 html_attr(cgit_currurl());
340 html("'>"); 364 html("'>");
341 if (!cgit_virtual_root) { 365 if (!cgit_virtual_root) {
342 if (cgit_query_repo) 366 if (cgit_query_repo)
343 html_hidden("r", cgit_query_repo); 367 html_hidden("r", cgit_query_repo);
344 if (cgit_query_page) 368 if (cgit_query_page)
345 html_hidden("p", cgit_query_page); 369 html_hidden("p", cgit_query_page);
346 } 370 }
347 if (cgit_query_head) 371 if (cgit_query_head)
348 html_hidden("h", cgit_query_head); 372 html_hidden("h", cgit_query_head);
349 if (cgit_query_sha1) 373 if (cgit_query_sha1)
350 html_hidden("id", cgit_query_sha1); 374 html_hidden("id", cgit_query_sha1);
351 if (cgit_query_sha2) 375 if (cgit_query_sha2)
352 html_hidden("id2", cgit_query_sha2); 376 html_hidden("id2", cgit_query_sha2);
353 html("<input type='text' name='q' value='"); 377 html("<input type='text' name='q' value='");
354 html_attr(cgit_query_search); 378 html_attr(cgit_query_search);
355 html("'/></form>"); 379 html("'/></form>");
356 } 380 }
357 html("</td></tr>"); 381 html("</td></tr>");
358 html("<tr><td id='content' colspan='2'>"); 382 html("<tr><td id='content' colspan='2'>");
359} 383}
360 384
361void cgit_print_snapshot_start(const char *mimetype, const char *filename, 385void cgit_print_snapshot_start(const char *mimetype, const char *filename,
362 struct cacheitem *item) 386 struct cacheitem *item)
363{ 387{
364 htmlf("Content-Type: %s\n", mimetype); 388 htmlf("Content-Type: %s\n", mimetype);
365 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 389 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
366 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 390 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
367 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 391 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
368 ttl_seconds(item->ttl))); 392 ttl_seconds(item->ttl)));
369 html("\n"); 393 html("\n");
370} 394}
diff --git a/ui-summary.c b/ui-summary.c
index b4bc6d8..de8a180 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -1,214 +1,189 @@
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]; 18 char buf[256];
19 char *ref; 19 char *ref;
20 20
21 ref = xstrdup(refname); 21 ref = xstrdup(refname);
22 strncpy(buf, refname, sizeof(buf)); 22 strncpy(buf, refname, sizeof(buf));
23 commit = lookup_commit(sha1); 23 commit = lookup_commit(sha1);
24 // object is not really parsed at this point, because of some fallout 24 // object is not really parsed at this point, because of some fallout
25 // from previous calls to git functions in cgit_print_log() 25 // from previous calls to git functions in cgit_print_log()
26 commit->object.parsed = 0; 26 commit->object.parsed = 0;
27 if (commit && !parse_commit(commit)){ 27 if (commit && !parse_commit(commit)){
28 info = cgit_parse_commit(commit); 28 info = cgit_parse_commit(commit);
29 html("<tr><td>"); 29 html("<tr><td>");
30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0); 30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0);
31 html("</td><td>"); 31 html("</td><td>");
32 cgit_print_age(commit->date, -1, NULL); 32 cgit_print_age(commit->date, -1, NULL);
33 html("</td><td>"); 33 html("</td><td>");
34 html_txt(info->author); 34 html_txt(info->author);
35 html("</td><td>"); 35 html("</td><td>");
36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL); 36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL);
37 html("</td></tr>\n"); 37 html("</td></tr>\n");
38 cgit_free_commitinfo(info); 38 cgit_free_commitinfo(info);
39 } else { 39 } else {
40 html("<tr><td>"); 40 html("<tr><td>");
41 html_txt(buf); 41 html_txt(buf);
42 html("</td><td colspan='3'>"); 42 html("</td><td colspan='3'>");
43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1)); 43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1));
44 html("</td></tr>\n"); 44 html("</td></tr>\n");
45 } 45 }
46 free(ref); 46 free(ref);
47 return 0; 47 return 0;
48} 48}
49 49
50
51static void cgit_print_object_ref(struct object *obj)
52{
53 char *page, *arg, *url;
54
55 if (obj->type == OBJ_COMMIT) {
56 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
57 cgit_query_head, sha1_to_hex(obj->sha1));
58 return;
59 } else if (obj->type == OBJ_TREE) {
60 page = "tree";
61 arg = "id";
62 } else {
63 page = "view";
64 arg = "id";
65 }
66
67 url = cgit_pageurl(cgit_query_repo, page,
68 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
69 html_link_open(url, NULL, NULL);
70 htmlf("%s %s", typename(obj->type),
71 sha1_to_hex(obj->sha1));
72 html_link_close();
73}
74
75static void print_tag_header() 50static void print_tag_header()
76{ 51{
77 html("<tr class='nohover'><th class='left'>Tag</th>" 52 html("<tr class='nohover'><th class='left'>Tag</th>"
78 "<th class='left'>Age</th>" 53 "<th class='left'>Age</th>"
79 "<th class='left'>Author</th>" 54 "<th class='left'>Author</th>"
80 "<th class='left'>Reference</th></tr>\n"); 55 "<th class='left'>Reference</th></tr>\n");
81 header = 1; 56 header = 1;
82} 57}
83 58
84static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1, 59static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
85 int flags, void *cb_data) 60 int flags, void *cb_data)
86{ 61{
87 struct tag *tag; 62 struct tag *tag;
88 struct taginfo *info; 63 struct taginfo *info;
89 struct object *obj; 64 struct object *obj;
90 char buf[256], *url; 65 char buf[256], *url;
91 66
92 strncpy(buf, refname, sizeof(buf)); 67 strncpy(buf, refname, sizeof(buf));
93 obj = parse_object(sha1); 68 obj = parse_object(sha1);
94 if (!obj) 69 if (!obj)
95 return 1; 70 return 1;
96 if (obj->type == OBJ_TAG) { 71 if (obj->type == OBJ_TAG) {
97 tag = lookup_tag(sha1); 72 tag = lookup_tag(sha1);
98 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 73 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
99 return 2; 74 return 2;
100 if (!header) 75 if (!header)
101 print_tag_header(); 76 print_tag_header();
102 html("<tr><td>"); 77 html("<tr><td>");
103 url = cgit_pageurl(cgit_query_repo, "view", 78 url = cgit_pageurl(cgit_query_repo, "tag",
104 fmt("id=%s", sha1_to_hex(sha1))); 79 fmt("id=%s", refname));
105 html_link_open(url, NULL, NULL); 80 html_link_open(url, NULL, NULL);
106 html_txt(buf); 81 html_txt(buf);
107 html_link_close(); 82 html_link_close();
108 html("</td><td>"); 83 html("</td><td>");
109 if (info->tagger_date > 0) 84 if (info->tagger_date > 0)
110 cgit_print_age(info->tagger_date, -1, NULL); 85 cgit_print_age(info->tagger_date, -1, NULL);
111 html("</td><td>"); 86 html("</td><td>");
112 if (info->tagger) 87 if (info->tagger)
113 html(info->tagger); 88 html(info->tagger);
114 html("</td><td>"); 89 html("</td><td>");
115 cgit_print_object_ref(tag->tagged); 90 cgit_object_link(tag->tagged);
116 html("</td></tr>\n"); 91 html("</td></tr>\n");
117 } else { 92 } else {
118 if (!header) 93 if (!header)
119 print_tag_header(); 94 print_tag_header();
120 html("<tr><td>"); 95 html("<tr><td>");
121 html_txt(buf); 96 html_txt(buf);
122 html("</td><td colspan='2'/><td>"); 97 html("</td><td colspan='2'/><td>");
123 cgit_print_object_ref(obj); 98 cgit_object_link(obj);
124 html("</td></tr>\n"); 99 html("</td></tr>\n");
125 } 100 }
126 return 0; 101 return 0;
127} 102}
128 103
129static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1, 104static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
130 int flags, void *cb_data) 105 int flags, void *cb_data)
131{ 106{
132 struct tag *tag; 107 struct tag *tag;
133 struct taginfo *info; 108 struct taginfo *info;
134 struct object *obj; 109 struct object *obj;
135 char buf[256], *url; 110 char buf[256], *url;
136 unsigned char fileid[20]; 111 unsigned char fileid[20];
137 112
138 if (prefixcmp(refname, "refs/archives")) 113 if (prefixcmp(refname, "refs/archives"))
139 return 0; 114 return 0;
140 strncpy(buf, refname+14, sizeof(buf)); 115 strncpy(buf, refname+14, sizeof(buf));
141 obj = parse_object(sha1); 116 obj = parse_object(sha1);
142 if (!obj) 117 if (!obj)
143 return 1; 118 return 1;
144 if (obj->type == OBJ_TAG) { 119 if (obj->type == OBJ_TAG) {
145 tag = lookup_tag(sha1); 120 tag = lookup_tag(sha1);
146 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 121 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
147 return 0; 122 return 0;
148 hashcpy(fileid, tag->tagged->sha1); 123 hashcpy(fileid, tag->tagged->sha1);
149 } else if (obj->type != OBJ_BLOB) { 124 } else if (obj->type != OBJ_BLOB) {
150 return 0; 125 return 0;
151 } else { 126 } else {
152 hashcpy(fileid, sha1); 127 hashcpy(fileid, sha1);
153 } 128 }
154 if (!header) { 129 if (!header) {
155 html("<table id='downloads'>"); 130 html("<table id='downloads'>");
156 html("<tr><th>Downloads</th></tr>"); 131 html("<tr><th>Downloads</th></tr>");
157 header = 1; 132 header = 1;
158 } 133 }
159 html("<tr><td>"); 134 html("<tr><td>");
160 url = cgit_pageurl(cgit_query_repo, "blob", 135 url = cgit_pageurl(cgit_query_repo, "blob",
161 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 136 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
162 buf)); 137 buf));
163 html_link_open(url, NULL, NULL); 138 html_link_open(url, NULL, NULL);
164 html_txt(buf); 139 html_txt(buf);
165 html_link_close(); 140 html_link_close();
166 html("</td></tr>"); 141 html("</td></tr>");
167 return 0; 142 return 0;
168} 143}
169 144
170static void cgit_print_branches() 145static void cgit_print_branches()
171{ 146{
172 html("<tr class='nohover'><th class='left'>Branch</th>" 147 html("<tr class='nohover'><th class='left'>Branch</th>"
173 "<th class='left'>Idle</th>" 148 "<th class='left'>Idle</th>"
174 "<th class='left'>Author</th>" 149 "<th class='left'>Author</th>"
175 "<th class='left'>Head commit</th></tr>\n"); 150 "<th class='left'>Head commit</th></tr>\n");
176 for_each_branch_ref(cgit_print_branch_cb, NULL); 151 for_each_branch_ref(cgit_print_branch_cb, NULL);
177} 152}
178 153
179static void cgit_print_tags() 154static void cgit_print_tags()
180{ 155{
181 header = 0; 156 header = 0;
182 for_each_tag_ref(cgit_print_tag_cb, NULL); 157 for_each_tag_ref(cgit_print_tag_cb, NULL);
183} 158}
184 159
185static void cgit_print_archives() 160static void cgit_print_archives()
186{ 161{
187 header = 0; 162 header = 0;
188 for_each_ref(cgit_print_archive_cb, NULL); 163 for_each_ref(cgit_print_archive_cb, NULL);
189 if (header) 164 if (header)
190 html("</table>"); 165 html("</table>");
191} 166}
192 167
193void cgit_print_summary() 168void cgit_print_summary()
194{ 169{
195 html("<div id='summary'>"); 170 html("<div id='summary'>");
196 cgit_print_archives(); 171 cgit_print_archives();
197 html("<h2>"); 172 html("<h2>");
198 html_txt(cgit_repo->name); 173 html_txt(cgit_repo->name);
199 html(" - "); 174 html(" - ");
200 html_txt(cgit_repo->desc); 175 html_txt(cgit_repo->desc);
201 html("</h2>"); 176 html("</h2>");
202 if (cgit_repo->readme) 177 if (cgit_repo->readme)
203 html_include(cgit_repo->readme); 178 html_include(cgit_repo->readme);
204 html("</div>"); 179 html("</div>");
205 if (cgit_summary_log > 0) 180 if (cgit_summary_log > 0)
206 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0); 181 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0);
207 html("<table class='list nowrap'>"); 182 html("<table class='list nowrap'>");
208 if (cgit_summary_log > 0) 183 if (cgit_summary_log > 0)
209 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 184 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
210 cgit_print_branches(); 185 cgit_print_branches();
211 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 186 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
212 cgit_print_tags(); 187 cgit_print_tags();
213 html("</table>"); 188 html("</table>");
214} 189}
diff --git a/ui-tag.c b/ui-tag.c
new file mode 100644
index 0000000..a6989ff
--- a/dev/null
+++ b/ui-tag.c
@@ -0,0 +1,74 @@
1/* ui-tag.c: display a tag
2 *
3 * Copyright (C) 2007 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
11
12static void print_tag_content(char *buf)
13{
14 char *p;
15
16 if (!buf)
17 return;
18
19 html("<div class='commit-subject'>");
20 p = strchr(buf, '\n');
21 if (p)
22 *p = '\0';
23 html_txt(buf);
24 html("</div>");
25 if (p) {
26 html("<div class='commit-msg'>");
27 html_txt(++p);
28 html("</div>");
29 }
30}
31
32void cgit_print_tag(char *revname)
33{
34 unsigned char sha1[20];
35 struct object *obj;
36 struct tag *tag;
37 struct taginfo *info;
38
39 if (get_sha1(revname, sha1)) {
40 cgit_print_error(fmt("Bad tag reference: %s", revname));
41 return;
42 }
43 obj = parse_object(sha1);
44 if (!obj) {
45 cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1)));
46 return;
47 }
48 if (obj->type == OBJ_TAG) {
49 tag = lookup_tag(sha1);
50 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
51 cgit_print_error(fmt("Bad tag object: %s", revname));
52 return;
53 }
54 html("<table class='commit-info'>\n");
55 htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n",
56 revname, sha1_to_hex(sha1));
57 if (info->tagger_date > 0) {
58 html("<tr><td>Tag date</td><td>");
59 cgit_print_date(info->tagger_date, FMT_LONGDATE);
60 html("</td><tr>\n");
61 }
62 if (info->tagger) {
63 html("<tr><td>Tagged by</td><td>");
64 html_txt(info->tagger);
65 html("</td></tr>\n");
66 }
67 html("<tr><td>Tagged object</td><td>");
68 cgit_object_link(tag->tagged);
69 html("</td></tr>\n");
70 html("</table>\n");
71 print_tag_content(info->msg);
72 }
73 return;
74}