summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c3
-rw-r--r--cgit.h27
-rw-r--r--cgitrc10
-rw-r--r--shared.c49
-rw-r--r--ui-refs.c30
-rw-r--r--ui-shared.c6
-rw-r--r--ui-summary.c139
8 files changed, 229 insertions, 37 deletions
diff --git a/Makefile b/Makefile
index 8e3da72..36b5ff6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,72 +1,72 @@
1CGIT_VERSION = v0.6.3 1CGIT_VERSION = v0.6.3
2CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit 3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_CONFIG = /etc/cgitrc 4CGIT_CONFIG = /etc/cgitrc
5CACHE_ROOT = /var/cache/cgit 5CACHE_ROOT = /var/cache/cgit
6SHA1_HEADER = <openssl/sha.h> 6SHA1_HEADER = <openssl/sha.h>
7GIT_VER = 1.5.3.3 7GIT_VER = 1.5.3.3
8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
9 9
10# 10#
11# Let the user override the above settings. 11# Let the user override the above settings.
12# 12#
13-include cgit.conf 13-include cgit.conf
14 14
15 15
16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ 18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
19 ui-snapshot.o ui-blob.o ui-tag.o 19 ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o
20 20
21 21
22.PHONY: all git install clean distclean force-version get-git 22.PHONY: all git install clean distclean force-version get-git
23 23
24all: cgit git 24all: cgit git
25 25
26VERSION: force-version 26VERSION: force-version
27 @./gen-version.sh "$(CGIT_VERSION)" 27 @./gen-version.sh "$(CGIT_VERSION)"
28-include VERSION 28-include VERSION
29 29
30 30
31CFLAGS += -g -Wall -Igit 31CFLAGS += -g -Wall -Igit
32CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 32CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
33CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 33CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
34CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 34CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
35CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 35CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
36CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 36CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
37 37
38 38
39cgit: cgit.c $(OBJECTS) 39cgit: cgit.c $(OBJECTS)
40 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 40 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
41 41
42$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION 42$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
43 43
44git/xdiff/lib.a: | git 44git/xdiff/lib.a: | git
45 45
46git/libgit.a: | git 46git/libgit.a: | git
47 47
48git: 48git:
49 cd git && $(MAKE) xdiff/lib.a 49 cd git && $(MAKE) xdiff/lib.a
50 cd git && $(MAKE) libgit.a 50 cd git && $(MAKE) libgit.a
51 51
52install: all 52install: all
53 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH) 53 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH)
54 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 54 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
55 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css 55 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css
56 rm -rf $(DESTDIR)$(CACHE_ROOT)/* 56 rm -rf $(DESTDIR)$(CACHE_ROOT)/*
57 57
58uninstall: 58uninstall:
59 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 59 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
60 rm -f $(CGIT_SCRIPT_PATH)/cgit.css 60 rm -f $(CGIT_SCRIPT_PATH)/cgit.css
61 rm -rf $(CACHE_ROOT) 61 rm -rf $(CACHE_ROOT)
62 62
63clean: 63clean:
64 rm -f cgit VERSION *.o 64 rm -f cgit VERSION *.o
65 cd git && $(MAKE) clean 65 cd git && $(MAKE) clean
66 66
67distclean: clean 67distclean: clean
68 git clean -d -x 68 git clean -d -x
69 cd git && git clean -d -x 69 cd git && git clean -d -x
70 70
71get-git: 71get-git:
72 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git 72 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 1b85b15..cc18ed4 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,256 +1,259 @@
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_head, cgit_query_sha1, 71 cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1,
72 cgit_repobasename(cgit_repo->url), 72 cgit_repobasename(cgit_repo->url),
73 cgit_query_path, 73 cgit_query_path,
74 cgit_repo->snapshots ); 74 cgit_repo->snapshots );
75 return; 75 return;
76 } 76 }
77 77
78 if (cgit_cmd == CMD_BLOB) { 78 if (cgit_cmd == CMD_BLOB) {
79 cgit_print_blob(item, cgit_query_sha1, cgit_query_path); 79 cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
80 return; 80 return;
81 } 81 }
82 82
83 show_search = (cgit_cmd == CMD_LOG); 83 show_search = (cgit_cmd == CMD_LOG);
84 cgit_print_docstart(title, item); 84 cgit_print_docstart(title, item);
85 if (!cgit_cmd) { 85 if (!cgit_cmd) {
86 cgit_print_pageheader("summary", show_search); 86 cgit_print_pageheader("summary", show_search);
87 cgit_print_summary(); 87 cgit_print_summary();
88 cgit_print_docend(); 88 cgit_print_docend();
89 return; 89 return;
90 } 90 }
91 91
92 cgit_print_pageheader(cgit_query_page, show_search); 92 cgit_print_pageheader(cgit_query_page, show_search);
93 93
94 switch(cgit_cmd) { 94 switch(cgit_cmd) {
95 case CMD_LOG: 95 case CMD_LOG:
96 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 96 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
97 cgit_max_commit_count, cgit_query_search, 97 cgit_max_commit_count, cgit_query_search,
98 cgit_query_path, 1); 98 cgit_query_path, 1);
99 break; 99 break;
100 case CMD_TREE: 100 case CMD_TREE:
101 cgit_print_tree(cgit_query_sha1, cgit_query_path); 101 cgit_print_tree(cgit_query_sha1, cgit_query_path);
102 break; 102 break;
103 case CMD_COMMIT: 103 case CMD_COMMIT:
104 cgit_print_commit(cgit_query_sha1); 104 cgit_print_commit(cgit_query_sha1);
105 break; 105 break;
106 case CMD_REFS:
107 cgit_print_refs();
108 break;
106 case CMD_TAG: 109 case CMD_TAG:
107 cgit_print_tag(cgit_query_sha1); 110 cgit_print_tag(cgit_query_sha1);
108 break; 111 break;
109 case CMD_DIFF: 112 case CMD_DIFF:
110 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path); 113 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path);
111 break; 114 break;
112 default: 115 default:
113 cgit_print_error("Invalid request"); 116 cgit_print_error("Invalid request");
114 } 117 }
115 cgit_print_docend(); 118 cgit_print_docend();
116} 119}
117 120
118static void cgit_fill_cache(struct cacheitem *item, int use_cache) 121static void cgit_fill_cache(struct cacheitem *item, int use_cache)
119{ 122{
120 static char buf[PATH_MAX]; 123 static char buf[PATH_MAX];
121 int stdout2; 124 int stdout2;
122 125
123 getcwd(buf, sizeof(buf)); 126 getcwd(buf, sizeof(buf));
124 item->st.st_mtime = time(NULL); 127 item->st.st_mtime = time(NULL);
125 128
126 if (use_cache) { 129 if (use_cache) {
127 stdout2 = chk_positive(dup(STDOUT_FILENO), 130 stdout2 = chk_positive(dup(STDOUT_FILENO),
128 "Preserving STDOUT"); 131 "Preserving STDOUT");
129 chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); 132 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
130 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); 133 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
131 } 134 }
132 135
133 if (cgit_repo) 136 if (cgit_repo)
134 cgit_print_repo_page(item); 137 cgit_print_repo_page(item);
135 else 138 else
136 cgit_print_repolist(item); 139 cgit_print_repolist(item);
137 140
138 if (use_cache) { 141 if (use_cache) {
139 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT"); 142 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
140 chk_positive(dup2(stdout2, STDOUT_FILENO), 143 chk_positive(dup2(stdout2, STDOUT_FILENO),
141 "Restoring original STDOUT"); 144 "Restoring original STDOUT");
142 chk_zero(close(stdout2), "Closing temporary STDOUT"); 145 chk_zero(close(stdout2), "Closing temporary STDOUT");
143 } 146 }
144 147
145 chdir(buf); 148 chdir(buf);
146} 149}
147 150
148static void cgit_check_cache(struct cacheitem *item) 151static void cgit_check_cache(struct cacheitem *item)
149{ 152{
150 int i = 0; 153 int i = 0;
151 154
152 top: 155 top:
153 if (++i > cgit_max_lock_attempts) { 156 if (++i > cgit_max_lock_attempts) {
154 die("cgit_refresh_cache: unable to lock %s: %s", 157 die("cgit_refresh_cache: unable to lock %s: %s",
155 item->name, strerror(errno)); 158 item->name, strerror(errno));
156 } 159 }
157 if (!cache_exist(item)) { 160 if (!cache_exist(item)) {
158 if (!cache_lock(item)) { 161 if (!cache_lock(item)) {
159 sleep(1); 162 sleep(1);
160 goto top; 163 goto top;
161 } 164 }
162 if (!cache_exist(item)) { 165 if (!cache_exist(item)) {
163 cgit_fill_cache(item, 1); 166 cgit_fill_cache(item, 1);
164 cache_unlock(item); 167 cache_unlock(item);
165 } else { 168 } else {
166 cache_cancel_lock(item); 169 cache_cancel_lock(item);
167 } 170 }
168 } else if (cache_expired(item) && cache_lock(item)) { 171 } else if (cache_expired(item) && cache_lock(item)) {
169 if (cache_expired(item)) { 172 if (cache_expired(item)) {
170 cgit_fill_cache(item, 1); 173 cgit_fill_cache(item, 1);
171 cache_unlock(item); 174 cache_unlock(item);
172 } else { 175 } else {
173 cache_cancel_lock(item); 176 cache_cancel_lock(item);
174 } 177 }
175 } 178 }
176} 179}
177 180
178static void cgit_print_cache(struct cacheitem *item) 181static void cgit_print_cache(struct cacheitem *item)
179{ 182{
180 static char buf[4096]; 183 static char buf[4096];
181 ssize_t i; 184 ssize_t i;
182 185
183 int fd = open(item->name, O_RDONLY); 186 int fd = open(item->name, O_RDONLY);
184 if (fd<0) 187 if (fd<0)
185 die("Unable to open cached file %s", item->name); 188 die("Unable to open cached file %s", item->name);
186 189
187 while((i=read(fd, buf, sizeof(buf))) > 0) 190 while((i=read(fd, buf, sizeof(buf))) > 0)
188 write(STDOUT_FILENO, buf, i); 191 write(STDOUT_FILENO, buf, i);
189 192
190 close(fd); 193 close(fd);
191} 194}
192 195
193static void cgit_parse_args(int argc, const char **argv) 196static void cgit_parse_args(int argc, const char **argv)
194{ 197{
195 int i; 198 int i;
196 199
197 for (i = 1; i < argc; i++) { 200 for (i = 1; i < argc; i++) {
198 if (!strncmp(argv[i], "--cache=", 8)) { 201 if (!strncmp(argv[i], "--cache=", 8)) {
199 cgit_cache_root = xstrdup(argv[i]+8); 202 cgit_cache_root = xstrdup(argv[i]+8);
200 } 203 }
201 if (!strcmp(argv[i], "--nocache")) { 204 if (!strcmp(argv[i], "--nocache")) {
202 cgit_nocache = 1; 205 cgit_nocache = 1;
203 } 206 }
204 if (!strncmp(argv[i], "--query=", 8)) { 207 if (!strncmp(argv[i], "--query=", 8)) {
205 cgit_querystring = xstrdup(argv[i]+8); 208 cgit_querystring = xstrdup(argv[i]+8);
206 } 209 }
207 if (!strncmp(argv[i], "--repo=", 7)) { 210 if (!strncmp(argv[i], "--repo=", 7)) {
208 cgit_query_repo = xstrdup(argv[i]+7); 211 cgit_query_repo = xstrdup(argv[i]+7);
209 } 212 }
210 if (!strncmp(argv[i], "--page=", 7)) { 213 if (!strncmp(argv[i], "--page=", 7)) {
211 cgit_query_page = xstrdup(argv[i]+7); 214 cgit_query_page = xstrdup(argv[i]+7);
212 } 215 }
213 if (!strncmp(argv[i], "--head=", 7)) { 216 if (!strncmp(argv[i], "--head=", 7)) {
214 cgit_query_head = xstrdup(argv[i]+7); 217 cgit_query_head = xstrdup(argv[i]+7);
215 cgit_query_has_symref = 1; 218 cgit_query_has_symref = 1;
216 } 219 }
217 if (!strncmp(argv[i], "--sha1=", 7)) { 220 if (!strncmp(argv[i], "--sha1=", 7)) {
218 cgit_query_sha1 = xstrdup(argv[i]+7); 221 cgit_query_sha1 = xstrdup(argv[i]+7);
219 cgit_query_has_sha1 = 1; 222 cgit_query_has_sha1 = 1;
220 } 223 }
221 if (!strncmp(argv[i], "--ofs=", 6)) { 224 if (!strncmp(argv[i], "--ofs=", 6)) {
222 cgit_query_ofs = atoi(argv[i]+6); 225 cgit_query_ofs = atoi(argv[i]+6);
223 } 226 }
224 } 227 }
225} 228}
226 229
227int main(int argc, const char **argv) 230int main(int argc, const char **argv)
228{ 231{
229 struct cacheitem item; 232 struct cacheitem item;
230 const char *cgit_config_env = getenv("CGIT_CONFIG"); 233 const char *cgit_config_env = getenv("CGIT_CONFIG");
231 234
232 htmlfd = STDOUT_FILENO; 235 htmlfd = STDOUT_FILENO;
233 item.st.st_mtime = time(NULL); 236 item.st.st_mtime = time(NULL);
234 cgit_repolist.length = 0; 237 cgit_repolist.length = 0;
235 cgit_repolist.count = 0; 238 cgit_repolist.count = 0;
236 cgit_repolist.repos = NULL; 239 cgit_repolist.repos = NULL;
237 240
238 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 241 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
239 cgit_global_config_cb); 242 cgit_global_config_cb);
240 cgit_repo = NULL; 243 cgit_repo = NULL;
241 if (getenv("SCRIPT_NAME")) 244 if (getenv("SCRIPT_NAME"))
242 cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); 245 cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
243 if (getenv("QUERY_STRING")) 246 if (getenv("QUERY_STRING"))
244 cgit_querystring = xstrdup(getenv("QUERY_STRING")); 247 cgit_querystring = xstrdup(getenv("QUERY_STRING"));
245 cgit_parse_args(argc, argv); 248 cgit_parse_args(argc, argv);
246 cgit_parse_query(cgit_querystring, cgit_querystring_cb); 249 cgit_parse_query(cgit_querystring, cgit_querystring_cb);
247 if (!cgit_prepare_cache(&item)) 250 if (!cgit_prepare_cache(&item))
248 return 0; 251 return 0;
249 if (cgit_nocache) { 252 if (cgit_nocache) {
250 cgit_fill_cache(&item, 0); 253 cgit_fill_cache(&item, 0);
251 } else { 254 } else {
252 cgit_check_cache(&item); 255 cgit_check_cache(&item);
253 cgit_print_cache(&item); 256 cgit_print_cache(&item);
254 } 257 }
255 return 0; 258 return 0;
256} 259}
diff --git a/cgit.h b/cgit.h
index e96311f..f8f0316 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,249 +1,276 @@
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#define CMD_TAG 7 30#define CMD_TAG 7
31#define CMD_REFS 8
31 32
32/* 33/*
33 * Dateformats used on misc. pages 34 * Dateformats used on misc. pages
34 */ 35 */
35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
36#define FMT_SHORTDATE "%Y-%m-%d" 37#define FMT_SHORTDATE "%Y-%m-%d"
37 38
38 39
39/* 40/*
40 * Limits used for relative dates 41 * Limits used for relative dates
41 */ 42 */
42#define TM_MIN 60 43#define TM_MIN 60
43#define TM_HOUR (TM_MIN * 60) 44#define TM_HOUR (TM_MIN * 60)
44#define TM_DAY (TM_HOUR * 24) 45#define TM_DAY (TM_HOUR * 24)
45#define TM_WEEK (TM_DAY * 7) 46#define TM_WEEK (TM_DAY * 7)
46#define TM_YEAR (TM_DAY * 365) 47#define TM_YEAR (TM_DAY * 365)
47#define TM_MONTH (TM_YEAR / 12.0) 48#define TM_MONTH (TM_YEAR / 12.0)
48 49
49 50
50typedef void (*configfn)(const char *name, const char *value); 51typedef void (*configfn)(const char *name, const char *value);
51typedef void (*filepair_fn)(struct diff_filepair *pair); 52typedef void (*filepair_fn)(struct diff_filepair *pair);
52typedef void (*linediff_fn)(char *line, int len); 53typedef void (*linediff_fn)(char *line, int len);
53 54
54struct cacheitem { 55struct cacheitem {
55 char *name; 56 char *name;
56 struct stat st; 57 struct stat st;
57 int ttl; 58 int ttl;
58 int fd; 59 int fd;
59}; 60};
60 61
61struct repoinfo { 62struct repoinfo {
62 char *url; 63 char *url;
63 char *name; 64 char *name;
64 char *path; 65 char *path;
65 char *desc; 66 char *desc;
66 char *owner; 67 char *owner;
67 char *defbranch; 68 char *defbranch;
68 char *group; 69 char *group;
69 char *module_link; 70 char *module_link;
70 char *readme; 71 char *readme;
71 int snapshots; 72 int snapshots;
72 int enable_log_filecount; 73 int enable_log_filecount;
73 int enable_log_linecount; 74 int enable_log_linecount;
74}; 75};
75 76
76struct repolist { 77struct repolist {
77 int length; 78 int length;
78 int count; 79 int count;
79 struct repoinfo *repos; 80 struct repoinfo *repos;
80}; 81};
81 82
82struct commitinfo { 83struct commitinfo {
83 struct commit *commit; 84 struct commit *commit;
84 char *author; 85 char *author;
85 char *author_email; 86 char *author_email;
86 unsigned long author_date; 87 unsigned long author_date;
87 char *committer; 88 char *committer;
88 char *committer_email; 89 char *committer_email;
89 unsigned long committer_date; 90 unsigned long committer_date;
90 char *subject; 91 char *subject;
91 char *msg; 92 char *msg;
92}; 93};
93 94
94struct taginfo { 95struct taginfo {
95 char *tagger; 96 char *tagger;
96 char *tagger_email; 97 char *tagger_email;
97 int tagger_date; 98 int tagger_date;
98 char *msg; 99 char *msg;
99}; 100};
100 101
102struct refinfo {
103 const char *refname;
104 struct object *object;
105 union {
106 struct taginfo *tag;
107 struct commitinfo *commit;
108 };
109};
110
111struct reflist {
112 struct refinfo **refs;
113 int alloc;
114 int count;
115};
116
101extern const char *cgit_version; 117extern const char *cgit_version;
102 118
103extern struct repolist cgit_repolist; 119extern struct repolist cgit_repolist;
104extern struct repoinfo *cgit_repo; 120extern struct repoinfo *cgit_repo;
105extern int cgit_cmd; 121extern int cgit_cmd;
106 122
107extern char *cgit_root_title; 123extern char *cgit_root_title;
108extern char *cgit_css; 124extern char *cgit_css;
109extern char *cgit_logo; 125extern char *cgit_logo;
110extern char *cgit_index_header; 126extern char *cgit_index_header;
111extern char *cgit_logo_link; 127extern char *cgit_logo_link;
112extern char *cgit_module_link; 128extern char *cgit_module_link;
113extern char *cgit_agefile; 129extern char *cgit_agefile;
114extern char *cgit_virtual_root; 130extern char *cgit_virtual_root;
115extern char *cgit_script_name; 131extern char *cgit_script_name;
116extern char *cgit_cache_root; 132extern char *cgit_cache_root;
117extern char *cgit_repo_group; 133extern char *cgit_repo_group;
118 134
119extern int cgit_nocache; 135extern int cgit_nocache;
120extern int cgit_snapshots; 136extern int cgit_snapshots;
121extern int cgit_enable_index_links; 137extern int cgit_enable_index_links;
122extern int cgit_enable_log_filecount; 138extern int cgit_enable_log_filecount;
123extern int cgit_enable_log_linecount; 139extern int cgit_enable_log_linecount;
124extern int cgit_max_lock_attempts; 140extern int cgit_max_lock_attempts;
125extern int cgit_cache_root_ttl; 141extern int cgit_cache_root_ttl;
126extern int cgit_cache_repo_ttl; 142extern int cgit_cache_repo_ttl;
127extern int cgit_cache_dynamic_ttl; 143extern int cgit_cache_dynamic_ttl;
128extern int cgit_cache_static_ttl; 144extern int cgit_cache_static_ttl;
129extern int cgit_cache_max_create_time; 145extern int cgit_cache_max_create_time;
130extern int cgit_summary_log; 146extern int cgit_summary_log;
147extern int cgit_summary_tags;
148extern int cgit_summary_branches;
131 149
132extern int cgit_max_msg_len; 150extern int cgit_max_msg_len;
133extern int cgit_max_repodesc_len; 151extern int cgit_max_repodesc_len;
134extern int cgit_max_commit_count; 152extern int cgit_max_commit_count;
135 153
136extern int cgit_query_has_symref; 154extern int cgit_query_has_symref;
137extern int cgit_query_has_sha1; 155extern int cgit_query_has_sha1;
138 156
139extern char *cgit_querystring; 157extern char *cgit_querystring;
140extern char *cgit_query_repo; 158extern char *cgit_query_repo;
141extern char *cgit_query_page; 159extern char *cgit_query_page;
142extern char *cgit_query_search; 160extern char *cgit_query_search;
143extern char *cgit_query_head; 161extern char *cgit_query_head;
144extern char *cgit_query_sha1; 162extern char *cgit_query_sha1;
145extern char *cgit_query_sha2; 163extern char *cgit_query_sha2;
146extern char *cgit_query_path; 164extern char *cgit_query_path;
147extern char *cgit_query_name; 165extern char *cgit_query_name;
148extern int cgit_query_ofs; 166extern int cgit_query_ofs;
149 167
150extern int htmlfd; 168extern int htmlfd;
151 169
152extern int cgit_get_cmd_index(const char *cmd); 170extern int cgit_get_cmd_index(const char *cmd);
153extern struct repoinfo *cgit_get_repoinfo(const char *url); 171extern struct repoinfo *cgit_get_repoinfo(const char *url);
154extern void cgit_global_config_cb(const char *name, const char *value); 172extern void cgit_global_config_cb(const char *name, const char *value);
155extern void cgit_repo_config_cb(const char *name, const char *value); 173extern void cgit_repo_config_cb(const char *name, const char *value);
156extern void cgit_querystring_cb(const char *name, const char *value); 174extern void cgit_querystring_cb(const char *name, const char *value);
157 175
158extern int chk_zero(int result, char *msg); 176extern int chk_zero(int result, char *msg);
159extern int chk_positive(int result, char *msg); 177extern int chk_positive(int result, char *msg);
160extern int chk_non_negative(int result, char *msg); 178extern int chk_non_negative(int result, char *msg);
161 179
162extern int hextoint(char c); 180extern int hextoint(char c);
163extern char *trim_end(const char *str, char c); 181extern char *trim_end(const char *str, char c);
164 182
183extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
184extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
185 int flags, void *cb_data);
186
165extern void *cgit_free_commitinfo(struct commitinfo *info); 187extern void *cgit_free_commitinfo(struct commitinfo *info);
166 188
167extern int cgit_diff_files(const unsigned char *old_sha1, 189extern int cgit_diff_files(const unsigned char *old_sha1,
168 const unsigned char *new_sha1, 190 const unsigned char *new_sha1,
169 linediff_fn fn); 191 linediff_fn fn);
170 192
171extern void cgit_diff_tree(const unsigned char *old_sha1, 193extern void cgit_diff_tree(const unsigned char *old_sha1,
172 const unsigned char *new_sha1, 194 const unsigned char *new_sha1,
173 filepair_fn fn, const char *prefix); 195 filepair_fn fn, const char *prefix);
174 196
175extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 197extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
176 198
177extern char *fmt(const char *format,...); 199extern char *fmt(const char *format,...);
178 200
179extern void html(const char *txt); 201extern void html(const char *txt);
180extern void htmlf(const char *format,...); 202extern void htmlf(const char *format,...);
181extern void html_txt(char *txt); 203extern void html_txt(char *txt);
182extern void html_ntxt(int len, char *txt); 204extern void html_ntxt(int len, char *txt);
183extern void html_attr(char *txt); 205extern void html_attr(char *txt);
184extern void html_hidden(char *name, char *value); 206extern void html_hidden(char *name, char *value);
185extern void html_link_open(char *url, char *title, char *class); 207extern void html_link_open(char *url, char *title, char *class);
186extern void html_link_close(void); 208extern void html_link_close(void);
187extern void html_filemode(unsigned short mode); 209extern void html_filemode(unsigned short mode);
188extern int html_include(const char *filename); 210extern int html_include(const char *filename);
189 211
190extern int cgit_read_config(const char *filename, configfn fn); 212extern int cgit_read_config(const char *filename, configfn fn);
191extern int cgit_parse_query(char *txt, configfn fn); 213extern int cgit_parse_query(char *txt, configfn fn);
192extern struct commitinfo *cgit_parse_commit(struct commit *commit); 214extern struct commitinfo *cgit_parse_commit(struct commit *commit);
193extern struct taginfo *cgit_parse_tag(struct tag *tag); 215extern struct taginfo *cgit_parse_tag(struct tag *tag);
194extern void cgit_parse_url(const char *url); 216extern void cgit_parse_url(const char *url);
195 217
196extern char *cache_safe_filename(const char *unsafe); 218extern char *cache_safe_filename(const char *unsafe);
197extern int cache_lock(struct cacheitem *item); 219extern int cache_lock(struct cacheitem *item);
198extern int cache_unlock(struct cacheitem *item); 220extern int cache_unlock(struct cacheitem *item);
199extern int cache_cancel_lock(struct cacheitem *item); 221extern int cache_cancel_lock(struct cacheitem *item);
200extern int cache_exist(struct cacheitem *item); 222extern int cache_exist(struct cacheitem *item);
201extern int cache_expired(struct cacheitem *item); 223extern int cache_expired(struct cacheitem *item);
202 224
203extern char *cgit_repourl(const char *reponame); 225extern char *cgit_repourl(const char *reponame);
204extern char *cgit_fileurl(const char *reponame, const char *pagename, 226extern char *cgit_fileurl(const char *reponame, const char *pagename,
205 const char *filename, const char *query); 227 const char *filename, const char *query);
206extern char *cgit_pageurl(const char *reponame, const char *pagename, 228extern char *cgit_pageurl(const char *reponame, const char *pagename,
207 const char *query); 229 const char *query);
208 230
209extern const char *cgit_repobasename(const char *reponame); 231extern const char *cgit_repobasename(const char *reponame);
210 232
211extern void cgit_tree_link(char *name, char *title, char *class, char *head, 233extern void cgit_tree_link(char *name, char *title, char *class, char *head,
212 char *rev, char *path); 234 char *rev, char *path);
213extern void cgit_log_link(char *name, char *title, char *class, char *head, 235extern void cgit_log_link(char *name, char *title, char *class, char *head,
214 char *rev, char *path, int ofs); 236 char *rev, char *path, int ofs);
215extern void cgit_commit_link(char *name, char *title, char *class, char *head, 237extern void cgit_commit_link(char *name, char *title, char *class, char *head,
216 char *rev); 238 char *rev);
239extern void cgit_refs_link(char *name, char *title, char *class, char *head,
240 char *rev, char *path);
217extern void cgit_snapshot_link(char *name, char *title, char *class, 241extern void cgit_snapshot_link(char *name, char *title, char *class,
218 char *head, char *rev, char *archivename); 242 char *head, char *rev, char *archivename);
219extern void cgit_diff_link(char *name, char *title, char *class, char *head, 243extern void cgit_diff_link(char *name, char *title, char *class, char *head,
220 char *new_rev, char *old_rev, char *path); 244 char *new_rev, char *old_rev, char *path);
221 245
222extern void cgit_object_link(struct object *obj); 246extern void cgit_object_link(struct object *obj);
223 247
224extern void cgit_print_error(char *msg); 248extern void cgit_print_error(char *msg);
225extern void cgit_print_date(time_t secs, char *format); 249extern void cgit_print_date(time_t secs, char *format);
226extern void cgit_print_age(time_t t, time_t max_relative, char *format); 250extern void cgit_print_age(time_t t, time_t max_relative, char *format);
227extern void cgit_print_docstart(char *title, struct cacheitem *item); 251extern void cgit_print_docstart(char *title, struct cacheitem *item);
228extern void cgit_print_docend(); 252extern void cgit_print_docend();
229extern void cgit_print_pageheader(char *title, int show_search); 253extern void cgit_print_pageheader(char *title, int show_search);
230extern void cgit_print_snapshot_start(const char *mimetype, 254extern void cgit_print_snapshot_start(const char *mimetype,
231 const char *filename, 255 const char *filename,
232 struct cacheitem *item); 256 struct cacheitem *item);
257extern void cgit_print_branches(int maxcount);
258extern void cgit_print_tags(int maxcount);
233 259
234extern void cgit_print_repolist(struct cacheitem *item); 260extern void cgit_print_repolist(struct cacheitem *item);
235extern void cgit_print_summary(); 261extern void cgit_print_summary();
236extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 262extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
237extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 263extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
238extern void cgit_print_tree(const char *rev, char *path); 264extern void cgit_print_tree(const char *rev, char *path);
239extern void cgit_print_commit(char *hex); 265extern void cgit_print_commit(char *hex);
266extern void cgit_print_refs();
240extern void cgit_print_tag(char *revname); 267extern void cgit_print_tag(char *revname);
241extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix); 268extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix);
242extern void cgit_print_snapshot(struct cacheitem *item, const char *head, 269extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
243 const char *hex, const char *prefix, 270 const char *hex, const char *prefix,
244 const char *filename, int snapshot); 271 const char *filename, int snapshot);
245extern void cgit_print_snapshot_links(const char *repo, const char *head, 272extern void cgit_print_snapshot_links(const char *repo, const char *head,
246 const char *hex, int snapshots); 273 const char *hex, int snapshots);
247extern int cgit_parse_snapshots_mask(const char *str); 274extern int cgit_parse_snapshots_mask(const char *str);
248 275
249#endif /* CGIT_H */ 276#endif /* CGIT_H */
diff --git a/cgitrc b/cgitrc
index 796a62c..2b09e01 100644
--- a/cgitrc
+++ b/cgitrc
@@ -1,167 +1,177 @@
1## 1##
2## cgitrc: template for /etc/cgitrc 2## cgitrc: template for /etc/cgitrc
3## 3##
4 4
5 5
6## Uncomment and set to 1 to deactivate caching of generated pages. Mostly 6## Uncomment and set to 1 to deactivate caching of generated pages. Mostly
7## usefull for testing. 7## usefull for testing.
8#nocache=0 8#nocache=0
9 9
10 10
11## Set allowed snapshot types by default. Can be overridden per repo 11## Set allowed snapshot types by default. Can be overridden per repo
12# can be any combination of zip/tar.gz/tar.bz2/tar 12# can be any combination of zip/tar.gz/tar.bz2/tar
13#snapshots=0 13#snapshots=0
14 14
15 15
16## Enable/disable extra links to summary/log/tree per repo on index page 16## Enable/disable extra links to summary/log/tree per repo on index page
17#enable-index-links=0 17#enable-index-links=0
18 18
19 19
20## Enable/disable display of 'number of files changed' in log view 20## Enable/disable display of 'number of files changed' in log view
21#enable-log-filecount=0 21#enable-log-filecount=0
22 22
23 23
24## Enable/disable display of 'number of lines changed' in log view 24## Enable/disable display of 'number of lines changed' in log view
25#enable-log-linecount=0 25#enable-log-linecount=0
26 26
27 27
28## Enable/disable display of HEAD shortlog in summary view. Set it to maximum 28## Enable/disable display of HEAD shortlog in summary view. Set it to maximum
29## number of commits that should be displayed 29## number of commits that should be displayed
30#summary-log=0 30#summary-log=0
31 31
32 32
33## Restrict the number of branches printed in summary view. Set to 0 to
34## print all branches.
35#summary-branches=0
36
37
38## Restrict the number of tags printed in summary view. Set to 0 to
39## print all tags.
40#summary-tags=0
41
42
33## The "Idle" column on the repository index page can read a timestamp 43## The "Idle" column on the repository index page can read a timestamp
34## from the specified agefile (if this file cannot be found, the mtime 44## from the specified agefile (if this file cannot be found, the mtime
35## of HEAD is used). 45## of HEAD is used).
36## The cgit repo on hjemli.net uses the the following command in it's 46## The cgit repo on hjemli.net uses the the following command in it's
37## post-receive hook to update the age-file: 47## post-receive hook to update the age-file:
38## git-for-each-ref --format="%(committerdate)" --sort=-committerdate \ 48## git-for-each-ref --format="%(committerdate)" --sort=-committerdate \
39## --count=1 > $GIT_DIR/info/web/last-modifie 49## --count=1 > $GIT_DIR/info/web/last-modifie
40## 50##
41#agefile=info/web/last-modified 51#agefile=info/web/last-modified
42 52
43 53
44## Git detects renames, but with a limit on the number of files to 54## Git detects renames, but with a limit on the number of files to
45## consider. This option can be used to specify another limit (or -1 to 55## consider. This option can be used to specify another limit (or -1 to
46## use the default limit). 56## use the default limit).
47## 57##
48#renamelimit=-1 58#renamelimit=-1
49 59
50 60
51## Specify a root for virtual urls. This makes cgit generate urls like 61## Specify a root for virtual urls. This makes cgit generate urls like
52## 62##
53## http://localhost/git/repo/log/?h=branch 63## http://localhost/git/repo/log/?h=branch
54## 64##
55## instead of 65## instead of
56## 66##
57## http://localhost/cgit/cgit.cgi?url=repo/log&h=branch 67## http://localhost/cgit/cgit.cgi?url=repo/log&h=branch
58## 68##
59## For this to work with apache, a rewrite rule must be added to httpd.conf, 69## For this to work with apache, a rewrite rule must be added to httpd.conf,
60## possibly looking something like this: 70## possibly looking something like this:
61## 71##
62## RewriteRule ^/git/(.*)$ /cgit/cgit.cgi?url=$1 [L,QSA] 72## RewriteRule ^/git/(.*)$ /cgit/cgit.cgi?url=$1 [L,QSA]
63## 73##
64## For this to work with lighttpd, the rewrite rule should look more like this: 74## For this to work with lighttpd, the rewrite rule should look more like this:
65## 75##
66## url.rewrite = ( 76## url.rewrite = (
67## "^/git/([^?/]+/[^?]*)?(?:\?(.*))?$" => "/cgit.cgi?url=$1&$2" 77## "^/git/([^?/]+/[^?]*)?(?:\?(.*))?$" => "/cgit.cgi?url=$1&$2"
68## ) 78## )
69## 79##
70## This setting is disabled by default. 80## This setting is disabled by default.
71#virtual-root=/git 81#virtual-root=/git
72 82
73 83
74## Set the title printed on the root page 84## Set the title printed on the root page
75#root-title=Git repository browser 85#root-title=Git repository browser
76 86
77 87
78## If specified, the file at this path will be included as HTML in the index 88## If specified, the file at this path will be included as HTML in the index
79## of repositories 89## of repositories
80#index-header= 90#index-header=
81 91
82 92
83## Link to css file 93## Link to css file
84#css=/cgit/cgit.css 94#css=/cgit/cgit.css
85 95
86 96
87## Link to logo file 97## Link to logo file
88#logo=/cgit/git-logo.png 98#logo=/cgit/git-logo.png
89 99
90 100
91## Url loaded when clicking the logo 101## Url loaded when clicking the logo
92#logo-link=http://www.kernel.org/pub/software/scm/git/docs/ 102#logo-link=http://www.kernel.org/pub/software/scm/git/docs/
93 103
94 104
95## Url loaded when clicking a submodule link 105## Url loaded when clicking a submodule link
96#module-link=./?repo=%s&page=commit&id=%s 106#module-link=./?repo=%s&page=commit&id=%s
97 107
98 108
99## Number of chars shown of repo description (in repolist view) 109## Number of chars shown of repo description (in repolist view)
100#max-repodesc-length=60 110#max-repodesc-length=60
101 111
102 112
103## Number of chars shown of commit subject message (in log view) 113## Number of chars shown of commit subject message (in log view)
104#max-message-length=60 114#max-message-length=60
105 115
106 116
107## Number of commits per page in log view 117## Number of commits per page in log view
108#max-commit-count=50 118#max-commit-count=50
109 119
110 120
111## Root of cached output 121## Root of cached output
112#cache-root=/var/cache/cgit 122#cache-root=/var/cache/cgit
113 123
114 124
115## Include another config-file 125## Include another config-file
116#include=/var/cgit/repolist 126#include=/var/cgit/repolist
117 127
118## 128##
119## Time-To-Live settings: specifies how long (in minutes) different pages 129## Time-To-Live settings: specifies how long (in minutes) different pages
120## should be cached (0 for instant expiration, -1 for immortal pages) 130## should be cached (0 for instant expiration, -1 for immortal pages)
121## 131##
122 132
123## ttl for root page 133## ttl for root page
124#cache-root-ttl=5 134#cache-root-ttl=5
125 135
126## ttl for repo summary page 136## ttl for repo summary page
127#cache-repo-ttl=5 137#cache-repo-ttl=5
128 138
129## ttl for other dynamic pages 139## ttl for other dynamic pages
130#cache-dynamic-ttl=5 140#cache-dynamic-ttl=5
131 141
132## ttl for static pages (addressed by SHA-1) 142## ttl for static pages (addressed by SHA-1)
133#cache-static-ttl=-1 143#cache-static-ttl=-1
134 144
135 145
136 146
137## Example repository entry. Required values are repo.url and repo.path (each 147## Example repository entry. Required values are repo.url and repo.path (each
138## repository section must start with repo.url). 148## repository section must start with repo.url).
139#repo.url=cgit 149#repo.url=cgit
140#repo.name=cgit 150#repo.name=cgit
141#repo.desc=the caching cgi for git 151#repo.desc=the caching cgi for git
142 #repo.path=/pub/git/cgit ## this is the path to $GIT_DIR 152 #repo.path=/pub/git/cgit ## this is the path to $GIT_DIR
143#repo.owner=Lars Hjemli 153#repo.owner=Lars Hjemli
144 #repo.defbranch=master ## define a default branch 154 #repo.defbranch=master ## define a default branch
145 #repo.snapshots=tar.bz2 ## override a sitewide snapshot-setting 155 #repo.snapshots=tar.bz2 ## override a sitewide snapshot-setting
146 #repo.enable-log-filecount=0 ## override the default filecount setting 156 #repo.enable-log-filecount=0 ## override the default filecount setting
147 #repo.enable-log-linecount=0 ## override the default linecount setting 157 #repo.enable-log-linecount=0 ## override the default linecount setting
148 #repo.module-link=/git/%s/commit/?id=%s ## override the standard module-link 158 #repo.module-link=/git/%s/commit/?id=%s ## override the standard module-link
149 #repo.readme=info/web/readme ## specify a file to include on summary page 159 #repo.readme=info/web/readme ## specify a file to include on summary page
150 160
151## Additional repositories grouped under "mirrors" 161## Additional repositories grouped under "mirrors"
152#repo.group=mirrors 162#repo.group=mirrors
153 163
154#repo.url=git 164#repo.url=git
155#repo.path=/pub/git/git 165#repo.path=/pub/git/git
156# 166#
157#repo.url=linux 167#repo.url=linux
158#repo.path=/pub/git/linux 168#repo.path=/pub/git/linux
159 169
160## A group of private repositories (with a working directory) 170## A group of private repositories (with a working directory)
161#repo.group=private 171#repo.group=private
162 172
163#repo.url=larsh/cgit 173#repo.url=larsh/cgit
164#repo.path=/home/larsh/src/cgit/.git 174#repo.path=/home/larsh/src/cgit/.git
165 175
166#repo.url=larsh/git 176#repo.url=larsh/git
167#repo.path=/home/larsh/src/git/.git 177#repo.path=/home/larsh/src/git/.git
diff --git a/shared.c b/shared.c
index 3d4feea..7eb2b0e 100644
--- a/shared.c
+++ b/shared.c
@@ -1,425 +1,472 @@
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 = CGIT_CACHE_ROOT; 26char *cgit_cache_root = CGIT_CACHE_ROOT;
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;
41int cgit_summary_tags = 0;
42int cgit_summary_branches = 0;
41int cgit_renamelimit = -1; 43int cgit_renamelimit = -1;
42 44
43int cgit_max_msg_len = 60; 45int cgit_max_msg_len = 60;
44int cgit_max_repodesc_len = 60; 46int cgit_max_repodesc_len = 60;
45int cgit_max_commit_count = 50; 47int cgit_max_commit_count = 50;
46 48
47int cgit_query_has_symref = 0; 49int cgit_query_has_symref = 0;
48int cgit_query_has_sha1 = 0; 50int cgit_query_has_sha1 = 0;
49 51
50char *cgit_querystring = NULL; 52char *cgit_querystring = NULL;
51char *cgit_query_repo = NULL; 53char *cgit_query_repo = NULL;
52char *cgit_query_page = NULL; 54char *cgit_query_page = NULL;
53char *cgit_query_head = NULL; 55char *cgit_query_head = NULL;
54char *cgit_query_search = NULL; 56char *cgit_query_search = NULL;
55char *cgit_query_sha1 = NULL; 57char *cgit_query_sha1 = NULL;
56char *cgit_query_sha2 = NULL; 58char *cgit_query_sha2 = NULL;
57char *cgit_query_path = NULL; 59char *cgit_query_path = NULL;
58char *cgit_query_name = NULL; 60char *cgit_query_name = NULL;
59int cgit_query_ofs = 0; 61int cgit_query_ofs = 0;
60 62
61int htmlfd = 0; 63int htmlfd = 0;
62 64
63 65
64int cgit_get_cmd_index(const char *cmd) 66int cgit_get_cmd_index(const char *cmd)
65{ 67{
66 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 68 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
67 "snapshot", "tag", NULL}; 69 "snapshot", "tag", "refs", NULL};
68 int i; 70 int i;
69 71
70 for(i = 0; cmds[i]; i++) 72 for(i = 0; cmds[i]; i++)
71 if (!strcmp(cmd, cmds[i])) 73 if (!strcmp(cmd, cmds[i]))
72 return i + 1; 74 return i + 1;
73 return 0; 75 return 0;
74} 76}
75 77
76int chk_zero(int result, char *msg) 78int chk_zero(int result, char *msg)
77{ 79{
78 if (result != 0) 80 if (result != 0)
79 die("%s: %s", msg, strerror(errno)); 81 die("%s: %s", msg, strerror(errno));
80 return result; 82 return result;
81} 83}
82 84
83int chk_positive(int result, char *msg) 85int chk_positive(int result, char *msg)
84{ 86{
85 if (result <= 0) 87 if (result <= 0)
86 die("%s: %s", msg, strerror(errno)); 88 die("%s: %s", msg, strerror(errno));
87 return result; 89 return result;
88} 90}
89 91
90int chk_non_negative(int result, char *msg) 92int chk_non_negative(int result, char *msg)
91{ 93{
92 if (result < 0) 94 if (result < 0)
93 die("%s: %s",msg, strerror(errno)); 95 die("%s: %s",msg, strerror(errno));
94 return result; 96 return result;
95} 97}
96 98
97struct repoinfo *add_repo(const char *url) 99struct repoinfo *add_repo(const char *url)
98{ 100{
99 struct repoinfo *ret; 101 struct repoinfo *ret;
100 102
101 if (++cgit_repolist.count > cgit_repolist.length) { 103 if (++cgit_repolist.count > cgit_repolist.length) {
102 if (cgit_repolist.length == 0) 104 if (cgit_repolist.length == 0)
103 cgit_repolist.length = 8; 105 cgit_repolist.length = 8;
104 else 106 else
105 cgit_repolist.length *= 2; 107 cgit_repolist.length *= 2;
106 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 108 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
107 cgit_repolist.length * 109 cgit_repolist.length *
108 sizeof(struct repoinfo)); 110 sizeof(struct repoinfo));
109 } 111 }
110 112
111 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 113 ret = &cgit_repolist.repos[cgit_repolist.count-1];
112 ret->url = trim_end(url, '/'); 114 ret->url = trim_end(url, '/');
113 ret->name = ret->url; 115 ret->name = ret->url;
114 ret->path = NULL; 116 ret->path = NULL;
115 ret->desc = NULL; 117 ret->desc = NULL;
116 ret->owner = NULL; 118 ret->owner = NULL;
117 ret->group = cgit_repo_group; 119 ret->group = cgit_repo_group;
118 ret->defbranch = "master"; 120 ret->defbranch = "master";
119 ret->snapshots = cgit_snapshots; 121 ret->snapshots = cgit_snapshots;
120 ret->enable_log_filecount = cgit_enable_log_filecount; 122 ret->enable_log_filecount = cgit_enable_log_filecount;
121 ret->enable_log_linecount = cgit_enable_log_linecount; 123 ret->enable_log_linecount = cgit_enable_log_linecount;
122 ret->module_link = cgit_module_link; 124 ret->module_link = cgit_module_link;
123 ret->readme = NULL; 125 ret->readme = NULL;
124 return ret; 126 return ret;
125} 127}
126 128
127struct repoinfo *cgit_get_repoinfo(const char *url) 129struct repoinfo *cgit_get_repoinfo(const char *url)
128{ 130{
129 int i; 131 int i;
130 struct repoinfo *repo; 132 struct repoinfo *repo;
131 133
132 for (i=0; i<cgit_repolist.count; i++) { 134 for (i=0; i<cgit_repolist.count; i++) {
133 repo = &cgit_repolist.repos[i]; 135 repo = &cgit_repolist.repos[i];
134 if (!strcmp(repo->url, url)) 136 if (!strcmp(repo->url, url))
135 return repo; 137 return repo;
136 } 138 }
137 return NULL; 139 return NULL;
138} 140}
139 141
140void cgit_global_config_cb(const char *name, const char *value) 142void cgit_global_config_cb(const char *name, const char *value)
141{ 143{
142 if (!strcmp(name, "root-title")) 144 if (!strcmp(name, "root-title"))
143 cgit_root_title = xstrdup(value); 145 cgit_root_title = xstrdup(value);
144 else if (!strcmp(name, "css")) 146 else if (!strcmp(name, "css"))
145 cgit_css = xstrdup(value); 147 cgit_css = xstrdup(value);
146 else if (!strcmp(name, "logo")) 148 else if (!strcmp(name, "logo"))
147 cgit_logo = xstrdup(value); 149 cgit_logo = xstrdup(value);
148 else if (!strcmp(name, "index-header")) 150 else if (!strcmp(name, "index-header"))
149 cgit_index_header = xstrdup(value); 151 cgit_index_header = xstrdup(value);
150 else if (!strcmp(name, "logo-link")) 152 else if (!strcmp(name, "logo-link"))
151 cgit_logo_link = xstrdup(value); 153 cgit_logo_link = xstrdup(value);
152 else if (!strcmp(name, "module-link")) 154 else if (!strcmp(name, "module-link"))
153 cgit_module_link = xstrdup(value); 155 cgit_module_link = xstrdup(value);
154 else if (!strcmp(name, "virtual-root")) 156 else if (!strcmp(name, "virtual-root"))
155 cgit_virtual_root = trim_end(value, '/'); 157 cgit_virtual_root = trim_end(value, '/');
156 else if (!strcmp(name, "nocache")) 158 else if (!strcmp(name, "nocache"))
157 cgit_nocache = atoi(value); 159 cgit_nocache = atoi(value);
158 else if (!strcmp(name, "snapshots")) 160 else if (!strcmp(name, "snapshots"))
159 cgit_snapshots = cgit_parse_snapshots_mask(value); 161 cgit_snapshots = cgit_parse_snapshots_mask(value);
160 else if (!strcmp(name, "enable-index-links")) 162 else if (!strcmp(name, "enable-index-links"))
161 cgit_enable_index_links = atoi(value); 163 cgit_enable_index_links = atoi(value);
162 else if (!strcmp(name, "enable-log-filecount")) 164 else if (!strcmp(name, "enable-log-filecount"))
163 cgit_enable_log_filecount = atoi(value); 165 cgit_enable_log_filecount = atoi(value);
164 else if (!strcmp(name, "enable-log-linecount")) 166 else if (!strcmp(name, "enable-log-linecount"))
165 cgit_enable_log_linecount = atoi(value); 167 cgit_enable_log_linecount = atoi(value);
166 else if (!strcmp(name, "cache-root")) 168 else if (!strcmp(name, "cache-root"))
167 cgit_cache_root = xstrdup(value); 169 cgit_cache_root = xstrdup(value);
168 else if (!strcmp(name, "cache-root-ttl")) 170 else if (!strcmp(name, "cache-root-ttl"))
169 cgit_cache_root_ttl = atoi(value); 171 cgit_cache_root_ttl = atoi(value);
170 else if (!strcmp(name, "cache-repo-ttl")) 172 else if (!strcmp(name, "cache-repo-ttl"))
171 cgit_cache_repo_ttl = atoi(value); 173 cgit_cache_repo_ttl = atoi(value);
172 else if (!strcmp(name, "cache-static-ttl")) 174 else if (!strcmp(name, "cache-static-ttl"))
173 cgit_cache_static_ttl = atoi(value); 175 cgit_cache_static_ttl = atoi(value);
174 else if (!strcmp(name, "cache-dynamic-ttl")) 176 else if (!strcmp(name, "cache-dynamic-ttl"))
175 cgit_cache_dynamic_ttl = atoi(value); 177 cgit_cache_dynamic_ttl = atoi(value);
176 else if (!strcmp(name, "max-message-length")) 178 else if (!strcmp(name, "max-message-length"))
177 cgit_max_msg_len = atoi(value); 179 cgit_max_msg_len = atoi(value);
178 else if (!strcmp(name, "max-repodesc-length")) 180 else if (!strcmp(name, "max-repodesc-length"))
179 cgit_max_repodesc_len = atoi(value); 181 cgit_max_repodesc_len = atoi(value);
180 else if (!strcmp(name, "max-commit-count")) 182 else if (!strcmp(name, "max-commit-count"))
181 cgit_max_commit_count = atoi(value); 183 cgit_max_commit_count = atoi(value);
182 else if (!strcmp(name, "summary-log")) 184 else if (!strcmp(name, "summary-log"))
183 cgit_summary_log = atoi(value); 185 cgit_summary_log = atoi(value);
186 else if (!strcmp(name, "summary-branches"))
187 cgit_summary_branches = atoi(value);
188 else if (!strcmp(name, "summary-tags"))
189 cgit_summary_tags = atoi(value);
184 else if (!strcmp(name, "agefile")) 190 else if (!strcmp(name, "agefile"))
185 cgit_agefile = xstrdup(value); 191 cgit_agefile = xstrdup(value);
186 else if (!strcmp(name, "renamelimit")) 192 else if (!strcmp(name, "renamelimit"))
187 cgit_renamelimit = atoi(value); 193 cgit_renamelimit = atoi(value);
188 else if (!strcmp(name, "repo.group")) 194 else if (!strcmp(name, "repo.group"))
189 cgit_repo_group = xstrdup(value); 195 cgit_repo_group = xstrdup(value);
190 else if (!strcmp(name, "repo.url")) 196 else if (!strcmp(name, "repo.url"))
191 cgit_repo = add_repo(value); 197 cgit_repo = add_repo(value);
192 else if (!strcmp(name, "repo.name")) 198 else if (!strcmp(name, "repo.name"))
193 cgit_repo->name = xstrdup(value); 199 cgit_repo->name = xstrdup(value);
194 else if (cgit_repo && !strcmp(name, "repo.path")) 200 else if (cgit_repo && !strcmp(name, "repo.path"))
195 cgit_repo->path = trim_end(value, '/'); 201 cgit_repo->path = trim_end(value, '/');
196 else if (cgit_repo && !strcmp(name, "repo.desc")) 202 else if (cgit_repo && !strcmp(name, "repo.desc"))
197 cgit_repo->desc = xstrdup(value); 203 cgit_repo->desc = xstrdup(value);
198 else if (cgit_repo && !strcmp(name, "repo.owner")) 204 else if (cgit_repo && !strcmp(name, "repo.owner"))
199 cgit_repo->owner = xstrdup(value); 205 cgit_repo->owner = xstrdup(value);
200 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 206 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
201 cgit_repo->defbranch = xstrdup(value); 207 cgit_repo->defbranch = xstrdup(value);
202 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 208 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
203 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 209 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
204 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 210 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
205 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 211 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
206 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 212 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
207 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); 213 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
208 else if (cgit_repo && !strcmp(name, "repo.module-link")) 214 else if (cgit_repo && !strcmp(name, "repo.module-link"))
209 cgit_repo->module_link= xstrdup(value); 215 cgit_repo->module_link= xstrdup(value);
210 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { 216 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
211 if (*value == '/') 217 if (*value == '/')
212 cgit_repo->readme = xstrdup(value); 218 cgit_repo->readme = xstrdup(value);
213 else 219 else
214 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); 220 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
215 } else if (!strcmp(name, "include")) 221 } else if (!strcmp(name, "include"))
216 cgit_read_config(value, cgit_global_config_cb); 222 cgit_read_config(value, cgit_global_config_cb);
217} 223}
218 224
219void cgit_querystring_cb(const char *name, const char *value) 225void cgit_querystring_cb(const char *name, const char *value)
220{ 226{
221 if (!strcmp(name,"r")) { 227 if (!strcmp(name,"r")) {
222 cgit_query_repo = xstrdup(value); 228 cgit_query_repo = xstrdup(value);
223 cgit_repo = cgit_get_repoinfo(value); 229 cgit_repo = cgit_get_repoinfo(value);
224 } else if (!strcmp(name, "p")) { 230 } else if (!strcmp(name, "p")) {
225 cgit_query_page = xstrdup(value); 231 cgit_query_page = xstrdup(value);
226 cgit_cmd = cgit_get_cmd_index(value); 232 cgit_cmd = cgit_get_cmd_index(value);
227 } else if (!strcmp(name, "url")) { 233 } else if (!strcmp(name, "url")) {
228 cgit_parse_url(value); 234 cgit_parse_url(value);
229 } else if (!strcmp(name, "q")) { 235 } else if (!strcmp(name, "q")) {
230 cgit_query_search = xstrdup(value); 236 cgit_query_search = xstrdup(value);
231 } else if (!strcmp(name, "h")) { 237 } else if (!strcmp(name, "h")) {
232 cgit_query_head = xstrdup(value); 238 cgit_query_head = xstrdup(value);
233 cgit_query_has_symref = 1; 239 cgit_query_has_symref = 1;
234 } else if (!strcmp(name, "id")) { 240 } else if (!strcmp(name, "id")) {
235 cgit_query_sha1 = xstrdup(value); 241 cgit_query_sha1 = xstrdup(value);
236 cgit_query_has_sha1 = 1; 242 cgit_query_has_sha1 = 1;
237 } else if (!strcmp(name, "id2")) { 243 } else if (!strcmp(name, "id2")) {
238 cgit_query_sha2 = xstrdup(value); 244 cgit_query_sha2 = xstrdup(value);
239 cgit_query_has_sha1 = 1; 245 cgit_query_has_sha1 = 1;
240 } else if (!strcmp(name, "ofs")) { 246 } else if (!strcmp(name, "ofs")) {
241 cgit_query_ofs = atoi(value); 247 cgit_query_ofs = atoi(value);
242 } else if (!strcmp(name, "path")) { 248 } else if (!strcmp(name, "path")) {
243 cgit_query_path = trim_end(value, '/'); 249 cgit_query_path = trim_end(value, '/');
244 } else if (!strcmp(name, "name")) { 250 } else if (!strcmp(name, "name")) {
245 cgit_query_name = xstrdup(value); 251 cgit_query_name = xstrdup(value);
246 } 252 }
247} 253}
248 254
249void *cgit_free_commitinfo(struct commitinfo *info) 255void *cgit_free_commitinfo(struct commitinfo *info)
250{ 256{
251 free(info->author); 257 free(info->author);
252 free(info->author_email); 258 free(info->author_email);
253 free(info->committer); 259 free(info->committer);
254 free(info->committer_email); 260 free(info->committer_email);
255 free(info->subject); 261 free(info->subject);
256 free(info); 262 free(info);
257 return NULL; 263 return NULL;
258} 264}
259 265
260int hextoint(char c) 266int hextoint(char c)
261{ 267{
262 if (c >= 'a' && c <= 'f') 268 if (c >= 'a' && c <= 'f')
263 return 10 + c - 'a'; 269 return 10 + c - 'a';
264 else if (c >= 'A' && c <= 'F') 270 else if (c >= 'A' && c <= 'F')
265 return 10 + c - 'A'; 271 return 10 + c - 'A';
266 else if (c >= '0' && c <= '9') 272 else if (c >= '0' && c <= '9')
267 return c - '0'; 273 return c - '0';
268 else 274 else
269 return -1; 275 return -1;
270} 276}
271 277
272char *trim_end(const char *str, char c) 278char *trim_end(const char *str, char c)
273{ 279{
274 int len; 280 int len;
275 char *s, *t; 281 char *s, *t;
276 282
277 if (str == NULL) 283 if (str == NULL)
278 return NULL; 284 return NULL;
279 t = (char *)str; 285 t = (char *)str;
280 len = strlen(t); 286 len = strlen(t);
281 while(len > 0 && t[len - 1] == c) 287 while(len > 0 && t[len - 1] == c)
282 len--; 288 len--;
283 289
284 if (len == 0) 290 if (len == 0)
285 return NULL; 291 return NULL;
286 292
287 c = t[len]; 293 c = t[len];
288 t[len] = '\0'; 294 t[len] = '\0';
289 s = xstrdup(t); 295 s = xstrdup(t);
290 t[len] = c; 296 t[len] = c;
291 return s; 297 return s;
292} 298}
293 299
300void cgit_add_ref(struct reflist *list, struct refinfo *ref)
301{
302 size_t size;
303
304 if (list->count >= list->alloc) {
305 list->alloc += (list->alloc ? list->alloc : 4);
306 size = list->alloc * sizeof(struct refinfo *);
307 list->refs = xrealloc(list->refs, size);
308 }
309 list->refs[list->count++] = ref;
310}
311
312struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
313{
314 struct refinfo *ref;
315
316 ref = xmalloc(sizeof (struct refinfo));
317 ref->refname = xstrdup(refname);
318 ref->object = parse_object(sha1);
319 switch (ref->object->type) {
320 case OBJ_TAG:
321 ref->tag = cgit_parse_tag((struct tag *)ref->object);
322 break;
323 case OBJ_COMMIT:
324 ref->commit = cgit_parse_commit((struct commit *)ref->object);
325 break;
326 }
327 return ref;
328}
329
330int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
331 void *cb_data)
332{
333 struct reflist *list = (struct reflist *)cb_data;
334 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
335
336 if (info)
337 cgit_add_ref(list, info);
338 return 0;
339}
340
294void cgit_diff_tree_cb(struct diff_queue_struct *q, 341void cgit_diff_tree_cb(struct diff_queue_struct *q,
295 struct diff_options *options, void *data) 342 struct diff_options *options, void *data)
296{ 343{
297 int i; 344 int i;
298 345
299 for (i = 0; i < q->nr; i++) { 346 for (i = 0; i < q->nr; i++) {
300 if (q->queue[i]->status == 'U') 347 if (q->queue[i]->status == 'U')
301 continue; 348 continue;
302 ((filepair_fn)data)(q->queue[i]); 349 ((filepair_fn)data)(q->queue[i]);
303 } 350 }
304} 351}
305 352
306static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 353static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
307{ 354{
308 enum object_type type; 355 enum object_type type;
309 356
310 if (is_null_sha1(sha1)) { 357 if (is_null_sha1(sha1)) {
311 file->ptr = (char *)""; 358 file->ptr = (char *)"";
312 file->size = 0; 359 file->size = 0;
313 } else { 360 } else {
314 file->ptr = read_sha1_file(sha1, &type, 361 file->ptr = read_sha1_file(sha1, &type,
315 (unsigned long *)&file->size); 362 (unsigned long *)&file->size);
316 } 363 }
317 return 1; 364 return 1;
318} 365}
319 366
320/* 367/*
321 * Receive diff-buffers from xdiff and concatenate them as 368 * Receive diff-buffers from xdiff and concatenate them as
322 * needed across multiple callbacks. 369 * needed across multiple callbacks.
323 * 370 *
324 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 371 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
325 * ripped from git and modified to use globals instead of 372 * ripped from git and modified to use globals instead of
326 * a special callback-struct. 373 * a special callback-struct.
327 */ 374 */
328char *diffbuf = NULL; 375char *diffbuf = NULL;
329int buflen = 0; 376int buflen = 0;
330 377
331int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 378int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
332{ 379{
333 int i; 380 int i;
334 381
335 for (i = 0; i < nbuf; i++) { 382 for (i = 0; i < nbuf; i++) {
336 if (mb[i].ptr[mb[i].size-1] != '\n') { 383 if (mb[i].ptr[mb[i].size-1] != '\n') {
337 /* Incomplete line */ 384 /* Incomplete line */
338 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 385 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
339 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 386 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
340 buflen += mb[i].size; 387 buflen += mb[i].size;
341 continue; 388 continue;
342 } 389 }
343 390
344 /* we have a complete line */ 391 /* we have a complete line */
345 if (!diffbuf) { 392 if (!diffbuf) {
346 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 393 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
347 continue; 394 continue;
348 } 395 }
349 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 396 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
350 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 397 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
351 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 398 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
352 free(diffbuf); 399 free(diffbuf);
353 diffbuf = NULL; 400 diffbuf = NULL;
354 buflen = 0; 401 buflen = 0;
355 } 402 }
356 if (diffbuf) { 403 if (diffbuf) {
357 ((linediff_fn)priv)(diffbuf, buflen); 404 ((linediff_fn)priv)(diffbuf, buflen);
358 free(diffbuf); 405 free(diffbuf);
359 diffbuf = NULL; 406 diffbuf = NULL;
360 buflen = 0; 407 buflen = 0;
361 } 408 }
362 return 0; 409 return 0;
363} 410}
364 411
365int cgit_diff_files(const unsigned char *old_sha1, 412int cgit_diff_files(const unsigned char *old_sha1,
366 const unsigned char *new_sha1, 413 const unsigned char *new_sha1,
367 linediff_fn fn) 414 linediff_fn fn)
368{ 415{
369 mmfile_t file1, file2; 416 mmfile_t file1, file2;
370 xpparam_t diff_params; 417 xpparam_t diff_params;
371 xdemitconf_t emit_params; 418 xdemitconf_t emit_params;
372 xdemitcb_t emit_cb; 419 xdemitcb_t emit_cb;
373 420
374 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 421 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
375 return 1; 422 return 1;
376 423
377 diff_params.flags = XDF_NEED_MINIMAL; 424 diff_params.flags = XDF_NEED_MINIMAL;
378 emit_params.ctxlen = 3; 425 emit_params.ctxlen = 3;
379 emit_params.flags = XDL_EMIT_FUNCNAMES; 426 emit_params.flags = XDL_EMIT_FUNCNAMES;
380 emit_params.find_func = NULL; 427 emit_params.find_func = NULL;
381 emit_cb.outf = filediff_cb; 428 emit_cb.outf = filediff_cb;
382 emit_cb.priv = fn; 429 emit_cb.priv = fn;
383 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 430 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
384 return 0; 431 return 0;
385} 432}
386 433
387void cgit_diff_tree(const unsigned char *old_sha1, 434void cgit_diff_tree(const unsigned char *old_sha1,
388 const unsigned char *new_sha1, 435 const unsigned char *new_sha1,
389 filepair_fn fn, const char *prefix) 436 filepair_fn fn, const char *prefix)
390{ 437{
391 struct diff_options opt; 438 struct diff_options opt;
392 int ret; 439 int ret;
393 int prefixlen; 440 int prefixlen;
394 441
395 diff_setup(&opt); 442 diff_setup(&opt);
396 opt.output_format = DIFF_FORMAT_CALLBACK; 443 opt.output_format = DIFF_FORMAT_CALLBACK;
397 opt.detect_rename = 1; 444 opt.detect_rename = 1;
398 opt.rename_limit = cgit_renamelimit; 445 opt.rename_limit = cgit_renamelimit;
399 opt.recursive = 1; 446 opt.recursive = 1;
400 opt.format_callback = cgit_diff_tree_cb; 447 opt.format_callback = cgit_diff_tree_cb;
401 opt.format_callback_data = fn; 448 opt.format_callback_data = fn;
402 if (prefix) { 449 if (prefix) {
403 opt.nr_paths = 1; 450 opt.nr_paths = 1;
404 opt.paths = &prefix; 451 opt.paths = &prefix;
405 prefixlen = strlen(prefix); 452 prefixlen = strlen(prefix);
406 opt.pathlens = &prefixlen; 453 opt.pathlens = &prefixlen;
407 } 454 }
408 diff_setup_done(&opt); 455 diff_setup_done(&opt);
409 456
410 if (old_sha1 && !is_null_sha1(old_sha1)) 457 if (old_sha1 && !is_null_sha1(old_sha1))
411 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 458 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
412 else 459 else
413 ret = diff_root_tree_sha1(new_sha1, "", &opt); 460 ret = diff_root_tree_sha1(new_sha1, "", &opt);
414 diffcore_std(&opt); 461 diffcore_std(&opt);
415 diff_flush(&opt); 462 diff_flush(&opt);
416} 463}
417 464
418void cgit_diff_commit(struct commit *commit, filepair_fn fn) 465void cgit_diff_commit(struct commit *commit, filepair_fn fn)
419{ 466{
420 unsigned char *old_sha1 = NULL; 467 unsigned char *old_sha1 = NULL;
421 468
422 if (commit->parents) 469 if (commit->parents)
423 old_sha1 = commit->parents->item->object.sha1; 470 old_sha1 = commit->parents->item->object.sha1;
424 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); 471 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
425} 472}
diff --git a/ui-refs.c b/ui-refs.c
new file mode 100644
index 0000000..295f5ba
--- a/dev/null
+++ b/ui-refs.c
@@ -0,0 +1,30 @@
1/* ui-refs.c: browse symbolic refs
2 *
3 * Copyright (C) 2006 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
12
13
14void cgit_print_refs()
15{
16
17 html("<table class='list nowrap'>");
18
19 if (cgit_query_path && !strncmp(cgit_query_path, "heads", 5))
20 cgit_print_branches(0);
21 else if (cgit_query_path && !strncmp(cgit_query_path, "tags", 4))
22 cgit_print_tags(0);
23 else {
24 cgit_print_branches(0);
25 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
26 cgit_print_tags(0);
27 }
28
29 html("</table>");
30}
diff --git a/ui-shared.c b/ui-shared.c
index 5c5bcf3..e4bb98f 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,433 +1,439 @@
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_fileurl(const char *reponame, const char *pagename, 60char *cgit_fileurl(const char *reponame, const char *pagename,
61 const char *filename, const char *query) 61 const char *filename, 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?%s", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
66 pagename, filename?filename:"", query); 66 pagename, filename?filename:"", 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_pageurl(const char *reponame, const char *pagename, 78char *cgit_pageurl(const char *reponame, const char *pagename,
79 const char *query) 79 const char *query)
80{ 80{
81 return cgit_fileurl(reponame,pagename,0,query); 81 return cgit_fileurl(reponame,pagename,0,query);
82} 82}
83 83
84const char *cgit_repobasename(const char *reponame) 84const char *cgit_repobasename(const char *reponame)
85{ 85{
86 /* I assume we don't need to store more than one repo basename */ 86 /* I assume we don't need to store more than one repo basename */
87 static char rvbuf[1024]; 87 static char rvbuf[1024];
88 int p; 88 int p;
89 const char *rv; 89 const char *rv;
90 strncpy(rvbuf,reponame,sizeof(rvbuf)); 90 strncpy(rvbuf,reponame,sizeof(rvbuf));
91 if(rvbuf[sizeof(rvbuf)-1]) 91 if(rvbuf[sizeof(rvbuf)-1])
92 die("cgit_repobasename: truncated repository name '%s'", reponame); 92 die("cgit_repobasename: truncated repository name '%s'", reponame);
93 p = strlen(rvbuf)-1; 93 p = strlen(rvbuf)-1;
94 /* strip trailing slashes */ 94 /* strip trailing slashes */
95 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 95 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
96 /* strip trailing .git */ 96 /* strip trailing .git */
97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
98 p -= 3; rvbuf[p--] = 0; 98 p -= 3; rvbuf[p--] = 0;
99 } 99 }
100 /* strip more trailing slashes if any */ 100 /* strip more trailing slashes if any */
101 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 101 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
102 /* find last slash in the remaining string */ 102 /* find last slash in the remaining string */
103 rv = strrchr(rvbuf,'/'); 103 rv = strrchr(rvbuf,'/');
104 if(rv) 104 if(rv)
105 return ++rv; 105 return ++rv;
106 return rvbuf; 106 return rvbuf;
107} 107}
108 108
109char *cgit_currurl() 109char *cgit_currurl()
110{ 110{
111 if (!cgit_virtual_root) 111 if (!cgit_virtual_root)
112 return cgit_script_name; 112 return cgit_script_name;
113 else if (cgit_query_page) 113 else if (cgit_query_page)
114 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page); 114 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page);
115 else if (cgit_query_repo) 115 else if (cgit_query_repo)
116 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); 116 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
117 else 117 else
118 return fmt("%s/", cgit_virtual_root); 118 return fmt("%s/", cgit_virtual_root);
119} 119}
120 120
121static char *repolink(char *title, char *class, char *page, char *head, 121static char *repolink(char *title, char *class, char *page, char *head,
122 char *path) 122 char *path)
123{ 123{
124 char *delim = "?"; 124 char *delim = "?";
125 125
126 html("<a"); 126 html("<a");
127 if (title) { 127 if (title) {
128 html(" title='"); 128 html(" title='");
129 html_attr(title); 129 html_attr(title);
130 html("'"); 130 html("'");
131 } 131 }
132 if (class) { 132 if (class) {
133 html(" class='"); 133 html(" class='");
134 html_attr(class); 134 html_attr(class);
135 html("'"); 135 html("'");
136 } 136 }
137 html(" href='"); 137 html(" href='");
138 if (cgit_virtual_root) { 138 if (cgit_virtual_root) {
139 html_attr(cgit_virtual_root); 139 html_attr(cgit_virtual_root);
140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/') 140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
141 html("/"); 141 html("/");
142 html_attr(cgit_repo->url); 142 html_attr(cgit_repo->url);
143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
144 html("/"); 144 html("/");
145 if (page) { 145 if (page) {
146 html(page); 146 html(page);
147 html("/"); 147 html("/");
148 if (path) 148 if (path)
149 html_attr(path); 149 html_attr(path);
150 } 150 }
151 } else { 151 } else {
152 html(cgit_script_name); 152 html(cgit_script_name);
153 html("?url="); 153 html("?url=");
154 html_attr(cgit_repo->url); 154 html_attr(cgit_repo->url);
155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
156 html("/"); 156 html("/");
157 if (page) { 157 if (page) {
158 html(page); 158 html(page);
159 html("/"); 159 html("/");
160 if (path) 160 if (path)
161 html_attr(path); 161 html_attr(path);
162 } 162 }
163 delim = "&amp;"; 163 delim = "&amp;";
164 } 164 }
165 if (head && strcmp(head, cgit_repo->defbranch)) { 165 if (head && strcmp(head, cgit_repo->defbranch)) {
166 html(delim); 166 html(delim);
167 html("h="); 167 html("h=");
168 html_attr(head); 168 html_attr(head);
169 delim = "&amp;"; 169 delim = "&amp;";
170 } 170 }
171 return fmt("%s", delim); 171 return fmt("%s", delim);
172} 172}
173 173
174static void reporevlink(char *page, char *name, char *title, char *class, 174static void reporevlink(char *page, char *name, char *title, char *class,
175 char *head, char *rev, char *path) 175 char *head, char *rev, char *path)
176{ 176{
177 char *delim; 177 char *delim;
178 178
179 delim = repolink(title, class, page, head, path); 179 delim = repolink(title, class, page, head, path);
180 if (rev && strcmp(rev, cgit_query_head)) { 180 if (rev && strcmp(rev, cgit_query_head)) {
181 html(delim); 181 html(delim);
182 html("id="); 182 html("id=");
183 html_attr(rev); 183 html_attr(rev);
184 } 184 }
185 html("'>"); 185 html("'>");
186 html_txt(name); 186 html_txt(name);
187 html("</a>"); 187 html("</a>");
188} 188}
189 189
190void cgit_tree_link(char *name, char *title, char *class, char *head, 190void cgit_tree_link(char *name, char *title, char *class, char *head,
191 char *rev, char *path) 191 char *rev, char *path)
192{ 192{
193 reporevlink("tree", name, title, class, head, rev, path); 193 reporevlink("tree", name, title, class, head, rev, path);
194} 194}
195 195
196void cgit_log_link(char *name, char *title, char *class, char *head, 196void cgit_log_link(char *name, char *title, char *class, char *head,
197 char *rev, char *path, int ofs) 197 char *rev, char *path, int ofs)
198{ 198{
199 char *delim; 199 char *delim;
200 200
201 delim = repolink(title, class, "log", head, path); 201 delim = repolink(title, class, "log", head, path);
202 if (rev && strcmp(rev, cgit_query_head)) { 202 if (rev && strcmp(rev, cgit_query_head)) {
203 html(delim); 203 html(delim);
204 html("id="); 204 html("id=");
205 html_attr(rev); 205 html_attr(rev);
206 delim = "&"; 206 delim = "&";
207 } 207 }
208 if (ofs > 0) { 208 if (ofs > 0) {
209 html(delim); 209 html(delim);
210 html("ofs="); 210 html("ofs=");
211 htmlf("%d", ofs); 211 htmlf("%d", ofs);
212 } 212 }
213 html("'>"); 213 html("'>");
214 html_txt(name); 214 html_txt(name);
215 html("</a>"); 215 html("</a>");
216} 216}
217 217
218void cgit_commit_link(char *name, char *title, char *class, char *head, 218void cgit_commit_link(char *name, char *title, char *class, char *head,
219 char *rev) 219 char *rev)
220{ 220{
221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
222 name[cgit_max_msg_len] = '\0'; 222 name[cgit_max_msg_len] = '\0';
223 name[cgit_max_msg_len - 1] = '.'; 223 name[cgit_max_msg_len - 1] = '.';
224 name[cgit_max_msg_len - 2] = '.'; 224 name[cgit_max_msg_len - 2] = '.';
225 name[cgit_max_msg_len - 3] = '.'; 225 name[cgit_max_msg_len - 3] = '.';
226 } 226 }
227 reporevlink("commit", name, title, class, head, rev, NULL); 227 reporevlink("commit", name, title, class, head, rev, NULL);
228} 228}
229 229
230void cgit_refs_link(char *name, char *title, char *class, char *head,
231 char *rev, char *path)
232{
233 reporevlink("refs", name, title, class, head, rev, path);
234}
235
230void cgit_snapshot_link(char *name, char *title, char *class, char *head, 236void cgit_snapshot_link(char *name, char *title, char *class, char *head,
231 char *rev, char *archivename) 237 char *rev, char *archivename)
232{ 238{
233 reporevlink("snapshot", name, title, class, head, rev, archivename); 239 reporevlink("snapshot", name, title, class, head, rev, archivename);
234} 240}
235 241
236void cgit_diff_link(char *name, char *title, char *class, char *head, 242void cgit_diff_link(char *name, char *title, char *class, char *head,
237 char *new_rev, char *old_rev, char *path) 243 char *new_rev, char *old_rev, char *path)
238{ 244{
239 char *delim; 245 char *delim;
240 246
241 delim = repolink(title, class, "diff", head, path); 247 delim = repolink(title, class, "diff", head, path);
242 if (new_rev && strcmp(new_rev, cgit_query_head)) { 248 if (new_rev && strcmp(new_rev, cgit_query_head)) {
243 html(delim); 249 html(delim);
244 html("id="); 250 html("id=");
245 html_attr(new_rev); 251 html_attr(new_rev);
246 delim = "&amp;"; 252 delim = "&amp;";
247 } 253 }
248 if (old_rev) { 254 if (old_rev) {
249 html(delim); 255 html(delim);
250 html("id2="); 256 html("id2=");
251 html_attr(old_rev); 257 html_attr(old_rev);
252 } 258 }
253 html("'>"); 259 html("'>");
254 html_txt(name); 260 html_txt(name);
255 html("</a>"); 261 html("</a>");
256} 262}
257 263
258void cgit_object_link(struct object *obj) 264void cgit_object_link(struct object *obj)
259{ 265{
260 char *page, *arg, *url; 266 char *page, *arg, *url;
261 267
262 if (obj->type == OBJ_COMMIT) { 268 if (obj->type == OBJ_COMMIT) {
263 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 269 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
264 cgit_query_head, sha1_to_hex(obj->sha1)); 270 cgit_query_head, sha1_to_hex(obj->sha1));
265 return; 271 return;
266 } else if (obj->type == OBJ_TREE) { 272 } else if (obj->type == OBJ_TREE) {
267 page = "tree"; 273 page = "tree";
268 arg = "id"; 274 arg = "id";
269 } else { 275 } else {
270 page = "blob"; 276 page = "blob";
271 arg = "id"; 277 arg = "id";
272 } 278 }
273 279
274 url = cgit_pageurl(cgit_query_repo, page, 280 url = cgit_pageurl(cgit_query_repo, page,
275 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 281 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
276 html_link_open(url, NULL, NULL); 282 html_link_open(url, NULL, NULL);
277 htmlf("%s %s", typename(obj->type), 283 htmlf("%s %s", typename(obj->type),
278 sha1_to_hex(obj->sha1)); 284 sha1_to_hex(obj->sha1));
279 html_link_close(); 285 html_link_close();
280} 286}
281 287
282void cgit_print_date(time_t secs, char *format) 288void cgit_print_date(time_t secs, char *format)
283{ 289{
284 char buf[64]; 290 char buf[64];
285 struct tm *time; 291 struct tm *time;
286 292
287 time = gmtime(&secs); 293 time = gmtime(&secs);
288 strftime(buf, sizeof(buf)-1, format, time); 294 strftime(buf, sizeof(buf)-1, format, time);
289 html_txt(buf); 295 html_txt(buf);
290} 296}
291 297
292void cgit_print_age(time_t t, time_t max_relative, char *format) 298void cgit_print_age(time_t t, time_t max_relative, char *format)
293{ 299{
294 time_t now, secs; 300 time_t now, secs;
295 301
296 time(&now); 302 time(&now);
297 secs = now - t; 303 secs = now - t;
298 304
299 if (secs > max_relative && max_relative >= 0) { 305 if (secs > max_relative && max_relative >= 0) {
300 cgit_print_date(t, format); 306 cgit_print_date(t, format);
301 return; 307 return;
302 } 308 }
303 309
304 if (secs < TM_HOUR * 2) { 310 if (secs < TM_HOUR * 2) {
305 htmlf("<span class='age-mins'>%.0f min.</span>", 311 htmlf("<span class='age-mins'>%.0f min.</span>",
306 secs * 1.0 / TM_MIN); 312 secs * 1.0 / TM_MIN);
307 return; 313 return;
308 } 314 }
309 if (secs < TM_DAY * 2) { 315 if (secs < TM_DAY * 2) {
310 htmlf("<span class='age-hours'>%.0f hours</span>", 316 htmlf("<span class='age-hours'>%.0f hours</span>",
311 secs * 1.0 / TM_HOUR); 317 secs * 1.0 / TM_HOUR);
312 return; 318 return;
313 } 319 }
314 if (secs < TM_WEEK * 2) { 320 if (secs < TM_WEEK * 2) {
315 htmlf("<span class='age-days'>%.0f days</span>", 321 htmlf("<span class='age-days'>%.0f days</span>",
316 secs * 1.0 / TM_DAY); 322 secs * 1.0 / TM_DAY);
317 return; 323 return;
318 } 324 }
319 if (secs < TM_MONTH * 2) { 325 if (secs < TM_MONTH * 2) {
320 htmlf("<span class='age-weeks'>%.0f weeks</span>", 326 htmlf("<span class='age-weeks'>%.0f weeks</span>",
321 secs * 1.0 / TM_WEEK); 327 secs * 1.0 / TM_WEEK);
322 return; 328 return;
323 } 329 }
324 if (secs < TM_YEAR * 2) { 330 if (secs < TM_YEAR * 2) {
325 htmlf("<span class='age-months'>%.0f months</span>", 331 htmlf("<span class='age-months'>%.0f months</span>",
326 secs * 1.0 / TM_MONTH); 332 secs * 1.0 / TM_MONTH);
327 return; 333 return;
328 } 334 }
329 htmlf("<span class='age-years'>%.0f years</span>", 335 htmlf("<span class='age-years'>%.0f years</span>",
330 secs * 1.0 / TM_YEAR); 336 secs * 1.0 / TM_YEAR);
331} 337}
332 338
333void cgit_print_docstart(char *title, struct cacheitem *item) 339void cgit_print_docstart(char *title, struct cacheitem *item)
334{ 340{
335 html("Content-Type: text/html; charset=utf-8\n"); 341 html("Content-Type: text/html; charset=utf-8\n");
336 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 342 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
337 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 343 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
338 ttl_seconds(item->ttl))); 344 ttl_seconds(item->ttl)));
339 html("\n"); 345 html("\n");
340 html(cgit_doctype); 346 html(cgit_doctype);
341 html("<html>\n"); 347 html("<html>\n");
342 html("<head>\n"); 348 html("<head>\n");
343 html("<title>"); 349 html("<title>");
344 html_txt(title); 350 html_txt(title);
345 html("</title>\n"); 351 html("</title>\n");
346 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 352 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
347 html("<link rel='stylesheet' type='text/css' href='"); 353 html("<link rel='stylesheet' type='text/css' href='");
348 html_attr(cgit_css); 354 html_attr(cgit_css);
349 html("'/>\n"); 355 html("'/>\n");
350 html("</head>\n"); 356 html("</head>\n");
351 html("<body>\n"); 357 html("<body>\n");
352} 358}
353 359
354void cgit_print_docend() 360void cgit_print_docend()
355{ 361{
356 html("</td></tr></table>"); 362 html("</td></tr></table>");
357 html("</body>\n</html>\n"); 363 html("</body>\n</html>\n");
358} 364}
359 365
360void cgit_print_pageheader(char *title, int show_search) 366void cgit_print_pageheader(char *title, int show_search)
361{ 367{
362 html("<table id='layout'>"); 368 html("<table id='layout'>");
363 html("<tr><td id='header'><a href='"); 369 html("<tr><td id='header'><a href='");
364 html_attr(cgit_rooturl()); 370 html_attr(cgit_rooturl());
365 html("'>"); 371 html("'>");
366 html_txt(cgit_root_title); 372 html_txt(cgit_root_title);
367 html("</a></td><td id='logo'>"); 373 html("</a></td><td id='logo'>");
368 html("<a href='"); 374 html("<a href='");
369 html_attr(cgit_logo_link); 375 html_attr(cgit_logo_link);
370 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 376 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo);
371 html("</td></tr>"); 377 html("</td></tr>");
372 html("<tr><td id='crumb'>"); 378 html("<tr><td id='crumb'>");
373 if (cgit_query_repo) { 379 if (cgit_query_repo) {
374 html_txt(cgit_repo->name); 380 html_txt(cgit_repo->name);
375 html(" ("); 381 html(" (");
376 html_txt(cgit_query_head); 382 html_txt(cgit_query_head);
377 html(") : &nbsp;"); 383 html(") : &nbsp;");
378 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head, 384 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head,
379 NULL, NULL); 385 NULL, NULL);
380 html(" "); 386 html(" ");
381 cgit_log_link("log", NULL, NULL, cgit_query_head, 387 cgit_log_link("log", NULL, NULL, cgit_query_head,
382 cgit_query_sha1, cgit_query_path, 0); 388 cgit_query_sha1, cgit_query_path, 0);
383 html(" "); 389 html(" ");
384 cgit_tree_link("tree", NULL, NULL, cgit_query_head, 390 cgit_tree_link("tree", NULL, NULL, cgit_query_head,
385 cgit_query_sha1, NULL); 391 cgit_query_sha1, NULL);
386 html(" "); 392 html(" ");
387 cgit_commit_link("commit", NULL, NULL, cgit_query_head, 393 cgit_commit_link("commit", NULL, NULL, cgit_query_head,
388 cgit_query_sha1); 394 cgit_query_sha1);
389 html(" "); 395 html(" ");
390 cgit_diff_link("diff", NULL, NULL, cgit_query_head, 396 cgit_diff_link("diff", NULL, NULL, cgit_query_head,
391 cgit_query_sha1, cgit_query_sha2, 397 cgit_query_sha1, cgit_query_sha2,
392 cgit_query_path); 398 cgit_query_path);
393 } else { 399 } else {
394 html_txt("Index of repositories"); 400 html_txt("Index of repositories");
395 } 401 }
396 html("</td>"); 402 html("</td>");
397 html("<td id='search'>"); 403 html("<td id='search'>");
398 if (show_search) { 404 if (show_search) {
399 html("<form method='get' action='"); 405 html("<form method='get' action='");
400 html_attr(cgit_currurl()); 406 html_attr(cgit_currurl());
401 html("'>"); 407 html("'>");
402 if (!cgit_virtual_root) { 408 if (!cgit_virtual_root) {
403 if (cgit_query_repo) 409 if (cgit_query_repo)
404 html_hidden("r", cgit_query_repo); 410 html_hidden("r", cgit_query_repo);
405 if (cgit_query_page) 411 if (cgit_query_page)
406 html_hidden("p", cgit_query_page); 412 html_hidden("p", cgit_query_page);
407 } 413 }
408 if (cgit_query_head) 414 if (cgit_query_head)
409 html_hidden("h", cgit_query_head); 415 html_hidden("h", cgit_query_head);
410 if (cgit_query_sha1) 416 if (cgit_query_sha1)
411 html_hidden("id", cgit_query_sha1); 417 html_hidden("id", cgit_query_sha1);
412 if (cgit_query_sha2) 418 if (cgit_query_sha2)
413 html_hidden("id2", cgit_query_sha2); 419 html_hidden("id2", cgit_query_sha2);
414 html("<input type='text' name='q' value='"); 420 html("<input type='text' name='q' value='");
415 html_attr(cgit_query_search); 421 html_attr(cgit_query_search);
416 html("'/></form>"); 422 html("'/></form>");
417 } 423 }
418 html("</td></tr>"); 424 html("</td></tr>");
419 html("<tr><td id='content' colspan='2'>"); 425 html("<tr><td id='content' colspan='2'>");
420} 426}
421 427
422void cgit_print_snapshot_start(const char *mimetype, const char *filename, 428void cgit_print_snapshot_start(const char *mimetype, const char *filename,
423 struct cacheitem *item) 429 struct cacheitem *item)
424{ 430{
425 htmlf("Content-Type: %s\n", mimetype); 431 htmlf("Content-Type: %s\n", mimetype);
426 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 432 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
427 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 433 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
428 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 434 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
429 ttl_seconds(item->ttl))); 435 ttl_seconds(item->ttl)));
430 html("\n"); 436 html("\n");
431} 437}
432 438
433/* vim:set sw=8: */ 439/* vim:set sw=8: */
diff --git a/ui-summary.c b/ui-summary.c
index de8a180..016fea2 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -1,189 +1,258 @@
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 cmp_age(int age1, int age2)
14 int flags, void *cb_data) 14{
15 if (age1 != 0 && age2 != 0)
16 return age2 - age1;
17
18 if (age1 == 0 && age2 == 0)
19 return 0;
20
21 if (age1 == 0)
22 return +1;
23
24 return -1;
25}
26
27static int cmp_ref_name(const void *a, const void *b)
28{
29 struct refinfo *r1 = *(struct refinfo **)a;
30 struct refinfo *r2 = *(struct refinfo **)b;
31
32 return strcmp(r1->refname, r2->refname);
33}
34
35static int cmp_branch_age(const void *a, const void *b)
36{
37 struct refinfo *r1 = *(struct refinfo **)a;
38 struct refinfo *r2 = *(struct refinfo **)b;
39
40 return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
41}
42
43static int cmp_tag_age(const void *a, const void *b)
44{
45 struct refinfo *r1 = *(struct refinfo **)a;
46 struct refinfo *r2 = *(struct refinfo **)b;
47
48 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
49}
50
51static void cgit_print_branch(struct refinfo *ref)
15{ 52{
16 struct commit *commit; 53 struct commit *commit;
17 struct commitinfo *info; 54 struct commitinfo *info;
18 char buf[256]; 55 char *name = (char *)ref->refname;
19 char *ref;
20 56
21 ref = xstrdup(refname); 57 commit = lookup_commit(ref->object->sha1);
22 strncpy(buf, refname, sizeof(buf));
23 commit = lookup_commit(sha1);
24 // object is not really parsed at this point, because of some fallout 58 // object is not really parsed at this point, because of some fallout
25 // from previous calls to git functions in cgit_print_log() 59 // from previous calls to git functions in cgit_print_log()
26 commit->object.parsed = 0; 60 commit->object.parsed = 0;
27 if (commit && !parse_commit(commit)){ 61 if (commit && !parse_commit(commit)){
28 info = cgit_parse_commit(commit); 62 info = cgit_parse_commit(commit);
29 html("<tr><td>"); 63 html("<tr><td>");
30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0); 64 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0);
31 html("</td><td>"); 65 html("</td><td>");
32 cgit_print_age(commit->date, -1, NULL); 66 cgit_print_age(commit->date, -1, NULL);
33 html("</td><td>"); 67 html("</td><td>");
34 html_txt(info->author); 68 html_txt(info->author);
35 html("</td><td>"); 69 html("</td><td>");
36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL); 70 cgit_commit_link(info->subject, NULL, NULL, name, NULL);
37 html("</td></tr>\n"); 71 html("</td></tr>\n");
38 cgit_free_commitinfo(info); 72 cgit_free_commitinfo(info);
39 } else { 73 } else {
40 html("<tr><td>"); 74 html("<tr><td>");
41 html_txt(buf); 75 html_txt(name);
42 html("</td><td colspan='3'>"); 76 html("</td><td colspan='3'>");
43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1)); 77 htmlf("*** bad ref %s ***", sha1_to_hex(ref->object->sha1));
44 html("</td></tr>\n"); 78 html("</td></tr>\n");
45 } 79 }
46 free(ref);
47 return 0;
48} 80}
49 81
50static void print_tag_header() 82static void print_tag_header()
51{ 83{
52 html("<tr class='nohover'><th class='left'>Tag</th>" 84 html("<tr class='nohover'><th class='left'>Tag</th>"
53 "<th class='left'>Age</th>" 85 "<th class='left'>Age</th>"
54 "<th class='left'>Author</th>" 86 "<th class='left'>Author</th>"
55 "<th class='left'>Reference</th></tr>\n"); 87 "<th class='left'>Reference</th></tr>\n");
56 header = 1; 88 header = 1;
57} 89}
58 90
59static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1, 91static int print_tag(struct refinfo *ref)
60 int flags, void *cb_data)
61{ 92{
62 struct tag *tag; 93 struct tag *tag;
63 struct taginfo *info; 94 struct taginfo *info;
64 struct object *obj; 95 char *url, *name = (char *)ref->refname;
65 char buf[256], *url;
66 96
67 strncpy(buf, refname, sizeof(buf)); 97 if (ref->object->type == OBJ_TAG) {
68 obj = parse_object(sha1); 98 tag = lookup_tag(ref->object->sha1);
69 if (!obj)
70 return 1;
71 if (obj->type == OBJ_TAG) {
72 tag = lookup_tag(sha1);
73 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 99 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
74 return 2; 100 return 2;
75 if (!header)
76 print_tag_header();
77 html("<tr><td>"); 101 html("<tr><td>");
78 url = cgit_pageurl(cgit_query_repo, "tag", 102 url = cgit_pageurl(cgit_query_repo, "tag",
79 fmt("id=%s", refname)); 103 fmt("id=%s", name));
80 html_link_open(url, NULL, NULL); 104 html_link_open(url, NULL, NULL);
81 html_txt(buf); 105 html_txt(name);
82 html_link_close(); 106 html_link_close();
83 html("</td><td>"); 107 html("</td><td>");
84 if (info->tagger_date > 0) 108 if (info->tagger_date > 0)
85 cgit_print_age(info->tagger_date, -1, NULL); 109 cgit_print_age(info->tagger_date, -1, NULL);
86 html("</td><td>"); 110 html("</td><td>");
87 if (info->tagger) 111 if (info->tagger)
88 html(info->tagger); 112 html(info->tagger);
89 html("</td><td>"); 113 html("</td><td>");
90 cgit_object_link(tag->tagged); 114 cgit_object_link(tag->tagged);
91 html("</td></tr>\n"); 115 html("</td></tr>\n");
92 } else { 116 } else {
93 if (!header) 117 if (!header)
94 print_tag_header(); 118 print_tag_header();
95 html("<tr><td>"); 119 html("<tr><td>");
96 html_txt(buf); 120 html_txt(name);
97 html("</td><td colspan='2'/><td>"); 121 html("</td><td colspan='2'/><td>");
98 cgit_object_link(obj); 122 cgit_object_link(ref->object);
99 html("</td></tr>\n"); 123 html("</td></tr>\n");
100 } 124 }
101 return 0; 125 return 0;
102} 126}
103 127
104static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1, 128static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
105 int flags, void *cb_data) 129 int flags, void *cb_data)
106{ 130{
107 struct tag *tag; 131 struct tag *tag;
108 struct taginfo *info; 132 struct taginfo *info;
109 struct object *obj; 133 struct object *obj;
110 char buf[256], *url; 134 char buf[256], *url;
111 unsigned char fileid[20]; 135 unsigned char fileid[20];
112 136
113 if (prefixcmp(refname, "refs/archives")) 137 if (prefixcmp(refname, "refs/archives"))
114 return 0; 138 return 0;
115 strncpy(buf, refname+14, sizeof(buf)); 139 strncpy(buf, refname+14, sizeof(buf));
116 obj = parse_object(sha1); 140 obj = parse_object(sha1);
117 if (!obj) 141 if (!obj)
118 return 1; 142 return 1;
119 if (obj->type == OBJ_TAG) { 143 if (obj->type == OBJ_TAG) {
120 tag = lookup_tag(sha1); 144 tag = lookup_tag(sha1);
121 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 145 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
122 return 0; 146 return 0;
123 hashcpy(fileid, tag->tagged->sha1); 147 hashcpy(fileid, tag->tagged->sha1);
124 } else if (obj->type != OBJ_BLOB) { 148 } else if (obj->type != OBJ_BLOB) {
125 return 0; 149 return 0;
126 } else { 150 } else {
127 hashcpy(fileid, sha1); 151 hashcpy(fileid, sha1);
128 } 152 }
129 if (!header) { 153 if (!header) {
130 html("<table id='downloads'>"); 154 html("<table id='downloads'>");
131 html("<tr><th>Downloads</th></tr>"); 155 html("<tr><th>Downloads</th></tr>");
132 header = 1; 156 header = 1;
133 } 157 }
134 html("<tr><td>"); 158 html("<tr><td>");
135 url = cgit_pageurl(cgit_query_repo, "blob", 159 url = cgit_pageurl(cgit_query_repo, "blob",
136 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 160 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
137 buf)); 161 buf));
138 html_link_open(url, NULL, NULL); 162 html_link_open(url, NULL, NULL);
139 html_txt(buf); 163 html_txt(buf);
140 html_link_close(); 164 html_link_close();
141 html("</td></tr>"); 165 html("</td></tr>");
142 return 0; 166 return 0;
143} 167}
144 168
145static void cgit_print_branches() 169static void print_refs_link(char *path)
146{ 170{
171 html("<tr class='nohover'><td colspan='4'>");
172 cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path);
173 html("</td></tr>");
174}
175
176void cgit_print_branches(int maxcount)
177{
178 struct reflist list;
179 int i;
180
147 html("<tr class='nohover'><th class='left'>Branch</th>" 181 html("<tr class='nohover'><th class='left'>Branch</th>"
148 "<th class='left'>Idle</th>" 182 "<th class='left'>Idle</th>"
149 "<th class='left'>Author</th>" 183 "<th class='left'>Author</th>"
150 "<th class='left'>Head commit</th></tr>\n"); 184 "<th class='left'>Head commit</th></tr>\n");
151 for_each_branch_ref(cgit_print_branch_cb, NULL); 185
186 list.refs = NULL;
187 list.alloc = list.count = 0;
188 for_each_branch_ref(cgit_refs_cb, &list);
189
190 if (maxcount == 0 || maxcount > list.count)
191 maxcount = list.count;
192
193 if (maxcount < list.count) {
194 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
195 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
196 }
197
198 for(i=0; i<maxcount; i++)
199 cgit_print_branch(list.refs[i]);
200
201 if (maxcount < list.count)
202 print_refs_link("heads");
152} 203}
153 204
154static void cgit_print_tags() 205void cgit_print_tags(int maxcount)
155{ 206{
207 struct reflist list;
208 int i;
209
156 header = 0; 210 header = 0;
157 for_each_tag_ref(cgit_print_tag_cb, NULL); 211 list.refs = NULL;
212 list.alloc = list.count = 0;
213 for_each_tag_ref(cgit_refs_cb, &list);
214 if (list.count == 0)
215 return;
216 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
217 if (!maxcount)
218 maxcount = list.count;
219 else if (maxcount > list.count)
220 maxcount = list.count;
221 print_tag_header();
222 for(i=0; i<maxcount; i++)
223 print_tag(list.refs[i]);
224
225 if (maxcount < list.count)
226 print_refs_link("tags");
158} 227}
159 228
160static void cgit_print_archives() 229static void cgit_print_archives()
161{ 230{
162 header = 0; 231 header = 0;
163 for_each_ref(cgit_print_archive_cb, NULL); 232 for_each_ref(cgit_print_archive_cb, NULL);
164 if (header) 233 if (header)
165 html("</table>"); 234 html("</table>");
166} 235}
167 236
168void cgit_print_summary() 237void cgit_print_summary()
169{ 238{
170 html("<div id='summary'>"); 239 html("<div id='summary'>");
171 cgit_print_archives(); 240 cgit_print_archives();
172 html("<h2>"); 241 html("<h2>");
173 html_txt(cgit_repo->name); 242 html_txt(cgit_repo->name);
174 html(" - "); 243 html(" - ");
175 html_txt(cgit_repo->desc); 244 html_txt(cgit_repo->desc);
176 html("</h2>"); 245 html("</h2>");
177 if (cgit_repo->readme) 246 if (cgit_repo->readme)
178 html_include(cgit_repo->readme); 247 html_include(cgit_repo->readme);
179 html("</div>"); 248 html("</div>");
180 if (cgit_summary_log > 0) 249 if (cgit_summary_log > 0)
181 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0); 250 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0);
182 html("<table class='list nowrap'>"); 251 html("<table class='list nowrap'>");
183 if (cgit_summary_log > 0) 252 if (cgit_summary_log > 0)
184 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 253 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
185 cgit_print_branches(); 254 cgit_print_branches(cgit_summary_branches);
186 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 255 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
187 cgit_print_tags(); 256 cgit_print_tags(cgit_summary_tags);
188 html("</table>"); 257 html("</table>");
189} 258}