aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules8
-rw-r--r--Makefile102
-rw-r--r--cgit.c36
-rw-r--r--cgit.css75
-rw-r--r--cgit.h47
-rw-r--r--cgitrc9
-rwxr-xr-xgen-version.sh20
m---------git0
-rw-r--r--parsing.c2
-rw-r--r--shared.c45
-rwxr-xr-xsubmodules.sh181
-rw-r--r--ui-commit.c78
-rw-r--r--ui-diff.c66
-rw-r--r--ui-log.c26
-rw-r--r--ui-repolist.c66
-rw-r--r--ui-shared.c234
-rw-r--r--ui-snapshot.c156
-rw-r--r--ui-summary.c49
-rw-r--r--ui-tag.c74
-rw-r--r--ui-tree.c213
-rw-r--r--ui-view.c55
22 files changed, 929 insertions, 614 deletions
diff --git a/.gitignore b/.gitignore
index c4c9ac3..5664962 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
1# Files I don't care to see in git-status/commit 1# Files I don't care to see in git-status/commit
2cgit 2cgit
3VERSION
3*.o 4*.o
4*~ 5*~
diff --git a/.gitmodules b/.gitmodules
index 51dd1ef..1daea94 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,5 +1,3 @@
1# This file maps a submodule path to an url from where the submodule 1[submodule "git"]
2# can be obtained. The script "submodules.sh" finds the url in this file 2 url = git://git.kernel.org/pub/scm/git/git.git
3# when invoked with -i to clone the submodules. 3 path = git
4
5git git://git.kernel.org/pub/scm/git/git.git
diff --git a/Makefile b/Makefile
index 57f80f8..fcbe3e4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,87 +1,71 @@
1CGIT_VERSION = 0.5 1CGIT_VERSION = v0.5
2
3prefix = /var/www/htdocs/cgit
4
5SHA1_HEADER = <openssl/sha.h>
6CACHE_ROOT = /var/cache/cgit
7CGIT_CONFIG = /etc/cgitrc
8CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_CONFIG = /etc/cgitrc
5CACHE_ROOT = /var/cache/cgit
6SHA1_HEADER = <openssl/sha.h>
7GIT_VER = 1.5.2
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
15EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
16OBJECTS = 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 \
17 ui-summary.o ui-log.o ui-view.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 \
18 ui-snapshot.o ui-blob.o 19 ui-snapshot.o ui-blob.o ui-tag.o
20
21
22.PHONY: all git install clean distclean force-version get-git
19 23
20CFLAGS += -Wall 24all: cgit git
21 25
22ifdef DEBUG 26VERSION: force-version
23 CFLAGS += -g 27 @./gen-version.sh "$(CGIT_VERSION)"
24endif 28-include VERSION
25 29
26CFLAGS += -Igit 30
31CFLAGS += -g -Wall -Igit
27CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 32CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
28CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 33CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
29CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 34CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
30CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 35CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
31 36
32 37
33# 38cgit: cgit.c $(OBJECTS)
34# If make is run on a nongit platform, get the git sources as a tarball.
35#
36GITVER = $(shell git version 2>/dev/null || echo nogit)
37ifeq ($(GITVER),nogit)
38GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2
39INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git)
40else
41INITGIT = ./submodules.sh -i
42endif
43
44
45#
46# basic build rules
47#
48all: cgit
49
50cgit: cgit.c cgit.h $(OBJECTS)
51 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 39 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
52 40
53$(OBJECTS): cgit.h git/libgit.a 41$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
54 42
55git/libgit.a: 43git/xdiff/lib.a: | git
56 $(INITGIT)
57 $(MAKE) -C git
58 44
59# 45git/libgit.a: | git
60# phony targets
61#
62install: all clean-cache
63 mkdir -p $(prefix)
64 install cgit $(prefix)/$(CGIT_SCRIPT_NAME)
65 install cgit.css $(prefix)/cgit.css
66 46
67clean-cgit: 47git:
68 rm -f cgit *.o 48 cd git && $(MAKE) xdiff/lib.a
49 cd git && $(MAKE) libgit.a
69 50
70distclean-cgit: clean-cgit 51install: all
71 git clean -d -x 52 mkdir -p $(CGIT_SCRIPT_PATH)
72 53 install cgit $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
73clean-sub: 54 install cgit.css $(CGIT_SCRIPT_PATH)/cgit.css
74 $(MAKE) -C git clean
75
76distclean-sub: clean-sub
77 $(shell cd git && git clean -d -x)
78
79clean-cache:
80 rm -rf $(CACHE_ROOT)/* 55 rm -rf $(CACHE_ROOT)/*
81 56
82clean: clean-cgit clean-sub 57uninstall:
58 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
59 rm -f $(CGIT_SCRIPT_PATH)/cgit.css
60 rm -rf $(CACHE_ROOT)
61
62clean:
63 rm -f cgit VERSION *.o
64 cd git && $(MAKE) clean
83 65
84distclean: distclean-cgit distclean-sub 66distclean: clean
67 git clean -d -x
68 cd git && git clean -d -x
85 69
86.PHONY: all install clean clean-cgit clean-sub clean-cache \ 70get-git:
87 distclean distclean-cgit distclean-sub 71 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 34e590e..c86d290 100644
--- a/cgit.c
+++ b/cgit.c
@@ -8,9 +8,6 @@
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11const char cgit_version[] = CGIT_VERSION;
12
13
14static int cgit_prepare_cache(struct cacheitem *item) 11static int cgit_prepare_cache(struct cacheitem *item)
15{ 12{
16 if (!cgit_repo && cgit_query_repo) { 13 if (!cgit_repo && cgit_query_repo) {
@@ -29,13 +26,15 @@ static int cgit_prepare_cache(struct cacheitem *item)
29 } 26 }
30 27
31 if (!cgit_cmd) { 28 if (!cgit_cmd) {
32 item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root, 29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
33 cache_safe_filename(cgit_repo->url))); 30 cache_safe_filename(cgit_repo->url),
31 cache_safe_filename(cgit_querystring)));
34 item->ttl = cgit_cache_repo_ttl; 32 item->ttl = cgit_cache_repo_ttl;
35 } else { 33 } else {
36 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,
37 cache_safe_filename(cgit_repo->url), cgit_query_page, 35 cache_safe_filename(cgit_repo->url),
38 cache_safe_filename(cgit_querystring))); 36 cgit_query_page,
37 cache_safe_filename(cgit_querystring)));
39 if (cgit_query_has_symref) 38 if (cgit_query_has_symref)
40 item->ttl = cgit_cache_dynamic_ttl; 39 item->ttl = cgit_cache_dynamic_ttl;
41 else if (cgit_query_has_sha1) 40 else if (cgit_query_has_sha1)
@@ -69,8 +68,10 @@ static void cgit_print_repo_page(struct cacheitem *item)
69 setenv("GIT_DIR", cgit_repo->path, 1); 68 setenv("GIT_DIR", cgit_repo->path, 1);
70 69
71 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
72 cgit_print_snapshot(item, cgit_query_sha1, "zip", 71 cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1,
73 cgit_repo->url, cgit_query_name); 72 cgit_repobasename(cgit_repo->url),
73 cgit_query_path,
74 cgit_repo->snapshots );
74 return; 75 return;
75 } 76 }
76 77
@@ -92,22 +93,21 @@ static void cgit_print_repo_page(struct cacheitem *item)
92 93
93 switch(cgit_cmd) { 94 switch(cgit_cmd) {
94 case CMD_LOG: 95 case CMD_LOG:
95 cgit_print_log(cgit_query_head, cgit_query_ofs, 96 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
96 cgit_max_commit_count, cgit_query_search, 97 cgit_max_commit_count, cgit_query_search,
97 cgit_query_path, 1); 98 cgit_query_path, 1);
98 break; 99 break;
99 case CMD_TREE: 100 case CMD_TREE:
100 cgit_print_tree(cgit_query_head, cgit_query_sha1, cgit_query_path); 101 cgit_print_tree(cgit_query_sha1, cgit_query_path);
101 break; 102 break;
102 case CMD_COMMIT: 103 case CMD_COMMIT:
103 cgit_print_commit(cgit_query_head); 104 cgit_print_commit(cgit_query_sha1);
104 break; 105 break;
105 case CMD_VIEW: 106 case CMD_TAG:
106 cgit_print_view(cgit_query_sha1, cgit_query_path); 107 cgit_print_tag(cgit_query_sha1);
107 break; 108 break;
108 case CMD_DIFF: 109 case CMD_DIFF:
109 cgit_print_diff(cgit_query_head, cgit_query_sha1, cgit_query_sha2, 110 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
110 cgit_query_path);
111 break; 111 break;
112 default: 112 default:
113 cgit_print_error("Invalid request"); 113 cgit_print_error("Invalid request");
@@ -227,6 +227,7 @@ static void cgit_parse_args(int argc, const char **argv)
227int main(int argc, const char **argv) 227int main(int argc, const char **argv)
228{ 228{
229 struct cacheitem item; 229 struct cacheitem item;
230 const char *cgit_config_env = getenv("CGIT_CONFIG");
230 231
231 htmlfd = STDOUT_FILENO; 232 htmlfd = STDOUT_FILENO;
232 item.st.st_mtime = time(NULL); 233 item.st.st_mtime = time(NULL);
@@ -234,7 +235,8 @@ int main(int argc, const char **argv)
234 cgit_repolist.count = 0; 235 cgit_repolist.count = 0;
235 cgit_repolist.repos = NULL; 236 cgit_repolist.repos = NULL;
236 237
237 cgit_read_config(CGIT_CONFIG, cgit_global_config_cb); 238 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
239 cgit_global_config_cb);
238 cgit_repo = NULL; 240 cgit_repo = NULL;
239 if (getenv("SCRIPT_NAME")) 241 if (getenv("SCRIPT_NAME"))
240 cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); 242 cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
diff --git a/cgit.css b/cgit.css
index 8977533..54bbfcc 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,6 +1,7 @@
1body { 1body {
2 font-family: arial; 2 font-family: arial, sans-serif;
3 font-size: 11pt; 3 font-size: 11pt;
4 color: black;
4 background: white; 5 background: white;
5} 6}
6 7
@@ -94,6 +95,14 @@ td#header {
94 vertical-align: text-bottom; 95 vertical-align: text-bottom;
95} 96}
96 97
98td#header a {
99 color: #666;
100}
101
102td#header a:hoved {
103 text-decoration: underline;
104}
105
97td#logo { 106td#logo {
98 text-align: right; 107 text-align: right;
99 vertical-align: middle; 108 vertical-align: middle;
@@ -114,15 +123,19 @@ td#crumb {
114 123
115td#crumb a { 124td#crumb a {
116 color: #ccc; 125 color: #ccc;
126 background-color: #666;
127 padding: 0em 0.5em 0em 0.5em;
117} 128}
118 129
119td#crumb a:hover { 130td#crumb a:hover {
120 color: #eee; 131 color: #666;
132 background-color: #ccc;
133 text-decoration: none;
121} 134}
122 135
123td#search { 136td#search {
124 text-align: right; 137 text-align: right;
125 vertical-align: center; 138 vertical-align: middle;
126 padding-right: 0.5em; 139 padding-right: 0.5em;
127} 140}
128 141
@@ -171,35 +184,47 @@ div.error {
171 margin: 1em 2em; 184 margin: 1em 2em;
172} 185}
173 186
174td.ls-blob, td.ls-dir, td.ls-mod { 187a.ls-blob, a.ls-dir, a.ls-mod {
175 font-family: monospace; 188 font-family: monospace;
176} 189}
177 190
178div.ls-dir a { 191td.ls-size {
179 font-weight: bold; 192 text-align: right;
180} 193}
181 194
182th.filesize, td.filesize { 195td.ls-size {
183 text-align: right; 196 font-family: monospace;
184} 197}
185 198
186td.filesize { 199td.ls-mode {
187 font-family: monospace; 200 font-family: monospace;
188} 201}
189 202
190td.links { 203table.blob {
191 font-size: 80%; 204 margin-top: 0.5em;
192 padding-left: 2em; 205 border-top: solid 1px black;
193} 206}
194 207
195td.filemode { 208table.blob td.no {
196 font-family: monospace; 209 border-right: solid 1px black;
210 color: black;
211 background-color: #eee;
212 text-align: right;
213}
214
215table.blob td.no a {
216 color: black;
197} 217}
198 218
199td.blob { 219table.blob td.no a:hover {
220 color: black;
221 text-decoration: none;
222}
223
224table.blob td.txt {
200 white-space: pre; 225 white-space: pre;
201 font-family: monospace; 226 font-family: monospace;
202 background-color: white; 227 padding-left: 0.5em;
203} 228}
204 229
205table.nowrap td { 230table.nowrap td {
@@ -215,6 +240,7 @@ table.commit-info th {
215 text-align: left; 240 text-align: left;
216 font-weight: normal; 241 font-weight: normal;
217 padding: 0.1em 1em 0.1em 0.1em; 242 padding: 0.1em 1em 0.1em 0.1em;
243 vertical-align: top;
218} 244}
219 245
220table.commit-info td { 246table.commit-info td {
@@ -287,7 +313,7 @@ table.diffstat td.upd a {
287 313
288table.diffstat td.graph { 314table.diffstat td.graph {
289 width: 75%; 315 width: 75%;
290 vertical-align: center; 316 vertical-align: middle;
291} 317}
292 318
293table.diffstat td.graph table { 319table.diffstat td.graph table {
@@ -308,10 +334,6 @@ table.diffstat td.graph td.rem {
308 background-color: #c55; 334 background-color: #c55;
309} 335}
310 336
311table.diffstat td.graph td.none {
312 background-color: none;
313}
314
315div.diffstat-summary { 337div.diffstat-summary {
316 color: #888; 338 color: #888;
317 padding-top: 0.5em; 339 padding-top: 0.5em;
@@ -340,7 +362,7 @@ table.diff td div.del {
340} 362}
341 363
342.sha1 { 364.sha1 {
343 font-family: courier; 365 font-family: monospace;
344 font-size: 90%; 366 font-size: 90%;
345} 367}
346 368
@@ -359,16 +381,17 @@ table.list td.repogroup {
359 381
360a.button { 382a.button {
361 font-size: 80%; 383 font-size: 80%;
362 color: #333; 384 color: #aaa;
363 background-color: #ccc; 385 background-color: #eee;
364 border: solid 1px #999; 386 border: solid 1px #aaa;
365 padding: 0em 0.5em; 387 padding: 0em 0.5em;
366 margin: 0.1em 0.25em; 388 margin: 0.1em 0.25em;
367} 389}
368 390
369a.button:hover { 391a.button:hover {
370 text-decoration: none; 392 text-decoration: none;
371 background-color: #eee; 393 color: #333;
394 background-color: #ccc;
372} 395}
373 396
374a.primary { 397a.primary {
diff --git a/cgit.h b/cgit.h
index 2f3fca1..e3d9cb8 100644
--- a/cgit.h
+++ b/cgit.h
@@ -25,10 +25,9 @@
25#define CMD_COMMIT 2 25#define CMD_COMMIT 2
26#define CMD_DIFF 3 26#define CMD_DIFF 3
27#define CMD_TREE 4 27#define CMD_TREE 4
28#define CMD_VIEW 5 28#define CMD_BLOB 5
29#define CMD_BLOB 6 29#define CMD_SNAPSHOT 6
30#define CMD_SNAPSHOT 7 30#define CMD_TAG 7
31
32 31
33/* 32/*
34 * Dateformats used on misc. pages 33 * Dateformats used on misc. pages
@@ -99,7 +98,7 @@ struct taginfo {
99 char *msg; 98 char *msg;
100}; 99};
101 100
102extern const char cgit_version[]; 101extern const char *cgit_version;
103 102
104extern struct repolist cgit_repolist; 103extern struct repolist cgit_repolist;
105extern struct repoinfo *cgit_repo; 104extern struct repoinfo *cgit_repo;
@@ -119,6 +118,7 @@ extern char *cgit_repo_group;
119 118
120extern int cgit_nocache; 119extern int cgit_nocache;
121extern int cgit_snapshots; 120extern int cgit_snapshots;
121extern int cgit_enable_index_links;
122extern int cgit_enable_log_filecount; 122extern int cgit_enable_log_filecount;
123extern int cgit_enable_log_linecount; 123extern int cgit_enable_log_linecount;
124extern int cgit_max_lock_attempts; 124extern int cgit_max_lock_attempts;
@@ -157,8 +157,10 @@ extern void cgit_querystring_cb(const char *name, const char *value);
157 157
158extern int chk_zero(int result, char *msg); 158extern int chk_zero(int result, char *msg);
159extern int chk_positive(int result, char *msg); 159extern int chk_positive(int result, char *msg);
160extern int chk_non_negative(int result, char *msg);
160 161
161extern int hextoint(char c); 162extern int hextoint(char c);
163extern char *trim_end(const char *str, char c);
162 164
163extern void *cgit_free_commitinfo(struct commitinfo *info); 165extern void *cgit_free_commitinfo(struct commitinfo *info);
164 166
@@ -199,9 +201,26 @@ extern int cache_exist(struct cacheitem *item);
199extern int cache_expired(struct cacheitem *item); 201extern int cache_expired(struct cacheitem *item);
200 202
201extern char *cgit_repourl(const char *reponame); 203extern char *cgit_repourl(const char *reponame);
204extern char *cgit_fileurl(const char *reponame, const char *pagename,
205 const char *filename, const char *query);
202extern char *cgit_pageurl(const char *reponame, const char *pagename, 206extern char *cgit_pageurl(const char *reponame, const char *pagename,
203 const char *query); 207 const char *query);
204 208
209extern const char *cgit_repobasename(const char *reponame);
210
211extern void cgit_tree_link(char *name, char *title, char *class, char *head,
212 char *rev, char *path);
213extern void cgit_log_link(char *name, char *title, char *class, char *head,
214 char *rev, char *path, int ofs);
215extern void cgit_commit_link(char *name, char *title, char *class, char *head,
216 char *rev);
217extern void cgit_snapshot_link(char *name, char *title, char *class,
218 char *head, char *rev, char *archivename);
219extern void cgit_diff_link(char *name, char *title, char *class, char *head,
220 char *new_rev, char *old_rev, char *path);
221
222extern void cgit_object_link(struct object *obj);
223
205extern void cgit_print_error(char *msg); 224extern void cgit_print_error(char *msg);
206extern void cgit_print_date(time_t secs, char *format); 225extern void cgit_print_date(time_t secs, char *format);
207extern void cgit_print_age(time_t t, time_t max_relative, char *format); 226extern void cgit_print_age(time_t t, time_t max_relative, char *format);
@@ -215,14 +234,16 @@ extern void cgit_print_snapshot_start(const char *mimetype,
215extern void cgit_print_repolist(struct cacheitem *item); 234extern void cgit_print_repolist(struct cacheitem *item);
216extern void cgit_print_summary(); 235extern void cgit_print_summary();
217extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 236extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
218extern void cgit_print_view(const char *hex, char *path);
219extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 237extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
220extern void cgit_print_tree(const char *rev, const char *hex, char *path); 238extern void cgit_print_tree(const char *rev, char *path);
221extern void cgit_print_commit(const char *hex); 239extern void cgit_print_commit(char *hex);
222extern void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex, 240extern void cgit_print_tag(char *revname);
223 char *path); 241extern void cgit_print_diff(const char *new_hex, const char *old_hex);
224extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 242extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
225 const char *format, const char *prefix, 243 const char *hex, const char *prefix,
226 const char *filename); 244 const char *filename, int snapshot);
245extern void cgit_print_snapshot_links(const char *repo, const char *head,
246 const char *hex, int snapshots);
247extern int cgit_parse_snapshots_mask(const char *str);
227 248
228#endif /* CGIT_H */ 249#endif /* CGIT_H */
diff --git a/cgitrc b/cgitrc
index 0f602e4..1040997 100644
--- a/cgitrc
+++ b/cgitrc
@@ -8,10 +8,15 @@
8#nocache=0 8#nocache=0
9 9
10 10
11## Enable/disable snapshots by default. This 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#snapshots=0 13#snapshots=0
13 14
14 15
16## Enable/disable extra links to summary/log/tree per repo on index page
17#enable-index-links=0
18
19
15## Enable/disable display of 'number of files changed' in log view 20## Enable/disable display of 'number of files changed' in log view
16#enable-log-filecount=0 21#enable-log-filecount=0
17 22
@@ -109,7 +114,7 @@
109#repo.desc=the caching cgi for git 114#repo.desc=the caching cgi for git
110#repo.path=/pub/git/cgit 115#repo.path=/pub/git/cgit
111#repo.owner=Lars Hjemli 116#repo.owner=Lars Hjemli
112#repo.snapshots=1 # override a sitewide snapshot-setting 117#repo.snapshots=tar.bz2 # override a sitewide snapshot-setting
113#repo.enable-log-filecount=0 # override the default filecount setting 118#repo.enable-log-filecount=0 # override the default filecount setting
114#repo.enable-log-linecount=0 # override the default linecount setting 119#repo.enable-log-linecount=0 # override the default linecount setting
115#repo.module-link=/git/%s/commit/?id=%s # override the standard module-link 120#repo.module-link=/git/%s/commit/?id=%s # override the standard module-link
diff --git a/gen-version.sh b/gen-version.sh
new file mode 100755
index 0000000..739c83e
--- /dev/null
+++ b/gen-version.sh
@@ -0,0 +1,20 @@
1#!/bin/sh
2
3# Get version-info specified in Makefile
4V=$1
5
6# Use `git describe` to get current version if we're inside a git repo
7if test -d .git
8then
9 V=$(git describe --abbrev=4 HEAD 2>/dev/null | sed -e 's/-/./g')
10fi
11
12new="CGIT_VERSION = $V"
13old=$(cat VERSION 2>/dev/null)
14
15# Exit if VERSION is uptodate
16test "$old" = "$new" && exit 0
17
18# Update VERSION with new version-info
19echo "$new" > VERSION
20cat VERSION
diff --git a/git b/git
Subproject aba170cdb4874b72dd619e6f7bbc13c33295f83 Subproject 86bab9615c3516d4ac7756ae3c1285d331b78f0
diff --git a/parsing.c b/parsing.c
index 74a2484..2c05c09 100644
--- a/parsing.c
+++ b/parsing.c
@@ -168,7 +168,7 @@ void cgit_parse_url(const char *url)
168 if (p) { 168 if (p) {
169 p[0] = '\0'; 169 p[0] = '\0';
170 if (p[1]) 170 if (p[1])
171 cgit_query_path = xstrdup(p + 1); 171 cgit_query_path = trim_end(p + 1, '/');
172 } 172 }
173 cgit_cmd = cgit_get_cmd_index(cmd + 1); 173 cgit_cmd = cgit_get_cmd_index(cmd + 1);
174 cgit_query_page = xstrdup(cmd + 1); 174 cgit_query_page = xstrdup(cmd + 1);
diff --git a/shared.c b/shared.c
index b6d2fa1..077934f 100644
--- a/shared.c
+++ b/shared.c
@@ -12,6 +12,8 @@ struct 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;
16
15char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
16char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
17char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
@@ -26,6 +28,7 @@ char *cgit_repo_group = NULL;
26 28
27int cgit_nocache = 0; 29int cgit_nocache = 0;
28int cgit_snapshots = 0; 30int cgit_snapshots = 0;
31int cgit_enable_index_links = 0;
29int cgit_enable_log_filecount = 0; 32int cgit_enable_log_filecount = 0;
30int cgit_enable_log_linecount = 0; 33int cgit_enable_log_linecount = 0;
31int cgit_max_lock_attempts = 5; 34int cgit_max_lock_attempts = 5;
@@ -59,7 +62,8 @@ int htmlfd = 0;
59 62
60int cgit_get_cmd_index(const char *cmd) 63int cgit_get_cmd_index(const char *cmd)
61{ 64{
62 static char *cmds[] = {"log", "commit", "diff", "tree", "view", "blob", "snapshot", NULL}; 65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
66 "snapshot", "tag", NULL};
63 int i; 67 int i;
64 68
65 for(i = 0; cmds[i]; i++) 69 for(i = 0; cmds[i]; i++)
@@ -82,6 +86,13 @@ int chk_positive(int result, char *msg)
82 return result; 86 return result;
83} 87}
84 88
89int chk_non_negative(int result, char *msg)
90{
91 if (result < 0)
92 die("%s: %s",msg, strerror(errno));
93 return result;
94}
95
85struct repoinfo *add_repo(const char *url) 96struct repoinfo *add_repo(const char *url)
86{ 97{
87 struct repoinfo *ret; 98 struct repoinfo *ret;
@@ -144,7 +155,9 @@ void cgit_global_config_cb(const char *name, const char *value)
144 else if (!strcmp(name, "nocache")) 155 else if (!strcmp(name, "nocache"))
145 cgit_nocache = atoi(value); 156 cgit_nocache = atoi(value);
146 else if (!strcmp(name, "snapshots")) 157 else if (!strcmp(name, "snapshots"))
147 cgit_snapshots = atoi(value); 158 cgit_snapshots = cgit_parse_snapshots_mask(value);
159 else if (!strcmp(name, "enable-index-links"))
160 cgit_enable_index_links = atoi(value);
148 else if (!strcmp(name, "enable-log-filecount")) 161 else if (!strcmp(name, "enable-log-filecount"))
149 cgit_enable_log_filecount = atoi(value); 162 cgit_enable_log_filecount = atoi(value);
150 else if (!strcmp(name, "enable-log-linecount")) 163 else if (!strcmp(name, "enable-log-linecount"))
@@ -184,7 +197,7 @@ void cgit_global_config_cb(const char *name, const char *value)
184 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 197 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
185 cgit_repo->defbranch = xstrdup(value); 198 cgit_repo->defbranch = xstrdup(value);
186 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 199 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
187 cgit_repo->snapshots = cgit_snapshots * atoi(value); 200 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
188 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 201 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
189 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 202 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
190 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 203 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
@@ -224,7 +237,7 @@ void cgit_querystring_cb(const char *name, const char *value)
224 } else if (!strcmp(name, "ofs")) { 237 } else if (!strcmp(name, "ofs")) {
225 cgit_query_ofs = atoi(value); 238 cgit_query_ofs = atoi(value);
226 } else if (!strcmp(name, "path")) { 239 } else if (!strcmp(name, "path")) {
227 cgit_query_path = xstrdup(value); 240 cgit_query_path = trim_end(value, '/');
228 } else if (!strcmp(name, "name")) { 241 } else if (!strcmp(name, "name")) {
229 cgit_query_name = xstrdup(value); 242 cgit_query_name = xstrdup(value);
230 } 243 }
@@ -253,6 +266,28 @@ int hextoint(char c)
253 return -1; 266 return -1;
254} 267}
255 268
269char *trim_end(const char *str, char c)
270{
271 int len;
272 char *s, *t;
273
274 if (str == NULL)
275 return NULL;
276 t = (char *)str;
277 len = strlen(t);
278 while(len > 0 && t[len - 1] == c)
279 len--;
280
281 if (len == 0)
282 return NULL;
283
284 c = t[len];
285 t[len] = '\0';
286 s = xstrdup(t);
287 t[len] = c;
288 return s;
289}
290
256void cgit_diff_tree_cb(struct diff_queue_struct *q, 291void cgit_diff_tree_cb(struct diff_queue_struct *q,
257 struct diff_options *options, void *data) 292 struct diff_options *options, void *data)
258{ 293{
@@ -359,7 +394,7 @@ void cgit_diff_tree(const unsigned char *old_sha1,
359 opt.format_callback_data = fn; 394 opt.format_callback_data = fn;
360 diff_setup_done(&opt); 395 diff_setup_done(&opt);
361 396
362 if (old_sha1) 397 if (old_sha1 && !is_null_sha1(old_sha1))
363 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 398 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
364 else 399 else
365 ret = diff_root_tree_sha1(new_sha1, "", &opt); 400 ret = diff_root_tree_sha1(new_sha1, "", &opt);
diff --git a/submodules.sh b/submodules.sh
deleted file mode 100755
index 1d7b13f..0000000
--- a/submodules.sh
+++ /dev/null
@@ -1,181 +0,0 @@
1#!/bin/sh
2#
3# submodules.sh: init, update or list git submodules
4#
5# Copyright (C) 2006 Lars Hjemli
6#
7# Licensed under GNU General Public License v2
8# (see COPYING for full license text)
9#
10
11
12usage="submodules.sh [-i | -u] [-q] [--cached] [path...]"
13init=
14update=
15quiet=
16cached=
17
18
19say()
20{
21 if test -z "$quiet"
22 then
23 echo -e "$@"
24 fi
25}
26
27
28die()
29{
30 echo >&2 -e "$@"
31 exit 1
32}
33
34
35
36#
37# Silently checkout specified submodule revision, return exit status of git-checkout
38#
39# $1 = local path
40# $2 = requested sha1
41#
42module_checkout()
43{
44 $(cd "$1" && git checkout "$2" 1>/dev/null 2>/dev/null)
45}
46
47
48#
49# Find all (requested) submodules, run clone + checkout on missing paths
50#
51# $@ = requested paths (default to all)
52#
53modules_init()
54{
55 git ls-files --stage -- $@ | grep -e '^160000 ' |
56 while read mode sha1 stage path
57 do
58 test -d "$path/.git" && continue
59
60 if test -d "$path"
61 then
62 rmdir "$path" 2>/dev/null ||
63 die "Directory '$path' exist, but not as a submodule"
64 fi
65
66 test -e "$path" && die "A file already exist at path '$path'"
67
68 url=$(sed -nre "s/^$path[ \t]+//p" .gitmodules)
69 test -z "$url" && die "No url found for $path in .gitmodules"
70
71 git clone "$url" "$path" || die "Clone of submodule '$path' failed"
72 module_checkout "$path" "$sha1" || die "Checkout of submodule '$path' failed"
73 say "Submodule '$path' initialized"
74 done
75}
76
77#
78# Checkout correct revision of each initialized submodule
79#
80# $@ = requested paths (default to all)
81#
82modules_update()
83{
84 git ls-files --stage -- $@ | grep -e '^160000 ' |
85 while read mode sha1 stage path
86 do
87 if ! test -d "$path/.git"
88 then
89 say "Submodule '$path' not initialized"
90 continue;
91 fi
92 subsha1=$(cd "$path" && git rev-parse --verify HEAD) ||
93 die "Unable to find current revision of submodule '$path'"
94 if test "$subsha1" != "$sha1"
95 then
96 module_checkout "$path" "$sha1" ||
97 die "Unable to checkout revision $sha1 of submodule '$path'"
98 say "Submodule '$path' reset to revision $sha1"
99 fi
100 done
101}
102
103#
104# List all registered submodules, prefixed with:
105# - submodule not initialized
106# + different version checked out
107#
108# If --cached was specified the revision in the index will be printed
109# instead of the currently checked out revision.
110#
111# $@ = requested paths (default to all)
112#
113modules_list()
114{
115 git ls-files --stage -- $@ | grep -e '^160000 ' |
116 while read mode sha1 stage path
117 do
118 if ! test -d "$path/.git"
119 then
120 say "-$sha1 $path"
121 continue;
122 fi
123 revname=$(cd "$path" && git describe $sha1)
124 if git diff-files --quiet -- "$path"
125 then
126 say " $sha1 $path\t($revname)"
127 else
128 if test -z "$cached"
129 then
130 sha1=$(cd "$path" && git rev-parse HEAD)
131 revname=$(cd "$path" && git describe HEAD)
132 fi
133 say "+$sha1 $path\t($revname)"
134 fi
135 done
136}
137
138
139while case "$#" in 0) break ;; esac
140do
141 case "$1" in
142 -i)
143 init=1
144 ;;
145 -u)
146 update=1
147 ;;
148 -q)
149 quiet=1
150 ;;
151 --cached)
152 cached=1
153 ;;
154 --)
155 break
156 ;;
157 -*)
158 echo "Usage: $usage"
159 exit 1
160 ;;
161 --*)
162 echo "Usage: $usage"
163 exit 1
164 ;;
165 *)
166 break
167 ;;
168 esac
169 shift
170done
171
172
173if test "$init" = "1"
174then
175 modules_init $@
176elif test "$update" = "1"
177then
178 modules_update $@
179else
180 modules_list $@
181fi
diff --git a/ui-commit.c b/ui-commit.c
index 1d12bbb..90e09ed 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -11,6 +11,7 @@
11static int files, slots; 11static int files, slots;
12static int total_adds, total_rems, max_changes; 12static int total_adds, total_rems, max_changes;
13static int lines_added, lines_removed; 13static int lines_added, lines_removed;
14static char *curr_rev;
14 15
15static struct fileinfo { 16static struct fileinfo {
16 char status; 17 char status;
@@ -27,7 +28,6 @@ static struct fileinfo {
27 28
28void print_fileinfo(struct fileinfo *info) 29void print_fileinfo(struct fileinfo *info)
29{ 30{
30 char *query, *query2;
31 char *class; 31 char *class;
32 32
33 switch (info->status) { 33 switch (info->status) {
@@ -75,24 +75,12 @@ void print_fileinfo(struct fileinfo *info)
75 html("]</span>"); 75 html("]</span>");
76 } 76 }
77 htmlf("</td><td class='%s'>", class); 77 htmlf("</td><td class='%s'>", class);
78 query = fmt("id=%s&amp;id2=%s&amp;path=%s", sha1_to_hex(info->old_sha1), 78 cgit_tree_link(info->new_path, NULL, NULL, cgit_query_head, curr_rev,
79 sha1_to_hex(info->new_sha1), info->new_path); 79 info->new_path);
80 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), 80 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
81 NULL, NULL); 81 htmlf(" (%s from %s)",
82 if (info->status == DIFF_STATUS_COPIED || 82 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
83 info->status == DIFF_STATUS_RENAMED) { 83 info->old_path);
84 html_txt(info->new_path);
85 htmlf("</a> (%s from ", info->status == DIFF_STATUS_COPIED ?
86 "copied" : "renamed");
87 query2 = fmt("id=%s", sha1_to_hex(info->old_sha1));
88 html_link_open(cgit_pageurl(cgit_query_repo, "view", query2),
89 NULL, NULL);
90 html_txt(info->old_path);
91 html("</a>)");
92 } else {
93 html_txt(info->new_path);
94 html("</a>");
95 }
96 html("</td><td class='right'>"); 84 html("</td><td class='right'>");
97 htmlf("%d", info->added + info->removed); 85 htmlf("%d", info->added + info->removed);
98 html("</td><td class='graph'>"); 86 html("</td><td class='graph'>");
@@ -145,16 +133,19 @@ void inspect_filepair(struct diff_filepair *pair)
145} 133}
146 134
147 135
148void cgit_print_commit(const char *hex) 136void cgit_print_commit(char *hex)
149{ 137{
150 struct commit *commit, *parent; 138 struct commit *commit, *parent;
151 struct commitinfo *info; 139 struct commitinfo *info;
152 struct commit_list *p; 140 struct commit_list *p;
153 unsigned char sha1[20]; 141 unsigned char sha1[20];
154 char *query; 142 char *tmp;
155 char *filename;
156 int i; 143 int i;
157 144
145 if (!hex)
146 hex = cgit_query_head;
147 curr_rev = hex;
148
158 if (get_sha1(hex, sha1)) { 149 if (get_sha1(hex, sha1)) {
159 cgit_print_error(fmt("Bad object id: %s", hex)); 150 cgit_print_error(fmt("Bad object id: %s", hex));
160 return; 151 return;
@@ -181,11 +172,11 @@ void cgit_print_commit(const char *hex)
181 html("</td><td class='right'>"); 172 html("</td><td class='right'>");
182 cgit_print_date(info->committer_date, FMT_LONGDATE); 173 cgit_print_date(info->committer_date, FMT_LONGDATE);
183 html("</td></tr>\n"); 174 html("</td></tr>\n");
184 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='"); 175 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
185 query = fmt("h=%s&amp;id=%s", sha1_to_hex(commit->object.sha1), 176 tmp = xstrdup(hex);
186 sha1_to_hex(commit->tree->object.sha1)); 177 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
187 html_attr(cgit_pageurl(cgit_query_repo, "tree", query)); 178 cgit_query_head, tmp, NULL);
188 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1)); 179 html("</td></tr>\n");
189 for (p = commit->parents; p ; p = p->next) { 180 for (p = commit->parents; p ; p = p->next) {
190 parent = lookup_commit_reference(p->item->object.sha1); 181 parent = lookup_commit_reference(p->item->object.sha1);
191 if (!parent) { 182 if (!parent) {
@@ -195,23 +186,19 @@ void cgit_print_commit(const char *hex)
195 continue; 186 continue;
196 } 187 }
197 html("<tr><th>parent</th>" 188 html("<tr><th>parent</th>"
198 "<td colspan='2' class='sha1'>" 189 "<td colspan='2' class='sha1'>");
199 "<a href='"); 190 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
200 query = fmt("h=%s", sha1_to_hex(p->item->object.sha1)); 191 cgit_query_head, sha1_to_hex(p->item->object.sha1));
201 html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); 192 html(" (");
202 htmlf("'>%s</a> (<a href='", 193 cgit_diff_link("diff", NULL, NULL, cgit_query_head, hex,
203 sha1_to_hex(p->item->object.sha1)); 194 sha1_to_hex(p->item->object.sha1), NULL);
204 query = fmt("id=%s&amp;id2=%s", sha1_to_hex(parent->tree->object.sha1), 195 html(")</td></tr>");
205 sha1_to_hex(commit->tree->object.sha1));
206 html_attr(cgit_pageurl(cgit_query_repo, "diff", query));
207 html("'>diff</a>)</td></tr>");
208 } 196 }
209 if (cgit_repo->snapshots) { 197 if (cgit_repo->snapshots) {
210 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='"); 198 html("<tr><th>download</th><td colspan='2' class='sha1'>");
211 filename = fmt("%s-%s.zip", cgit_query_repo, hex); 199 cgit_print_snapshot_links(cgit_query_repo, cgit_query_head,
212 html_attr(cgit_pageurl(cgit_query_repo, "snapshot", 200 hex, cgit_repo->snapshots);
213 fmt("id=%s&amp;name=%s", hex, filename))); 201 html("</td></tr>");
214 htmlf("'>%s</a></td></tr>", filename);
215 } 202 }
216 html("</table>\n"); 203 html("</table>\n");
217 html("<div class='commit-subject'>"); 204 html("<div class='commit-subject'>");
@@ -231,10 +218,9 @@ void cgit_print_commit(const char *hex)
231 html("<div class='diffstat-summary'>"); 218 html("<div class='diffstat-summary'>");
232 htmlf("%d files changed, %d insertions, %d deletions (", 219 htmlf("%d files changed, %d insertions, %d deletions (",
233 files, total_adds, total_rems); 220 files, total_adds, total_rems);
234 query = fmt("h=%s", hex); 221 cgit_diff_link("show diff", NULL, NULL, cgit_query_head, hex,
235 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), NULL, NULL); 222 NULL, NULL);
236 html("show diff</a>)"); 223 html(")</div>");
237 html("</div>");
238 } 224 }
239 cgit_free_commitinfo(info); 225 cgit_free_commitinfo(info);
240} 226}
diff --git a/ui-diff.c b/ui-diff.c
index 4695e3a..0be845f 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -89,54 +89,52 @@ static void filepair_cb(struct diff_filepair *pair)
89 cgit_print_error("Error running diff"); 89 cgit_print_error("Error running diff");
90} 90}
91 91
92void cgit_print_diff(const char *head, const char *old_hex, const char *new_hex, char *path) 92void cgit_print_diff(const char *new_rev, const char *old_rev)
93{ 93{
94 unsigned char sha1[20], sha2[20]; 94 unsigned char sha1[20], sha2[20];
95 enum object_type type; 95 enum object_type type;
96 unsigned long size; 96 unsigned long size;
97 struct commit *commit; 97 struct commit *commit, *commit2;
98 98
99 if (head && !old_hex && !new_hex) { 99 if (!new_rev)
100 get_sha1(head, sha1); 100 new_rev = cgit_query_head;
101 commit = lookup_commit_reference(sha1); 101 get_sha1(new_rev, sha1);
102 if (commit && !parse_commit(commit)) { 102 type = sha1_object_info(sha1, &size);
103 html("<table class='diff'>"); 103 if (type == OBJ_BAD) {
104 html("<tr><td>"); 104 cgit_print_error(fmt("Bad object name: %s", new_rev));
105 cgit_diff_commit(commit, filepair_cb); 105 return;
106 html("</td></tr>"); 106 }
107 html("</table>"); 107 if (type != OBJ_COMMIT) {
108 } 108 cgit_print_error(fmt("Unhandled object type: %s",
109 typename(type)));
109 return; 110 return;
110 } 111 }
111 112
112 get_sha1(old_hex, sha1); 113 commit = lookup_commit_reference(sha1);
113 get_sha1(new_hex, sha2); 114 if (!commit || parse_commit(commit))
115 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha1)));
114 116
115 type = sha1_object_info(sha1, &size); 117 if (old_rev)
116 if (type == OBJ_BAD) { 118 get_sha1(old_rev, sha2);
119 else if (commit->parents && commit->parents->item)
120 hashcpy(sha2, commit->parents->item->object.sha1);
121 else
122 hashclr(sha2);
123
124 if (!is_null_sha1(sha2)) {
117 type = sha1_object_info(sha2, &size); 125 type = sha1_object_info(sha2, &size);
118 if (type == OBJ_BAD) { 126 if (type == OBJ_BAD) {
119 cgit_print_error(fmt("Bad object names: %s, %s", old_hex, new_hex)); 127 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(sha2)));
120 return; 128 return;
121 } 129 }
130 commit2 = lookup_commit_reference(sha2);
131 if (!commit2 || parse_commit(commit2))
132 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(sha2)));
122 } 133 }
123 134
124 html("<table class='diff'>"); 135 html("<table class='diff'>");
125 switch(type) { 136 html("<tr><td>");
126 case OBJ_BLOB: 137 cgit_diff_tree(sha2, sha1, filepair_cb);
127 html("<tr><td>"); 138 html("</td></tr>");
128 header(sha1, path, 0644, sha2, path, 0644);
129 if (cgit_diff_files(sha1, sha2, print_line))
130 cgit_print_error("Error running diff");
131 html("</td></tr>");
132 break;
133 case OBJ_TREE:
134 cgit_diff_tree(sha1, sha2, filepair_cb);
135 break;
136 default:
137 cgit_print_error(fmt("Unhandled object type: %s",
138 typename(type)));
139 break;
140 }
141 html("</table>"); 139 html("</table>");
142} 140}
diff --git a/ui-log.c b/ui-log.c
index bb17e1d..d38e40a 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -31,11 +31,8 @@ void print_commit(struct commit *commit)
31 html("<tr><td>"); 31 html("<tr><td>");
32 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 32 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
33 html("</td><td>"); 33 html("</td><td>");
34 char *qry = fmt("h=%s", sha1_to_hex(commit->object.sha1)); 34 cgit_commit_link(info->subject, NULL, NULL, cgit_query_head,
35 char *url = cgit_pageurl(cgit_query_repo, "commit", qry); 35 sha1_to_hex(commit->object.sha1));
36 html_link_open(url, NULL, NULL);
37 html_ntxt(cgit_max_msg_len, info->subject);
38 html_link_close();
39 if (cgit_repo->enable_log_filecount) { 36 if (cgit_repo->enable_log_filecount) {
40 files = 0; 37 files = 0;
41 lines = 0; 38 lines = 0;
@@ -62,6 +59,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, i
62 int argc = 2; 59 int argc = 2;
63 int i; 60 int i;
64 61
62 if (!tip)
63 argv[1] = cgit_query_head;
64
65 if (grep) 65 if (grep)
66 argv[argc++] = fmt("--grep=%s", grep); 66 argv[argc++] = fmt("--grep=%s", grep);
67 if (path) { 67 if (path) {
@@ -113,17 +113,15 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, i
113 if (pager) { 113 if (pager) {
114 html("<div class='pager'>"); 114 html("<div class='pager'>");
115 if (ofs > 0) { 115 if (ofs > 0) {
116 html("&nbsp;<a href='"); 116 cgit_log_link("[prev]", NULL, NULL, cgit_query_head,
117 html(cgit_pageurl(cgit_query_repo, cgit_query_page, 117 cgit_query_sha1, cgit_query_path,
118 fmt("h=%s&amp;ofs=%d", tip, ofs-cnt))); 118 ofs - cnt);
119 html("'>[prev]</a>&nbsp;"); 119 html("&nbsp;");
120 } 120 }
121
122 if ((commit = get_revision(&rev)) != NULL) { 121 if ((commit = get_revision(&rev)) != NULL) {
123 html("&nbsp;<a href='"); 122 cgit_log_link("[next]", NULL, NULL, cgit_query_head,
124 html(cgit_pageurl(cgit_query_repo, "log", 123 cgit_query_sha1, cgit_query_path,
125 fmt("h=%s&amp;ofs=%d", tip, ofs+cnt))); 124 ofs + cnt);
126 html("'>[next]</a>&nbsp;");
127 } 125 }
128 html("</div>"); 126 html("</div>");
129 } 127 }
diff --git a/ui-repolist.c b/ui-repolist.c
index e5c6c20..4c86543 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -44,16 +44,19 @@ static void print_modtime(struct repoinfo *repo)
44 44
45void cgit_print_repolist(struct cacheitem *item) 45void cgit_print_repolist(struct cacheitem *item)
46{ 46{
47 struct repoinfo *repo; 47 int i, columns = 4;
48 int i;
49 char *last_group = NULL; 48 char *last_group = NULL;
50 49
50 if (cgit_enable_index_links)
51 columns++;
52
51 cgit_print_docstart(cgit_root_title, item); 53 cgit_print_docstart(cgit_root_title, item);
52 cgit_print_pageheader(cgit_root_title, 0); 54 cgit_print_pageheader(cgit_root_title, 0);
53 55
54 html("<table class='list nowrap'>"); 56 html("<table class='list nowrap'>");
55 if (cgit_index_header) { 57 if (cgit_index_header) {
56 html("<tr class='nohover'><td colspan='5' class='include-block'>"); 58 htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>",
59 columns);
57 html_include(cgit_index_header); 60 html_include(cgit_index_header);
58 html("</td></tr>"); 61 html("</td></tr>");
59 } 62 }
@@ -61,42 +64,45 @@ void cgit_print_repolist(struct cacheitem *item)
61 "<th class='left'>Name</th>" 64 "<th class='left'>Name</th>"
62 "<th class='left'>Description</th>" 65 "<th class='left'>Description</th>"
63 "<th class='left'>Owner</th>" 66 "<th class='left'>Owner</th>"
64 "<th class='left'>Idle</th>" 67 "<th class='left'>Idle</th>");
65 "<th>Links</th></tr>\n"); 68 if (cgit_enable_index_links)
69 html("<th>Links</th>");
70 html("</tr>\n");
66 71
67 for (i=0; i<cgit_repolist.count; i++) { 72 for (i=0; i<cgit_repolist.count; i++) {
68 repo = &cgit_repolist.repos[i]; 73 cgit_repo = &cgit_repolist.repos[i];
69 if ((last_group == NULL && repo->group != NULL) || 74 if ((last_group == NULL && cgit_repo->group != NULL) ||
70 (last_group != NULL && repo->group == NULL) || 75 (last_group != NULL && cgit_repo->group == NULL) ||
71 (last_group != NULL && repo->group!= NULL && 76 (last_group != NULL && cgit_repo->group != NULL &&
72 strcmp(repo->group, last_group))) { 77 strcmp(cgit_repo->group, last_group))) {
73 html("<tr class='nohover'><td colspan='4' class='repogroup'>"); 78 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
74 html_txt(repo->group); 79 columns);
80 html_txt(cgit_repo->group);
75 html("</td></tr>"); 81 html("</td></tr>");
76 last_group = repo->group; 82 last_group = cgit_repo->group;
77 } 83 }
78 htmlf("<tr><td class='%s'>", 84 htmlf("<tr><td class='%s'>",
79 repo->group ? "sublevel-repo" : "toplevel-repo"); 85 cgit_repo->group ? "sublevel-repo" : "toplevel-repo");
80 html_link_open(cgit_repourl(repo->url), repo->desc, NULL); 86 html_link_open(cgit_repourl(cgit_repo->url), NULL, NULL);
81 html_txt(repo->name); 87 html_txt(cgit_repo->name);
82 html_link_close(); 88 html_link_close();
83 html("</td><td>"); 89 html("</td><td>");
84 html_ntxt(cgit_max_repodesc_len, repo->desc); 90 html_ntxt(cgit_max_repodesc_len, cgit_repo->desc);
85 html("</td><td>");
86 html_txt(repo->owner);
87 html("</td><td>"); 91 html("</td><td>");
88 print_modtime(repo); 92 html_txt(cgit_repo->owner);
89 html("</td><td>"); 93 html("</td><td>");
90 html_link_open(cgit_repourl(repo->url), 94 print_modtime(cgit_repo);
91 "Summary", "button"); 95 html("</td>");
92 html("S</a>"); 96 if (cgit_enable_index_links) {
93 html_link_open(cgit_pageurl(repo->name, "log", NULL), 97 html("<td>");
94 "Log", "button"); 98 html_link_open(cgit_repourl(cgit_repo->url),
95 html("L</a>"); 99 NULL, "button");
96 html_link_open(cgit_pageurl(repo->name, "tree", NULL), 100 html("summary</a>");
97 "Files", "button"); 101 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 0);
98 html("F</a>"); 102 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
99 html("</td></tr>\n"); 103 html("</td>");
104 }
105 html("</tr>\n");
100 } 106 }
101 html("</table>"); 107 html("</table>");
102 cgit_print_docend(); 108 cgit_print_docend();
diff --git a/ui-shared.c b/ui-shared.c
index aba93e8..5c5bcf3 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -57,13 +57,13 @@ char *cgit_repourl(const char *reponame)
57 } 57 }
58} 58}
59 59
60char *cgit_pageurl(const char *reponame, const char *pagename, 60char *cgit_fileurl(const char *reponame, const char *pagename,
61 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", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
66 pagename, 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);
@@ -75,6 +75,37 @@ char *cgit_pageurl(const char *reponame, const char *pagename,
75 } 75 }
76} 76}
77 77
78char *cgit_pageurl(const char *reponame, const char *pagename,
79 const char *query)
80{
81 return cgit_fileurl(reponame,pagename,0,query);
82}
83
84const char *cgit_repobasename(const char *reponame)
85{
86 /* I assume we don't need to store more than one repo basename */
87 static char rvbuf[1024];
88 int p;
89 const char *rv;
90 strncpy(rvbuf,reponame,sizeof(rvbuf));
91 if(rvbuf[sizeof(rvbuf)-1])
92 die("cgit_repobasename: truncated repository name '%s'", reponame);
93 p = strlen(rvbuf)-1;
94 /* strip trailing slashes */
95 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
96 /* strip trailing .git */
97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
98 p -= 3; rvbuf[p--] = 0;
99 }
100 /* strip more trailing slashes if any */
101 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
102 /* find last slash in the remaining string */
103 rv = strrchr(rvbuf,'/');
104 if(rv)
105 return ++rv;
106 return rvbuf;
107}
108
78char *cgit_currurl() 109char *cgit_currurl()
79{ 110{
80 if (!cgit_virtual_root) 111 if (!cgit_virtual_root)
@@ -87,6 +118,166 @@ char *cgit_currurl()
87 return fmt("%s/", cgit_virtual_root); 118 return fmt("%s/", cgit_virtual_root);
88} 119}
89 120
121static char *repolink(char *title, char *class, char *page, char *head,
122 char *path)
123{
124 char *delim = "?";
125
126 html("<a");
127 if (title) {
128 html(" title='");
129 html_attr(title);
130 html("'");
131 }
132 if (class) {
133 html(" class='");
134 html_attr(class);
135 html("'");
136 }
137 html(" href='");
138 if (cgit_virtual_root) {
139 html_attr(cgit_virtual_root);
140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
141 html("/");
142 html_attr(cgit_repo->url);
143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
144 html("/");
145 if (page) {
146 html(page);
147 html("/");
148 if (path)
149 html_attr(path);
150 }
151 } else {
152 html(cgit_script_name);
153 html("?url=");
154 html_attr(cgit_repo->url);
155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
156 html("/");
157 if (page) {
158 html(page);
159 html("/");
160 if (path)
161 html_attr(path);
162 }
163 delim = "&amp;";
164 }
165 if (head && strcmp(head, cgit_repo->defbranch)) {
166 html(delim);
167 html("h=");
168 html_attr(head);
169 delim = "&amp;";
170 }
171 return fmt("%s", delim);
172}
173
174static void reporevlink(char *page, char *name, char *title, char *class,
175 char *head, char *rev, char *path)
176{
177 char *delim;
178
179 delim = repolink(title, class, page, head, path);
180 if (rev && strcmp(rev, cgit_query_head)) {
181 html(delim);
182 html("id=");
183 html_attr(rev);
184 }
185 html("'>");
186 html_txt(name);
187 html("</a>");
188}
189
190void cgit_tree_link(char *name, char *title, char *class, char *head,
191 char *rev, char *path)
192{
193 reporevlink("tree", name, title, class, head, rev, path);
194}
195
196void cgit_log_link(char *name, char *title, char *class, char *head,
197 char *rev, char *path, int ofs)
198{
199 char *delim;
200
201 delim = repolink(title, class, "log", head, path);
202 if (rev && strcmp(rev, cgit_query_head)) {
203 html(delim);
204 html("id=");
205 html_attr(rev);
206 delim = "&";
207 }
208 if (ofs > 0) {
209 html(delim);
210 html("ofs=");
211 htmlf("%d", ofs);
212 }
213 html("'>");
214 html_txt(name);
215 html("</a>");
216}
217
218void cgit_commit_link(char *name, char *title, char *class, char *head,
219 char *rev)
220{
221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
222 name[cgit_max_msg_len] = '\0';
223 name[cgit_max_msg_len - 1] = '.';
224 name[cgit_max_msg_len - 2] = '.';
225 name[cgit_max_msg_len - 3] = '.';
226 }
227 reporevlink("commit", name, title, class, head, rev, NULL);
228}
229
230void cgit_snapshot_link(char *name, char *title, char *class, char *head,
231 char *rev, char *archivename)
232{
233 reporevlink("snapshot", name, title, class, head, rev, archivename);
234}
235
236void cgit_diff_link(char *name, char *title, char *class, char *head,
237 char *new_rev, char *old_rev, char *path)
238{
239 char *delim;
240
241 delim = repolink(title, class, "diff", head, path);
242 if (new_rev && strcmp(new_rev, cgit_query_head)) {
243 html(delim);
244 html("id=");
245 html_attr(new_rev);
246 delim = "&amp;";
247 }
248 if (old_rev) {
249 html(delim);
250 html("id2=");
251 html_attr(old_rev);
252 }
253 html("'>");
254 html_txt(name);
255 html("</a>");
256}
257
258void cgit_object_link(struct object *obj)
259{
260 char *page, *arg, *url;
261
262 if (obj->type == OBJ_COMMIT) {
263 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
264 cgit_query_head, sha1_to_hex(obj->sha1));
265 return;
266 } else if (obj->type == OBJ_TREE) {
267 page = "tree";
268 arg = "id";
269 } else {
270 page = "blob";
271 arg = "id";
272 }
273
274 url = cgit_pageurl(cgit_query_repo, page,
275 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
276 html_link_open(url, NULL, NULL);
277 htmlf("%s %s", typename(obj->type),
278 sha1_to_hex(obj->sha1));
279 html_link_close();
280}
90 281
91void cgit_print_date(time_t secs, char *format) 282void cgit_print_date(time_t secs, char *format)
92{ 283{
@@ -152,7 +343,7 @@ void cgit_print_docstart(char *title, struct cacheitem *item)
152 html("<title>"); 343 html("<title>");
153 html_txt(title); 344 html_txt(title);
154 html("</title>\n"); 345 html("</title>\n");
155 htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version); 346 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
156 html("<link rel='stylesheet' type='text/css' href='"); 347 html("<link rel='stylesheet' type='text/css' href='");
157 html_attr(cgit_css); 348 html_attr(cgit_css);
158 html("'/>\n"); 349 html("'/>\n");
@@ -169,19 +360,38 @@ void cgit_print_docend()
169void cgit_print_pageheader(char *title, int show_search) 360void cgit_print_pageheader(char *title, int show_search)
170{ 361{
171 html("<table id='layout'>"); 362 html("<table id='layout'>");
172 html("<tr><td id='header'>"); 363 html("<tr><td id='header'><a href='");
173 html(cgit_root_title); 364 html_attr(cgit_rooturl());
174 html("</td><td id='logo'>"); 365 html("'>");
366 html_txt(cgit_root_title);
367 html("</a></td><td id='logo'>");
175 html("<a href='"); 368 html("<a href='");
176 html_attr(cgit_logo_link); 369 html_attr(cgit_logo_link);
177 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 370 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo);
178 html("</td></tr>"); 371 html("</td></tr>");
179 html("<tr><td id='crumb'>"); 372 html("<tr><td id='crumb'>");
180 htmlf("<a href='%s'>root</a>", cgit_rooturl());
181 if (cgit_query_repo) { 373 if (cgit_query_repo) {
182 htmlf(" : <a href='%s'>", cgit_repourl(cgit_repo->url));
183 html_txt(cgit_repo->name); 374 html_txt(cgit_repo->name);
184 htmlf("</a> : %s", title); 375 html(" (");
376 html_txt(cgit_query_head);
377 html(") : &nbsp;");
378 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head,
379 NULL, NULL);
380 html(" ");
381 cgit_log_link("log", NULL, NULL, cgit_query_head,
382 cgit_query_sha1, cgit_query_path, 0);
383 html(" ");
384 cgit_tree_link("tree", NULL, NULL, cgit_query_head,
385 cgit_query_sha1, NULL);
386 html(" ");
387 cgit_commit_link("commit", NULL, NULL, cgit_query_head,
388 cgit_query_sha1);
389 html(" ");
390 cgit_diff_link("diff", NULL, NULL, cgit_query_head,
391 cgit_query_sha1, cgit_query_sha2,
392 cgit_query_path);
393 } else {
394 html_txt("Index of repositories");
185 } 395 }
186 html("</td>"); 396 html("</td>");
187 html("<td id='search'>"); 397 html("<td id='search'>");
@@ -219,3 +429,5 @@ void cgit_print_snapshot_start(const char *mimetype, const char *filename,
219 ttl_seconds(item->ttl))); 429 ttl_seconds(item->ttl)));
220 html("\n"); 430 html("\n");
221} 431}
432
433/* vim:set sw=8: */
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 2257d6b..bd34a28 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -8,40 +8,148 @@
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11static void cgit_print_zip(struct cacheitem *item, const char *hex, 11static int write_compressed_tar_archive(struct archiver_args *args,const char *filter)
12 const char *prefix, const char *filename)
13{ 12{
13 int rw[2];
14 pid_t gzpid;
15 int stdout2;
16 int status;
17 int rv;
18
19 stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing");
20 chk_zero(pipe(rw), "Opening pipe from compressor subprocess");
21 gzpid = chk_non_negative(fork(), "Forking compressor subprocess");
22 if(gzpid==0) {
23 /* child */
24 chk_zero(close(rw[1]), "Closing write end of pipe in child");
25 chk_zero(close(STDIN_FILENO), "Closing STDIN");
26 chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin");
27 execlp(filter,filter,NULL);
28 _exit(-1);
29 }
30 /* parent */
31 chk_zero(close(rw[0]), "Closing read end of pipe");
32 chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor");
33
34 rv = write_tar_archive(args);
35
36 chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor");
37 chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT");
38 chk_zero(close(stdout2), "Closing uncompressed STDOUT");
39 chk_zero(close(rw[1]), "Closing write end of pipe in parent");
40 chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process");
41 if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) )
42 cgit_print_error("Failed to compress archive");
43
44 return rv;
45}
46
47static int write_tar_gzip_archive(struct archiver_args *args)
48{
49 return write_compressed_tar_archive(args,"gzip");
50}
51
52static int write_tar_bzip2_archive(struct archiver_args *args)
53{
54 return write_compressed_tar_archive(args,"bzip2");
55}
56
57static const struct snapshot_archive_t {
58 const char *suffix;
59 const char *mimetype;
60 write_archive_fn_t write_func;
61 int bit;
62} snapshot_archives[] = {
63 { ".zip", "application/x-zip", write_zip_archive, 0x1 },
64 { ".tar.gz", "application/x-tar", write_tar_gzip_archive, 0x2 },
65 { ".tar.bz2", "application/x-tar", write_tar_bzip2_archive, 0x4 },
66 { ".tar", "application/x-tar", write_tar_archive, 0x8 }
67};
68
69#define snapshot_archives_len (sizeof(snapshot_archives) / sizeof(*snapshot_archives))
70
71void cgit_print_snapshot(struct cacheitem *item, const char *head,
72 const char *hex, const char *prefix,
73 const char *filename, int snapshots)
74{
75 const struct snapshot_archive_t* sat;
14 struct archiver_args args; 76 struct archiver_args args;
15 struct commit *commit; 77 struct commit *commit;
16 unsigned char sha1[20]; 78 unsigned char sha1[20];
79 int f, sl, fnl = strlen(filename);
17 80
18 if (get_sha1(hex, sha1)) { 81 for(f=0; f<snapshot_archives_len; f++) {
19 cgit_print_error(fmt("Bad object id: %s", hex)); 82 sat = &snapshot_archives[f];
83 if(!(snapshots & sat->bit))
84 continue;
85 sl = strlen(sat->suffix);
86 if(fnl<sl || strcmp(&filename[fnl-sl],sat->suffix))
87 continue;
88 if (!hex)
89 hex = head;
90 if(get_sha1(hex, sha1)) {
91 cgit_print_error(fmt("Bad object id: %s", hex));
92 return;
93 }
94 commit = lookup_commit_reference(sha1);
95 if(!commit) {
96 cgit_print_error(fmt("Not a commit reference: %s", hex));
97 return;;
98 }
99 memset(&args,0,sizeof(args));
100 args.base = fmt("%s/", prefix);
101 args.tree = commit->tree;
102 cgit_print_snapshot_start(sat->mimetype, filename, item);
103 (*sat->write_func)(&args);
20 return; 104 return;
21 } 105 }
22 commit = lookup_commit_reference(sha1); 106 cgit_print_error(fmt("Unsupported snapshot format: %s", filename));
107}
23 108
24 if (!commit) { 109void cgit_print_snapshot_links(const char *repo, const char *head,
25 cgit_print_error(fmt("Not a commit reference: %s", hex)); 110 const char *hex, int snapshots)
26 return; 111{
27 } 112 const struct snapshot_archive_t* sat;
113 char *filename;
114 int f;
28 115
29 memset(&args, 0, sizeof(args)); 116 for(f=0; f<snapshot_archives_len; f++) {
30 args.base = fmt("%s/", prefix); 117 sat = &snapshot_archives[f];
31 args.tree = commit->tree; 118 if(!(snapshots & sat->bit))
32 119 continue;
33 cgit_print_snapshot_start("application/x-zip", filename, item); 120 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex,
34 write_zip_archive(&args); 121 sat->suffix);
122 cgit_snapshot_link(filename, NULL, NULL, (char *)head,
123 (char *)hex, filename);
124 html("<br/>");
125 }
35} 126}
36 127
37 128int cgit_parse_snapshots_mask(const char *str)
38void cgit_print_snapshot(struct cacheitem *item, const char *hex,
39 const char *format, const char *prefix,
40 const char *filename)
41{ 129{
42 if (!strcmp(format, "zip")) 130 const struct snapshot_archive_t* sat;
43 cgit_print_zip(item, hex, prefix, filename); 131 static const char *delim = " \t,:/|;";
44 else 132 int f, tl, rv = 0;
45 cgit_print_error(fmt("Unsupported snapshot format: %s", 133
46 format)); 134 /* favor legacy setting */
135 if(atoi(str))
136 return 1;
137 for(;;) {
138 str += strspn(str,delim);
139 tl = strcspn(str,delim);
140 if(!tl)
141 break;
142 for(f=0; f<snapshot_archives_len; f++) {
143 sat = &snapshot_archives[f];
144 if(!(strncmp(sat->suffix, str, tl) &&
145 strncmp(sat->suffix+1, str, tl-1))) {
146 rv |= sat->bit;
147 break;
148 }
149 }
150 str += tl;
151 }
152 return rv;
47} 153}
154
155/* vim:set sw=8: */
diff --git a/ui-summary.c b/ui-summary.c
index 4bda4c2..de8a180 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -15,8 +15,10 @@ static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
15{ 15{
16 struct commit *commit; 16 struct commit *commit;
17 struct commitinfo *info; 17 struct commitinfo *info;
18 char buf[256], *url; 18 char buf[256];
19 char *ref;
19 20
21 ref = xstrdup(refname);
20 strncpy(buf, refname, sizeof(buf)); 22 strncpy(buf, refname, sizeof(buf));
21 commit = lookup_commit(sha1); 23 commit = lookup_commit(sha1);
22 // object is not really parsed at this point, because of some fallout 24 // object is not really parsed at this point, because of some fallout
@@ -25,21 +27,13 @@ static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
25 if (commit && !parse_commit(commit)){ 27 if (commit && !parse_commit(commit)){
26 info = cgit_parse_commit(commit); 28 info = cgit_parse_commit(commit);
27 html("<tr><td>"); 29 html("<tr><td>");
28 url = cgit_pageurl(cgit_query_repo, "log", 30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0);
29 fmt("h=%s", refname));
30 html_link_open(url, NULL, NULL);
31 html_txt(buf);
32 html_link_close();
33 html("</td><td>"); 31 html("</td><td>");
34 cgit_print_age(commit->date, -1, NULL); 32 cgit_print_age(commit->date, -1, NULL);
35 html("</td><td>"); 33 html("</td><td>");
36 html_txt(info->author); 34 html_txt(info->author);
37 html("</td><td>"); 35 html("</td><td>");
38 url = cgit_pageurl(cgit_query_repo, "commit", 36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL);
39 fmt("h=%s", sha1_to_hex(sha1)));
40 html_link_open(url, NULL, NULL);
41 html_ntxt(cgit_max_msg_len, info->subject);
42 html_link_close();
43 html("</td></tr>\n"); 37 html("</td></tr>\n");
44 cgit_free_commitinfo(info); 38 cgit_free_commitinfo(info);
45 } else { 39 } else {
@@ -49,33 +43,10 @@ static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
49 htmlf("*** bad ref %s ***", sha1_to_hex(sha1)); 43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1));
50 html("</td></tr>\n"); 44 html("</td></tr>\n");
51 } 45 }
46 free(ref);
52 return 0; 47 return 0;
53} 48}
54 49
55
56static void cgit_print_object_ref(struct object *obj)
57{
58 char *page, *arg, *url;
59
60 if (obj->type == OBJ_COMMIT) {
61 page = "commit";
62 arg = "h";
63 } else if (obj->type == OBJ_TREE) {
64 page = "tree";
65 arg = "id";
66 } else {
67 page = "view";
68 arg = "id";
69 }
70
71 url = cgit_pageurl(cgit_query_repo, page,
72 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
73 html_link_open(url, NULL, NULL);
74 htmlf("%s %s", typename(obj->type),
75 sha1_to_hex(obj->sha1));
76 html_link_close();
77}
78
79static void print_tag_header() 50static void print_tag_header()
80{ 51{
81 html("<tr class='nohover'><th class='left'>Tag</th>" 52 html("<tr class='nohover'><th class='left'>Tag</th>"
@@ -104,8 +75,8 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
104 if (!header) 75 if (!header)
105 print_tag_header(); 76 print_tag_header();
106 html("<tr><td>"); 77 html("<tr><td>");
107 url = cgit_pageurl(cgit_query_repo, "view", 78 url = cgit_pageurl(cgit_query_repo, "tag",
108 fmt("id=%s", sha1_to_hex(sha1))); 79 fmt("id=%s", refname));
109 html_link_open(url, NULL, NULL); 80 html_link_open(url, NULL, NULL);
110 html_txt(buf); 81 html_txt(buf);
111 html_link_close(); 82 html_link_close();
@@ -116,7 +87,7 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
116 if (info->tagger) 87 if (info->tagger)
117 html(info->tagger); 88 html(info->tagger);
118 html("</td><td>"); 89 html("</td><td>");
119 cgit_print_object_ref(tag->tagged); 90 cgit_object_link(tag->tagged);
120 html("</td></tr>\n"); 91 html("</td></tr>\n");
121 } else { 92 } else {
122 if (!header) 93 if (!header)
@@ -124,7 +95,7 @@ static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
124 html("<tr><td>"); 95 html("<tr><td>");
125 html_txt(buf); 96 html_txt(buf);
126 html("</td><td colspan='2'/><td>"); 97 html("</td><td colspan='2'/><td>");
127 cgit_print_object_ref(obj); 98 cgit_object_link(obj);
128 html("</td></tr>\n"); 99 html("</td></tr>\n");
129 } 100 }
130 return 0; 101 return 0;
diff --git a/ui-tag.c b/ui-tag.c
new file mode 100644
index 0000000..6d761f3
--- /dev/null
+++ b/ui-tag.c
@@ -0,0 +1,74 @@
1/* ui-tag.c: display a tag
2 *
3 * Copyright (C) 2007 Lars Hjemli
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9#include "cgit.h"
10
11
12static void print_tag_content(char *buf)
13{
14 char *p;
15
16 if (!buf)
17 return;
18
19 html("<div class='commit-subject'>");
20 p = strchr(buf, '\n');
21 if (p)
22 *p = '\0';
23 html_txt(buf);
24 html("</div>");
25 if (p) {
26 html("<div class='commit-msg'>");
27 html_txt(++p);
28 html("</div>");
29 }
30}
31
32void cgit_print_tag(char *revname)
33{
34 unsigned char sha1[20];
35 struct object *obj;
36 struct tag *tag;
37 struct taginfo *info;
38
39 if (get_sha1(revname, sha1)) {
40 cgit_print_error(fmt("Bad tag reference: %s", revname));
41 return;
42 }
43 obj = parse_object(sha1);
44 if (!obj) {
45 cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1)));
46 return;
47 }
48 if (obj->type == OBJ_TAG) {
49 tag = lookup_tag(sha1);
50 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
51 cgit_print_error(fmt("Bad tag object: %s", revname));
52 return;
53 }
54 html("<table class='commit-info'>\n");
55 htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n",
56 revname, sha1_to_hex(sha1));
57 if (info->tagger_date > 0) {
58 html("<tr><td>Tag date</td><td>");
59 cgit_print_date(info->tagger_date, FMT_LONGDATE);
60 html("</td></tr>\n");
61 }
62 if (info->tagger) {
63 html("<tr><td>Tagged by</td><td>");
64 html_txt(info->tagger);
65 html("</td></tr>\n");
66 }
67 html("<tr><td>Tagged object</td><td>");
68 cgit_object_link(tag->tagged);
69 html("</td></tr>\n");
70 html("</table>\n");
71 print_tag_content(info->msg);
72 }
73 return;
74}
diff --git a/ui-tree.c b/ui-tree.c
index 21dd533..1cb09f7 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -9,16 +9,64 @@
9#include "cgit.h" 9#include "cgit.h"
10 10
11char *curr_rev; 11char *curr_rev;
12char *match_path;
13int header = 0;
12 14
13static int print_entry(const unsigned char *sha1, const char *base, 15static void print_object(const unsigned char *sha1, char *path)
14 int baselen, const char *pathname, unsigned int mode, 16{
15 int stage) 17 enum object_type type;
18 unsigned char *buf;
19 unsigned long size, lineno, start, idx;
20
21 type = sha1_object_info(sha1, &size);
22 if (type == OBJ_BAD) {
23 cgit_print_error(fmt("Bad object name: %s",
24 sha1_to_hex(sha1)));
25 return;
26 }
27
28 buf = read_sha1_file(sha1, &type, &size);
29 if (!buf) {
30 cgit_print_error(fmt("Error reading object %s",
31 sha1_to_hex(sha1)));
32 return;
33 }
34
35 html(" blob: <a href='");
36 html_attr(cgit_pageurl(cgit_query_repo, "blob", fmt("id=%s", sha1_to_hex(sha1))));
37 htmlf("'>%s</a>",sha1_to_hex(sha1));
38
39 html("<table class='blob'>\n");
40 idx = 0;
41 start = 0;
42 lineno = 0;
43 while(idx < size) {
44 if (buf[idx] == '\n') {
45 buf[idx] = '\0';
46 htmlf("<tr><td class='no'><a name='%d'>%1$d</a></td><td class='txt'>",
47 ++lineno);
48 html_txt(buf + start);
49 html("</td></tr>\n");
50 start = idx + 1;
51 }
52 idx++;
53 }
54 html("</table>\n");
55}
56
57
58static int ls_item(const unsigned char *sha1, const char *base, int baselen,
59 const char *pathname, unsigned int mode, int stage)
16{ 60{
17 char *name; 61 char *name;
62 char *fullpath;
18 enum object_type type; 63 enum object_type type;
19 unsigned long size = 0; 64 unsigned long size = 0;
20 65
21 name = xstrdup(pathname); 66 name = xstrdup(pathname);
67 fullpath = fmt("%s%s%s", cgit_query_path ? cgit_query_path : "",
68 cgit_query_path ? "/" : "", name);
69
22 type = sha1_object_info(sha1, &size); 70 type = sha1_object_info(sha1, &size);
23 if (type == OBJ_BAD && !S_ISGITLINK(mode)) { 71 if (type == OBJ_BAD && !S_ISGITLINK(mode)) {
24 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 72 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
@@ -26,79 +74,140 @@ static int print_entry(const unsigned char *sha1, const char *base,
26 sha1_to_hex(sha1)); 74 sha1_to_hex(sha1));
27 return 0; 75 return 0;
28 } 76 }
29 html("<tr><td class='filemode'>"); 77
78 html("<tr><td class='ls-mode'>");
30 html_filemode(mode); 79 html_filemode(mode);
31 html("</td><td "); 80 html("</td><td>");
32 if (S_ISGITLINK(mode)) { 81 if (S_ISGITLINK(mode)) {
33 htmlf("class='ls-mod'><a href='"); 82 htmlf("<a class='ls-mod' href='");
34 html_attr(fmt(cgit_repo->module_link, 83 html_attr(fmt(cgit_repo->module_link,
35 name, 84 name,
36 sha1_to_hex(sha1))); 85 sha1_to_hex(sha1)));
86 html("'>");
87 html_txt(name);
88 html("</a>");
37 } else if (S_ISDIR(mode)) { 89 } else if (S_ISDIR(mode)) {
38 html("class='ls-dir'><a href='"); 90 cgit_tree_link(name, NULL, "ls-dir", cgit_query_head,
39 html_attr(cgit_pageurl(cgit_query_repo, "tree", 91 curr_rev, fullpath);
40 fmt("h=%s&amp;id=%s&amp;path=%s%s/",
41 curr_rev,
42 sha1_to_hex(sha1),
43 cgit_query_path ? cgit_query_path : "",
44 pathname)));
45 } else { 92 } else {
46 html("class='ls-blob'><a href='"); 93 cgit_tree_link(name, NULL, "ls-blob", cgit_query_head,
47 html_attr(cgit_pageurl(cgit_query_repo, "view", 94 curr_rev, fullpath);
48 fmt("h=%s&amp;id=%s&amp;path=%s%s", curr_rev,
49 sha1_to_hex(sha1),
50 cgit_query_path ? cgit_query_path : "",
51 pathname)));
52 } 95 }
53 htmlf("'>%s</a></td>", name); 96 htmlf("</td><td class='ls-size'>%li</td>", size);
54 htmlf("<td class='filesize'>%li</td>", size); 97
55 98 html("<td>");
56 html("<td class='links'><a href='"); 99 cgit_log_link("log", NULL, "button", cgit_query_head, curr_rev,
57 html_attr(cgit_pageurl(cgit_query_repo, "log", 100 fullpath, 0);
58 fmt("h=%s&amp;path=%s%s", 101 html("</td></tr>\n");
59 curr_rev,
60 cgit_query_path ? cgit_query_path : "",
61 pathname)));
62 html("'>history</a></td>");
63 html("</tr>\n");
64 free(name); 102 free(name);
65 return 0; 103 return 0;
66} 104}
67 105
68void cgit_print_tree(const char *rev, const char *hex, char *path) 106static void ls_head()
107{
108 html("<table class='list'>\n");
109 html("<tr class='nohover'>");
110 html("<th class='left'>Mode</th>");
111 html("<th class='left'>Name</th>");
112 html("<th class='right'>Size</th>");
113 html("<th/>");
114 html("</tr>\n");
115 header = 1;
116}
117
118static void ls_tail()
119{
120 if (!header)
121 return;
122 html("</table>\n");
123 header = 0;
124}
125
126static void ls_tree(const unsigned char *sha1, char *path)
69{ 127{
70 struct tree *tree; 128 struct tree *tree;
129
130 tree = parse_tree_indirect(sha1);
131 if (!tree) {
132 cgit_print_error(fmt("Not a tree object: %s",
133 sha1_to_hex(sha1)));
134 return;
135 }
136
137 ls_head();
138 read_tree_recursive(tree, "", 0, 1, NULL, ls_item);
139 ls_tail();
140}
141
142
143static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
144 const char *pathname, unsigned mode, int stage)
145{
146 static int state;
147 static char buffer[PATH_MAX];
148 char *url;
149
150 if (state == 0) {
151 memcpy(buffer, base, baselen);
152 strcpy(buffer+baselen, pathname);
153 url = cgit_pageurl(cgit_query_repo, "tree",
154 fmt("h=%s&amp;path=%s", curr_rev, buffer));
155 html("/");
156 cgit_tree_link(xstrdup(pathname), NULL, NULL, cgit_query_head,
157 curr_rev, buffer);
158
159 if (strcmp(match_path, buffer))
160 return READ_TREE_RECURSIVE;
161
162 if (S_ISDIR(mode)) {
163 state = 1;
164 ls_head();
165 return READ_TREE_RECURSIVE;
166 } else {
167 print_object(sha1, buffer);
168 return 0;
169 }
170 }
171 ls_item(sha1, base, baselen, pathname, mode, stage);
172 return 0;
173}
174
175
176/*
177 * Show a tree or a blob
178 * rev: the commit pointing at the root tree object
179 * path: path to tree or blob
180 */
181void cgit_print_tree(const char *rev, char *path)
182{
71 unsigned char sha1[20]; 183 unsigned char sha1[20];
72 struct commit *commit; 184 struct commit *commit;
185 const char *paths[] = {path, NULL};
186
187 if (!rev)
188 rev = cgit_query_head;
73 189
74 curr_rev = xstrdup(rev); 190 curr_rev = xstrdup(rev);
75 get_sha1(rev, sha1); 191 if (get_sha1(rev, sha1)) {
192 cgit_print_error(fmt("Invalid revision name: %s", rev));
193 return;
194 }
76 commit = lookup_commit_reference(sha1); 195 commit = lookup_commit_reference(sha1);
77 if (!commit || parse_commit(commit)) { 196 if (!commit || parse_commit(commit)) {
78 cgit_print_error(fmt("Invalid head: %s", rev)); 197 cgit_print_error(fmt("Invalid commit reference: %s", rev));
79 return; 198 return;
80 } 199 }
81 if (!hex)
82 hex = sha1_to_hex(commit->tree->object.sha1);
83 200
84 if (get_sha1_hex(hex, sha1)) { 201 html("path: <a href='");
85 cgit_print_error(fmt("Invalid object id: %s", hex)); 202 html_attr(cgit_pageurl(cgit_query_repo, "tree", fmt("h=%s", rev)));
86 return; 203 html("'>root</a>");
87 } 204
88 tree = parse_tree_indirect(sha1); 205 if (path == NULL) {
89 if (!tree) { 206 ls_tree(commit->tree->object.sha1, NULL);
90 cgit_print_error(fmt("Not a tree object: %s", hex));
91 return; 207 return;
92 } 208 }
93 209
94 html_txt(path); 210 match_path = path;
95 html("<table class='list'>\n"); 211 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree);
96 html("<tr class='nohover'>"); 212 ls_tail();
97 html("<th class='left'>Mode</th>");
98 html("<th class='left'>Name</th>");
99 html("<th class='right'>Size</th>");
100 html("<th/>");
101 html("</tr>\n");
102 read_tree_recursive(tree, "", 0, 1, NULL, print_entry);
103 html("</table>\n");
104} 213}
diff --git a/ui-view.c b/ui-view.c
deleted file mode 100644
index 8873415..0000000
--- a/ui-view.c
+++ /dev/null
@@ -1,55 +0,0 @@
1/* ui-view.c: functions to output _any_ object, given it's sha1
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
11void cgit_print_view(const char *hex, char *path)
12{
13 unsigned char sha1[20];
14 enum object_type type;
15 unsigned char *buf;
16 unsigned long size;
17
18 if (get_sha1_hex(hex, sha1)){
19 cgit_print_error(fmt("Bad hex value: %s", hex));
20 return;
21 }
22
23 type = sha1_object_info(sha1, &size);
24 if (type == OBJ_BAD) {
25 cgit_print_error(fmt("Bad object name: %s", hex));
26 return;
27 }
28
29 buf = read_sha1_file(sha1, &type, &size);
30 if (!buf) {
31 cgit_print_error(fmt("Error reading object %s", hex));
32 return;
33 }
34
35 buf[size] = '\0';
36 html("<table class='list'>\n");
37 html("<tr class='nohover'><th class='left'>");
38 if (path)
39 htmlf("%s (", path);
40 htmlf("%s %s, %li bytes", typename(type), hex, size);
41 if (path)
42 html(")");
43
44 html(" <a href='");
45 html_attr(cgit_pageurl(cgit_query_repo, "blob",
46 fmt("id=%s&amp;path=%s",
47 hex,
48 path)));
49 html("'>download</a>");
50 html("</th></tr>\n");
51 html("<tr><td class='blob'>\n");
52 html_txt(buf);
53 html("\n</td></tr>\n");
54 html("</table>\n");
55}