summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c3
-rw-r--r--cgit.h5
-rw-r--r--shared.c2
-rw-r--r--ui-shared.c24
-rw-r--r--ui-summary.c33
-rw-r--r--ui-tag.c74
7 files changed, 111 insertions, 32 deletions
diff --git a/Makefile b/Makefile
index cea09f1..50d0011 100644
--- a/Makefile
+++ b/Makefile
@@ -1,90 +1,90 @@
1prefix = /var/www/htdocs/cgit 1prefix = /var/www/htdocs/cgit
2SHA1_HEADER = <openssl/sha.h> 2SHA1_HEADER = <openssl/sha.h>
3CACHE_ROOT = /var/cache/cgit 3CACHE_ROOT = /var/cache/cgit
4CGIT_CONFIG = /etc/cgitrc 4CGIT_CONFIG = /etc/cgitrc
5CGIT_SCRIPT_NAME = cgit.cgi 5CGIT_SCRIPT_NAME = cgit.cgi
6 6
7# 7#
8# Let the user override the above settings. 8# Let the user override the above settings.
9# 9#
10-include cgit.conf 10-include cgit.conf
11 11
12 12
13CGIT_VERSION = 0.5 13CGIT_VERSION = 0.5
14 14
15all: cgit 15all: cgit
16 16
17VERSION: 17VERSION:
18 sh gen-version.sh 18 sh gen-version.sh
19 19
20-include VERSION 20-include VERSION
21 21
22 22
23EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 23EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
24OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 24OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
25 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ 25 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
26 ui-snapshot.o ui-blob.o 26 ui-snapshot.o ui-blob.o ui-tag.o
27 27
28CFLAGS += -Wall 28CFLAGS += -Wall
29 29
30ifdef DEBUG 30ifdef DEBUG
31 CFLAGS += -g 31 CFLAGS += -g
32endif 32endif
33 33
34CFLAGS += -Igit 34CFLAGS += -Igit
35CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 35CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
36CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 36CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
37CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 37CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
38CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 38CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
39 39
40 40
41# 41#
42# If make is run on a nongit platform, get the git sources as a tarball. 42# If make is run on a nongit platform, get the git sources as a tarball.
43# 43#
44GITVER = $(shell git version 2>/dev/null || echo nogit) 44GITVER = $(shell git version 2>/dev/null || echo nogit)
45ifeq ($(GITVER),nogit) 45ifeq ($(GITVER),nogit)
46GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2 46GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2
47INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git) 47INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git)
48else 48else
49INITGIT = ./submodules.sh -i 49INITGIT = ./submodules.sh -i
50endif 50endif
51 51
52 52
53cgit: cgit.c cgit.h VERSION $(OBJECTS) 53cgit: cgit.c cgit.h VERSION $(OBJECTS)
54 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 54 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
55 55
56$(OBJECTS): cgit.h git/libgit.a 56$(OBJECTS): cgit.h git/libgit.a
57 57
58git/libgit.a: 58git/libgit.a:
59 $(INITGIT) 59 $(INITGIT)
60 $(MAKE) -C git 60 $(MAKE) -C git
61 61
62# 62#
63# phony targets 63# phony targets
64# 64#
65install: all clean-cache 65install: all clean-cache
66 mkdir -p $(prefix) 66 mkdir -p $(prefix)
67 install cgit $(prefix)/$(CGIT_SCRIPT_NAME) 67 install cgit $(prefix)/$(CGIT_SCRIPT_NAME)
68 install cgit.css $(prefix)/cgit.css 68 install cgit.css $(prefix)/cgit.css
69 69
70clean-cgit: 70clean-cgit:
71 rm -f cgit VERSION *.o 71 rm -f cgit VERSION *.o
72 72
73distclean-cgit: clean-cgit 73distclean-cgit: clean-cgit
74 git clean -d -x 74 git clean -d -x
75 75
76clean-sub: 76clean-sub:
77 $(MAKE) -C git clean 77 $(MAKE) -C git clean
78 78
79distclean-sub: clean-sub 79distclean-sub: clean-sub
80 $(shell cd git && git clean -d -x) 80 $(shell cd git && git clean -d -x)
81 81
82clean-cache: 82clean-cache:
83 rm -rf $(CACHE_ROOT)/* 83 rm -rf $(CACHE_ROOT)/*
84 84
85clean: clean-cgit clean-sub 85clean: clean-cgit clean-sub
86 86
87distclean: distclean-cgit distclean-sub 87distclean: distclean-cgit distclean-sub
88 88
89version: clean-cgit 89version: clean-cgit
90 ./gen-version.sh 90 ./gen-version.sh
diff --git a/cgit.c b/cgit.c
index 1281bfa..4b91829 100644
--- a/cgit.c
+++ b/cgit.c
@@ -40,128 +40,131 @@ static int cgit_prepare_cache(struct cacheitem *item)
40 else if (cgit_query_has_sha1) 40 else if (cgit_query_has_sha1)
41 item->ttl = cgit_cache_static_ttl; 41 item->ttl = cgit_cache_static_ttl;
42 else 42 else
43 item->ttl = cgit_cache_repo_ttl; 43 item->ttl = cgit_cache_repo_ttl;
44 } 44 }
45 return 1; 45 return 1;
46} 46}
47 47
48static void cgit_print_repo_page(struct cacheitem *item) 48static void cgit_print_repo_page(struct cacheitem *item)
49{ 49{
50 char *title; 50 char *title;
51 int show_search; 51 int show_search;
52 52
53 if (!cgit_query_head) 53 if (!cgit_query_head)
54 cgit_query_head = cgit_repo->defbranch; 54 cgit_query_head = cgit_repo->defbranch;
55 55
56 if (chdir(cgit_repo->path)) { 56 if (chdir(cgit_repo->path)) {
57 title = fmt("%s - %s", cgit_root_title, "Bad request"); 57 title = fmt("%s - %s", cgit_root_title, "Bad request");
58 cgit_print_docstart(title, item); 58 cgit_print_docstart(title, item);
59 cgit_print_pageheader(title, 0); 59 cgit_print_pageheader(title, 0);
60 cgit_print_error(fmt("Unable to scan repository: %s", 60 cgit_print_error(fmt("Unable to scan repository: %s",
61 strerror(errno))); 61 strerror(errno)));
62 cgit_print_docend(); 62 cgit_print_docend();
63 return; 63 return;
64 } 64 }
65 65
66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
67 show_search = 0; 67 show_search = 0;
68 setenv("GIT_DIR", cgit_repo->path, 1); 68 setenv("GIT_DIR", cgit_repo->path, 1);
69 69
70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
71 cgit_print_snapshot(item, cgit_query_sha1, "zip", 71 cgit_print_snapshot(item, cgit_query_sha1, "zip",
72 cgit_repo->url, cgit_query_name); 72 cgit_repo->url, cgit_query_name);
73 return; 73 return;
74 } 74 }
75 75
76 if (cgit_cmd == CMD_BLOB) { 76 if (cgit_cmd == CMD_BLOB) {
77 cgit_print_blob(item, cgit_query_sha1, cgit_query_path); 77 cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
78 return; 78 return;
79 } 79 }
80 80
81 show_search = (cgit_cmd == CMD_LOG); 81 show_search = (cgit_cmd == CMD_LOG);
82 cgit_print_docstart(title, item); 82 cgit_print_docstart(title, item);
83 if (!cgit_cmd) { 83 if (!cgit_cmd) {
84 cgit_print_pageheader("summary", show_search); 84 cgit_print_pageheader("summary", show_search);
85 cgit_print_summary(); 85 cgit_print_summary();
86 cgit_print_docend(); 86 cgit_print_docend();
87 return; 87 return;
88 } 88 }
89 89
90 cgit_print_pageheader(cgit_query_page, show_search); 90 cgit_print_pageheader(cgit_query_page, show_search);
91 91
92 switch(cgit_cmd) { 92 switch(cgit_cmd) {
93 case CMD_LOG: 93 case CMD_LOG:
94 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 94 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
95 cgit_max_commit_count, cgit_query_search, 95 cgit_max_commit_count, cgit_query_search,
96 cgit_query_path, 1); 96 cgit_query_path, 1);
97 break; 97 break;
98 case CMD_TREE: 98 case CMD_TREE:
99 cgit_print_tree(cgit_query_sha1, cgit_query_path); 99 cgit_print_tree(cgit_query_sha1, cgit_query_path);
100 break; 100 break;
101 case CMD_COMMIT: 101 case CMD_COMMIT:
102 cgit_print_commit(cgit_query_sha1); 102 cgit_print_commit(cgit_query_sha1);
103 break; 103 break;
104 case CMD_TAG:
105 cgit_print_tag(cgit_query_sha1);
106 break;
104 case CMD_DIFF: 107 case CMD_DIFF:
105 cgit_print_diff(cgit_query_sha1, cgit_query_sha2); 108 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
106 break; 109 break;
107 default: 110 default:
108 cgit_print_error("Invalid request"); 111 cgit_print_error("Invalid request");
109 } 112 }
110 cgit_print_docend(); 113 cgit_print_docend();
111} 114}
112 115
113static void cgit_fill_cache(struct cacheitem *item, int use_cache) 116static void cgit_fill_cache(struct cacheitem *item, int use_cache)
114{ 117{
115 static char buf[PATH_MAX]; 118 static char buf[PATH_MAX];
116 int stdout2; 119 int stdout2;
117 120
118 getcwd(buf, sizeof(buf)); 121 getcwd(buf, sizeof(buf));
119 item->st.st_mtime = time(NULL); 122 item->st.st_mtime = time(NULL);
120 123
121 if (use_cache) { 124 if (use_cache) {
122 stdout2 = chk_positive(dup(STDOUT_FILENO), 125 stdout2 = chk_positive(dup(STDOUT_FILENO),
123 "Preserving STDOUT"); 126 "Preserving STDOUT");
124 chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); 127 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
125 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); 128 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
126 } 129 }
127 130
128 if (cgit_repo) 131 if (cgit_repo)
129 cgit_print_repo_page(item); 132 cgit_print_repo_page(item);
130 else 133 else
131 cgit_print_repolist(item); 134 cgit_print_repolist(item);
132 135
133 if (use_cache) { 136 if (use_cache) {
134 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT"); 137 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
135 chk_positive(dup2(stdout2, STDOUT_FILENO), 138 chk_positive(dup2(stdout2, STDOUT_FILENO),
136 "Restoring original STDOUT"); 139 "Restoring original STDOUT");
137 chk_zero(close(stdout2), "Closing temporary STDOUT"); 140 chk_zero(close(stdout2), "Closing temporary STDOUT");
138 } 141 }
139 142
140 chdir(buf); 143 chdir(buf);
141} 144}
142 145
143static void cgit_check_cache(struct cacheitem *item) 146static void cgit_check_cache(struct cacheitem *item)
144{ 147{
145 int i = 0; 148 int i = 0;
146 149
147 top: 150 top:
148 if (++i > cgit_max_lock_attempts) { 151 if (++i > cgit_max_lock_attempts) {
149 die("cgit_refresh_cache: unable to lock %s: %s", 152 die("cgit_refresh_cache: unable to lock %s: %s",
150 item->name, strerror(errno)); 153 item->name, strerror(errno));
151 } 154 }
152 if (!cache_exist(item)) { 155 if (!cache_exist(item)) {
153 if (!cache_lock(item)) { 156 if (!cache_lock(item)) {
154 sleep(1); 157 sleep(1);
155 goto top; 158 goto top;
156 } 159 }
157 if (!cache_exist(item)) { 160 if (!cache_exist(item)) {
158 cgit_fill_cache(item, 1); 161 cgit_fill_cache(item, 1);
159 cache_unlock(item); 162 cache_unlock(item);
160 } else { 163 } else {
161 cache_cancel_lock(item); 164 cache_cancel_lock(item);
162 } 165 }
163 } else if (cache_expired(item) && cache_lock(item)) { 166 } else if (cache_expired(item) && cache_lock(item)) {
164 if (cache_expired(item)) { 167 if (cache_expired(item)) {
165 cgit_fill_cache(item, 1); 168 cgit_fill_cache(item, 1);
166 cache_unlock(item); 169 cache_unlock(item);
167 } else { 170 } else {
diff --git a/cgit.h b/cgit.h
index 2ff5340..610a16d 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,94 +1,94 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19 19
20 20
21/* 21/*
22 * The valid cgit repo-commands 22 * The valid cgit repo-commands
23 */ 23 */
24#define CMD_LOG 1 24#define CMD_LOG 1
25#define CMD_COMMIT 2 25#define CMD_COMMIT 2
26#define CMD_DIFF 3 26#define CMD_DIFF 3
27#define CMD_TREE 4 27#define CMD_TREE 4
28#define CMD_BLOB 5 28#define CMD_BLOB 5
29#define CMD_SNAPSHOT 6 29#define CMD_SNAPSHOT 6
30 30#define CMD_TAG 7
31 31
32/* 32/*
33 * Dateformats used on misc. pages 33 * Dateformats used on misc. pages
34 */ 34 */
35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
36#define FMT_SHORTDATE "%Y-%m-%d" 36#define FMT_SHORTDATE "%Y-%m-%d"
37 37
38 38
39/* 39/*
40 * Limits used for relative dates 40 * Limits used for relative dates
41 */ 41 */
42#define TM_MIN 60 42#define TM_MIN 60
43#define TM_HOUR (TM_MIN * 60) 43#define TM_HOUR (TM_MIN * 60)
44#define TM_DAY (TM_HOUR * 24) 44#define TM_DAY (TM_HOUR * 24)
45#define TM_WEEK (TM_DAY * 7) 45#define TM_WEEK (TM_DAY * 7)
46#define TM_YEAR (TM_DAY * 365) 46#define TM_YEAR (TM_DAY * 365)
47#define TM_MONTH (TM_YEAR / 12.0) 47#define TM_MONTH (TM_YEAR / 12.0)
48 48
49 49
50typedef void (*configfn)(const char *name, const char *value); 50typedef void (*configfn)(const char *name, const char *value);
51typedef void (*filepair_fn)(struct diff_filepair *pair); 51typedef void (*filepair_fn)(struct diff_filepair *pair);
52typedef void (*linediff_fn)(char *line, int len); 52typedef void (*linediff_fn)(char *line, int len);
53 53
54struct cacheitem { 54struct cacheitem {
55 char *name; 55 char *name;
56 struct stat st; 56 struct stat st;
57 int ttl; 57 int ttl;
58 int fd; 58 int fd;
59}; 59};
60 60
61struct repoinfo { 61struct repoinfo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *group; 68 char *group;
69 char *module_link; 69 char *module_link;
70 char *readme; 70 char *readme;
71 int snapshots; 71 int snapshots;
72 int enable_log_filecount; 72 int enable_log_filecount;
73 int enable_log_linecount; 73 int enable_log_linecount;
74}; 74};
75 75
76struct repolist { 76struct repolist {
77 int length; 77 int length;
78 int count; 78 int count;
79 struct repoinfo *repos; 79 struct repoinfo *repos;
80}; 80};
81 81
82struct commitinfo { 82struct commitinfo {
83 struct commit *commit; 83 struct commit *commit;
84 char *author; 84 char *author;
85 char *author_email; 85 char *author_email;
86 unsigned long author_date; 86 unsigned long author_date;
87 char *committer; 87 char *committer;
88 char *committer_email; 88 char *committer_email;
89 unsigned long committer_date; 89 unsigned long committer_date;
90 char *subject; 90 char *subject;
91 char *msg; 91 char *msg;
92}; 92};
93 93
94struct taginfo { 94struct taginfo {
@@ -151,86 +151,89 @@ extern int htmlfd;
151 151
152extern int cgit_get_cmd_index(const char *cmd); 152extern int cgit_get_cmd_index(const char *cmd);
153extern struct repoinfo *cgit_get_repoinfo(const char *url); 153extern struct repoinfo *cgit_get_repoinfo(const char *url);
154extern void cgit_global_config_cb(const char *name, const char *value); 154extern void cgit_global_config_cb(const char *name, const char *value);
155extern void cgit_repo_config_cb(const char *name, const char *value); 155extern void cgit_repo_config_cb(const char *name, const char *value);
156extern void cgit_querystring_cb(const char *name, const char *value); 156extern void cgit_querystring_cb(const char *name, const char *value);
157 157
158extern int chk_zero(int result, char *msg); 158extern int chk_zero(int result, char *msg);
159extern int chk_positive(int result, char *msg); 159extern int chk_positive(int result, char *msg);
160 160
161extern int hextoint(char c); 161extern int hextoint(char c);
162extern char *trim_end(const char *str, char c); 162extern char *trim_end(const char *str, char c);
163 163
164extern void *cgit_free_commitinfo(struct commitinfo *info); 164extern void *cgit_free_commitinfo(struct commitinfo *info);
165 165
166extern int cgit_diff_files(const unsigned char *old_sha1, 166extern int cgit_diff_files(const unsigned char *old_sha1,
167 const unsigned char *new_sha1, 167 const unsigned char *new_sha1,
168 linediff_fn fn); 168 linediff_fn fn);
169 169
170extern void cgit_diff_tree(const unsigned char *old_sha1, 170extern void cgit_diff_tree(const unsigned char *old_sha1,
171 const unsigned char *new_sha1, 171 const unsigned char *new_sha1,
172 filepair_fn fn); 172 filepair_fn fn);
173 173
174extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 174extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
175 175
176extern char *fmt(const char *format,...); 176extern char *fmt(const char *format,...);
177 177
178extern void html(const char *txt); 178extern void html(const char *txt);
179extern void htmlf(const char *format,...); 179extern void htmlf(const char *format,...);
180extern void html_txt(char *txt); 180extern void html_txt(char *txt);
181extern void html_ntxt(int len, char *txt); 181extern void html_ntxt(int len, char *txt);
182extern void html_attr(char *txt); 182extern void html_attr(char *txt);
183extern void html_hidden(char *name, char *value); 183extern void html_hidden(char *name, char *value);
184extern void html_link_open(char *url, char *title, char *class); 184extern void html_link_open(char *url, char *title, char *class);
185extern void html_link_close(void); 185extern void html_link_close(void);
186extern void html_filemode(unsigned short mode); 186extern void html_filemode(unsigned short mode);
187extern int html_include(const char *filename); 187extern int html_include(const char *filename);
188 188
189extern int cgit_read_config(const char *filename, configfn fn); 189extern int cgit_read_config(const char *filename, configfn fn);
190extern int cgit_parse_query(char *txt, configfn fn); 190extern int cgit_parse_query(char *txt, configfn fn);
191extern struct commitinfo *cgit_parse_commit(struct commit *commit); 191extern struct commitinfo *cgit_parse_commit(struct commit *commit);
192extern struct taginfo *cgit_parse_tag(struct tag *tag); 192extern struct taginfo *cgit_parse_tag(struct tag *tag);
193extern void cgit_parse_url(const char *url); 193extern void cgit_parse_url(const char *url);
194 194
195extern char *cache_safe_filename(const char *unsafe); 195extern char *cache_safe_filename(const char *unsafe);
196extern int cache_lock(struct cacheitem *item); 196extern int cache_lock(struct cacheitem *item);
197extern int cache_unlock(struct cacheitem *item); 197extern int cache_unlock(struct cacheitem *item);
198extern int cache_cancel_lock(struct cacheitem *item); 198extern int cache_cancel_lock(struct cacheitem *item);
199extern int cache_exist(struct cacheitem *item); 199extern int cache_exist(struct cacheitem *item);
200extern int cache_expired(struct cacheitem *item); 200extern int cache_expired(struct cacheitem *item);
201 201
202extern char *cgit_repourl(const char *reponame); 202extern char *cgit_repourl(const char *reponame);
203extern char *cgit_pageurl(const char *reponame, const char *pagename, 203extern char *cgit_pageurl(const char *reponame, const char *pagename,
204 const char *query); 204 const char *query);
205 205
206extern void cgit_tree_link(char *name, char *title, char *class, char *head, 206extern void cgit_tree_link(char *name, char *title, char *class, char *head,
207 char *rev, char *path); 207 char *rev, char *path);
208extern void cgit_log_link(char *name, char *title, char *class, char *head, 208extern void cgit_log_link(char *name, char *title, char *class, char *head,
209 char *rev, char *path, int ofs); 209 char *rev, char *path, int ofs);
210extern void cgit_commit_link(char *name, char *title, char *class, char *head, 210extern void cgit_commit_link(char *name, char *title, char *class, char *head,
211 char *rev); 211 char *rev);
212extern void cgit_diff_link(char *name, char *title, char *class, char *head, 212extern void cgit_diff_link(char *name, char *title, char *class, char *head,
213 char *new_rev, char *old_rev, char *path); 213 char *new_rev, char *old_rev, char *path);
214 214
215extern void cgit_object_link(struct object *obj);
216
215extern void cgit_print_error(char *msg); 217extern void cgit_print_error(char *msg);
216extern void cgit_print_date(time_t secs, char *format); 218extern void cgit_print_date(time_t secs, char *format);
217extern void cgit_print_age(time_t t, time_t max_relative, char *format); 219extern void cgit_print_age(time_t t, time_t max_relative, char *format);
218extern void cgit_print_docstart(char *title, struct cacheitem *item); 220extern void cgit_print_docstart(char *title, struct cacheitem *item);
219extern void cgit_print_docend(); 221extern void cgit_print_docend();
220extern void cgit_print_pageheader(char *title, int show_search); 222extern void cgit_print_pageheader(char *title, int show_search);
221extern void cgit_print_snapshot_start(const char *mimetype, 223extern void cgit_print_snapshot_start(const char *mimetype,
222 const char *filename, 224 const char *filename,
223 struct cacheitem *item); 225 struct cacheitem *item);
224 226
225extern void cgit_print_repolist(struct cacheitem *item); 227extern void cgit_print_repolist(struct cacheitem *item);
226extern void cgit_print_summary(); 228extern void cgit_print_summary();
227extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 229extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
228extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 230extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
229extern void cgit_print_tree(const char *rev, char *path); 231extern void cgit_print_tree(const char *rev, char *path);
230extern void cgit_print_commit(char *hex); 232extern void cgit_print_commit(char *hex);
233extern void cgit_print_tag(char *revname);
231extern void cgit_print_diff(const char *new_hex, const char *old_hex); 234extern void cgit_print_diff(const char *new_hex, const char *old_hex);
232extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 235extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
233 const char *format, const char *prefix, 236 const char *format, const char *prefix,
234 const char *filename); 237 const char *filename);
235 238
236#endif /* CGIT_H */ 239#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index 1a5b866..06693b0 100644
--- a/shared.c
+++ b/shared.c
@@ -2,129 +2,129 @@
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11struct repolist cgit_repolist; 11struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13int cgit_cmd; 13int cgit_cmd;
14 14
15const char *cgit_version = CGIT_VERSION; 15const char *cgit_version = CGIT_VERSION;
16 16
17char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
18char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
19char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
20char *cgit_index_header = NULL; 20char *cgit_index_header = NULL;
21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
22char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; 22char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
23char *cgit_agefile = "info/web/last-modified"; 23char *cgit_agefile = "info/web/last-modified";
24char *cgit_virtual_root = NULL; 24char *cgit_virtual_root = NULL;
25char *cgit_script_name = CGIT_SCRIPT_NAME; 25char *cgit_script_name = CGIT_SCRIPT_NAME;
26char *cgit_cache_root = "/var/cache/cgit"; 26char *cgit_cache_root = "/var/cache/cgit";
27char *cgit_repo_group = NULL; 27char *cgit_repo_group = NULL;
28 28
29int cgit_nocache = 0; 29int cgit_nocache = 0;
30int cgit_snapshots = 0; 30int cgit_snapshots = 0;
31int cgit_enable_index_links = 0; 31int cgit_enable_index_links = 0;
32int cgit_enable_log_filecount = 0; 32int cgit_enable_log_filecount = 0;
33int cgit_enable_log_linecount = 0; 33int cgit_enable_log_linecount = 0;
34int cgit_max_lock_attempts = 5; 34int cgit_max_lock_attempts = 5;
35int cgit_cache_root_ttl = 5; 35int cgit_cache_root_ttl = 5;
36int cgit_cache_repo_ttl = 5; 36int cgit_cache_repo_ttl = 5;
37int cgit_cache_dynamic_ttl = 5; 37int cgit_cache_dynamic_ttl = 5;
38int cgit_cache_static_ttl = -1; 38int cgit_cache_static_ttl = -1;
39int cgit_cache_max_create_time = 5; 39int cgit_cache_max_create_time = 5;
40int cgit_summary_log = 0; 40int cgit_summary_log = 0;
41 41
42int cgit_max_msg_len = 60; 42int cgit_max_msg_len = 60;
43int cgit_max_repodesc_len = 60; 43int cgit_max_repodesc_len = 60;
44int cgit_max_commit_count = 50; 44int cgit_max_commit_count = 50;
45 45
46int cgit_query_has_symref = 0; 46int cgit_query_has_symref = 0;
47int cgit_query_has_sha1 = 0; 47int cgit_query_has_sha1 = 0;
48 48
49char *cgit_querystring = NULL; 49char *cgit_querystring = NULL;
50char *cgit_query_repo = NULL; 50char *cgit_query_repo = NULL;
51char *cgit_query_page = NULL; 51char *cgit_query_page = NULL;
52char *cgit_query_head = NULL; 52char *cgit_query_head = NULL;
53char *cgit_query_search = NULL; 53char *cgit_query_search = NULL;
54char *cgit_query_sha1 = NULL; 54char *cgit_query_sha1 = NULL;
55char *cgit_query_sha2 = NULL; 55char *cgit_query_sha2 = NULL;
56char *cgit_query_path = NULL; 56char *cgit_query_path = NULL;
57char *cgit_query_name = NULL; 57char *cgit_query_name = NULL;
58int cgit_query_ofs = 0; 58int cgit_query_ofs = 0;
59 59
60int htmlfd = 0; 60int htmlfd = 0;
61 61
62 62
63int cgit_get_cmd_index(const char *cmd) 63int cgit_get_cmd_index(const char *cmd)
64{ 64{
65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 65 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
66 "snapshot", NULL}; 66 "snapshot", "tag", NULL};
67 int i; 67 int i;
68 68
69 for(i = 0; cmds[i]; i++) 69 for(i = 0; cmds[i]; i++)
70 if (!strcmp(cmd, cmds[i])) 70 if (!strcmp(cmd, cmds[i]))
71 return i + 1; 71 return i + 1;
72 return 0; 72 return 0;
73} 73}
74 74
75int chk_zero(int result, char *msg) 75int chk_zero(int result, char *msg)
76{ 76{
77 if (result != 0) 77 if (result != 0)
78 die("%s: %s", msg, strerror(errno)); 78 die("%s: %s", msg, strerror(errno));
79 return result; 79 return result;
80} 80}
81 81
82int chk_positive(int result, char *msg) 82int chk_positive(int result, char *msg)
83{ 83{
84 if (result <= 0) 84 if (result <= 0)
85 die("%s: %s", msg, strerror(errno)); 85 die("%s: %s", msg, strerror(errno));
86 return result; 86 return result;
87} 87}
88 88
89struct repoinfo *add_repo(const char *url) 89struct repoinfo *add_repo(const char *url)
90{ 90{
91 struct repoinfo *ret; 91 struct repoinfo *ret;
92 92
93 if (++cgit_repolist.count > cgit_repolist.length) { 93 if (++cgit_repolist.count > cgit_repolist.length) {
94 if (cgit_repolist.length == 0) 94 if (cgit_repolist.length == 0)
95 cgit_repolist.length = 8; 95 cgit_repolist.length = 8;
96 else 96 else
97 cgit_repolist.length *= 2; 97 cgit_repolist.length *= 2;
98 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 98 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
99 cgit_repolist.length * 99 cgit_repolist.length *
100 sizeof(struct repoinfo)); 100 sizeof(struct repoinfo));
101 } 101 }
102 102
103 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 103 ret = &cgit_repolist.repos[cgit_repolist.count-1];
104 ret->url = xstrdup(url); 104 ret->url = xstrdup(url);
105 ret->name = ret->url; 105 ret->name = ret->url;
106 ret->path = NULL; 106 ret->path = NULL;
107 ret->desc = NULL; 107 ret->desc = NULL;
108 ret->owner = NULL; 108 ret->owner = NULL;
109 ret->group = cgit_repo_group; 109 ret->group = cgit_repo_group;
110 ret->defbranch = "master"; 110 ret->defbranch = "master";
111 ret->snapshots = cgit_snapshots; 111 ret->snapshots = cgit_snapshots;
112 ret->enable_log_filecount = cgit_enable_log_filecount; 112 ret->enable_log_filecount = cgit_enable_log_filecount;
113 ret->enable_log_linecount = cgit_enable_log_linecount; 113 ret->enable_log_linecount = cgit_enable_log_linecount;
114 ret->module_link = cgit_module_link; 114 ret->module_link = cgit_module_link;
115 ret->readme = NULL; 115 ret->readme = NULL;
116 return ret; 116 return ret;
117} 117}
118 118
119struct repoinfo *cgit_get_repoinfo(const char *url) 119struct repoinfo *cgit_get_repoinfo(const char *url)
120{ 120{
121 int i; 121 int i;
122 struct repoinfo *repo; 122 struct repoinfo *repo;
123 123
124 for (i=0; i<cgit_repolist.count; i++) { 124 for (i=0; i<cgit_repolist.count; i++) {
125 repo = &cgit_repolist.repos[i]; 125 repo = &cgit_repolist.repos[i];
126 if (!strcmp(repo->url, url)) 126 if (!strcmp(repo->url, url))
127 return repo; 127 return repo;
128 } 128 }
129 return NULL; 129 return NULL;
130} 130}
diff --git a/ui-shared.c b/ui-shared.c
index d4376ce..fd71c12 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -157,128 +157,152 @@ static void reporevlink(char *page, char *name, char *title, char *class,
157} 157}
158 158
159void cgit_tree_link(char *name, char *title, char *class, char *head, 159void cgit_tree_link(char *name, char *title, char *class, char *head,
160 char *rev, char *path) 160 char *rev, char *path)
161{ 161{
162 reporevlink("tree", name, title, class, head, rev, path); 162 reporevlink("tree", name, title, class, head, rev, path);
163} 163}
164 164
165void cgit_log_link(char *name, char *title, char *class, char *head, 165void cgit_log_link(char *name, char *title, char *class, char *head,
166 char *rev, char *path, int ofs) 166 char *rev, char *path, int ofs)
167{ 167{
168 char *delim; 168 char *delim;
169 169
170 delim = repolink(title, class, "log", head, path); 170 delim = repolink(title, class, "log", head, path);
171 if (rev && strcmp(rev, cgit_query_head)) { 171 if (rev && strcmp(rev, cgit_query_head)) {
172 html(delim); 172 html(delim);
173 html("id="); 173 html("id=");
174 html_attr(rev); 174 html_attr(rev);
175 delim = "&"; 175 delim = "&";
176 } 176 }
177 if (ofs > 0) { 177 if (ofs > 0) {
178 html(delim); 178 html(delim);
179 html("ofs="); 179 html("ofs=");
180 htmlf("%d", ofs); 180 htmlf("%d", ofs);
181 } 181 }
182 html("'>"); 182 html("'>");
183 html_txt(name); 183 html_txt(name);
184 html("</a>"); 184 html("</a>");
185} 185}
186 186
187void cgit_commit_link(char *name, char *title, char *class, char *head, 187void cgit_commit_link(char *name, char *title, char *class, char *head,
188 char *rev) 188 char *rev)
189{ 189{
190 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 190 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
191 name[cgit_max_msg_len] = '\0'; 191 name[cgit_max_msg_len] = '\0';
192 name[cgit_max_msg_len - 1] = '.'; 192 name[cgit_max_msg_len - 1] = '.';
193 name[cgit_max_msg_len - 2] = '.'; 193 name[cgit_max_msg_len - 2] = '.';
194 name[cgit_max_msg_len - 3] = '.'; 194 name[cgit_max_msg_len - 3] = '.';
195 } 195 }
196 reporevlink("commit", name, title, class, head, rev, NULL); 196 reporevlink("commit", name, title, class, head, rev, NULL);
197} 197}
198 198
199void cgit_diff_link(char *name, char *title, char *class, char *head, 199void cgit_diff_link(char *name, char *title, char *class, char *head,
200 char *new_rev, char *old_rev, char *path) 200 char *new_rev, char *old_rev, char *path)
201{ 201{
202 char *delim; 202 char *delim;
203 203
204 delim = repolink(title, class, "diff", head, path); 204 delim = repolink(title, class, "diff", head, path);
205 if (new_rev && strcmp(new_rev, cgit_query_head)) { 205 if (new_rev && strcmp(new_rev, cgit_query_head)) {
206 html(delim); 206 html(delim);
207 html("id="); 207 html("id=");
208 html_attr(new_rev); 208 html_attr(new_rev);
209 delim = "&amp;"; 209 delim = "&amp;";
210 } 210 }
211 if (old_rev) { 211 if (old_rev) {
212 html(delim); 212 html(delim);
213 html("id2="); 213 html("id2=");
214 html_attr(old_rev); 214 html_attr(old_rev);
215 } 215 }
216 html("'>"); 216 html("'>");
217 html_txt(name); 217 html_txt(name);
218 html("</a>"); 218 html("</a>");
219} 219}
220 220
221void cgit_object_link(struct object *obj)
222{
223 char *page, *arg, *url;
224
225 if (obj->type == OBJ_COMMIT) {
226 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
227 cgit_query_head, sha1_to_hex(obj->sha1));
228 return;
229 } else if (obj->type == OBJ_TREE) {
230 page = "tree";
231 arg = "id";
232 } else {
233 page = "blob";
234 arg = "id";
235 }
236
237 url = cgit_pageurl(cgit_query_repo, page,
238 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
239 html_link_open(url, NULL, NULL);
240 htmlf("%s %s", typename(obj->type),
241 sha1_to_hex(obj->sha1));
242 html_link_close();
243}
244
221void cgit_print_date(time_t secs, char *format) 245void cgit_print_date(time_t secs, char *format)
222{ 246{
223 char buf[64]; 247 char buf[64];
224 struct tm *time; 248 struct tm *time;
225 249
226 time = gmtime(&secs); 250 time = gmtime(&secs);
227 strftime(buf, sizeof(buf)-1, format, time); 251 strftime(buf, sizeof(buf)-1, format, time);
228 html_txt(buf); 252 html_txt(buf);
229} 253}
230 254
231void cgit_print_age(time_t t, time_t max_relative, char *format) 255void cgit_print_age(time_t t, time_t max_relative, char *format)
232{ 256{
233 time_t now, secs; 257 time_t now, secs;
234 258
235 time(&now); 259 time(&now);
236 secs = now - t; 260 secs = now - t;
237 261
238 if (secs > max_relative && max_relative >= 0) { 262 if (secs > max_relative && max_relative >= 0) {
239 cgit_print_date(t, format); 263 cgit_print_date(t, format);
240 return; 264 return;
241 } 265 }
242 266
243 if (secs < TM_HOUR * 2) { 267 if (secs < TM_HOUR * 2) {
244 htmlf("<span class='age-mins'>%.0f min.</span>", 268 htmlf("<span class='age-mins'>%.0f min.</span>",
245 secs * 1.0 / TM_MIN); 269 secs * 1.0 / TM_MIN);
246 return; 270 return;
247 } 271 }
248 if (secs < TM_DAY * 2) { 272 if (secs < TM_DAY * 2) {
249 htmlf("<span class='age-hours'>%.0f hours</span>", 273 htmlf("<span class='age-hours'>%.0f hours</span>",
250 secs * 1.0 / TM_HOUR); 274 secs * 1.0 / TM_HOUR);
251 return; 275 return;
252 } 276 }
253 if (secs < TM_WEEK * 2) { 277 if (secs < TM_WEEK * 2) {
254 htmlf("<span class='age-days'>%.0f days</span>", 278 htmlf("<span class='age-days'>%.0f days</span>",
255 secs * 1.0 / TM_DAY); 279 secs * 1.0 / TM_DAY);
256 return; 280 return;
257 } 281 }
258 if (secs < TM_MONTH * 2) { 282 if (secs < TM_MONTH * 2) {
259 htmlf("<span class='age-weeks'>%.0f weeks</span>", 283 htmlf("<span class='age-weeks'>%.0f weeks</span>",
260 secs * 1.0 / TM_WEEK); 284 secs * 1.0 / TM_WEEK);
261 return; 285 return;
262 } 286 }
263 if (secs < TM_YEAR * 2) { 287 if (secs < TM_YEAR * 2) {
264 htmlf("<span class='age-months'>%.0f months</span>", 288 htmlf("<span class='age-months'>%.0f months</span>",
265 secs * 1.0 / TM_MONTH); 289 secs * 1.0 / TM_MONTH);
266 return; 290 return;
267 } 291 }
268 htmlf("<span class='age-years'>%.0f years</span>", 292 htmlf("<span class='age-years'>%.0f years</span>",
269 secs * 1.0 / TM_YEAR); 293 secs * 1.0 / TM_YEAR);
270} 294}
271 295
272void cgit_print_docstart(char *title, struct cacheitem *item) 296void cgit_print_docstart(char *title, struct cacheitem *item)
273{ 297{
274 html("Content-Type: text/html; charset=utf-8\n"); 298 html("Content-Type: text/html; charset=utf-8\n");
275 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 299 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
276 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 300 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
277 ttl_seconds(item->ttl))); 301 ttl_seconds(item->ttl)));
278 html("\n"); 302 html("\n");
279 html(cgit_doctype); 303 html(cgit_doctype);
280 html("<html>\n"); 304 html("<html>\n");
281 html("<head>\n"); 305 html("<head>\n");
282 html("<title>"); 306 html("<title>");
283 html_txt(title); 307 html_txt(title);
284 html("</title>\n"); 308 html("</title>\n");
diff --git a/ui-summary.c b/ui-summary.c
index b4bc6d8..de8a180 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -1,187 +1,162 @@
1/* ui-summary.c: functions for generating repo summary page 1/* ui-summary.c: functions for generating repo summary page
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11static int header; 11static int header;
12 12
13static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1, 13static int cgit_print_branch_cb(const char *refname, const unsigned char *sha1,
14 int flags, void *cb_data) 14 int flags, void *cb_data)
15{ 15{
16 struct commit *commit; 16 struct commit *commit;
17 struct commitinfo *info; 17 struct commitinfo *info;
18 char buf[256]; 18 char buf[256];
19 char *ref; 19 char *ref;
20 20
21 ref = xstrdup(refname); 21 ref = xstrdup(refname);
22 strncpy(buf, refname, sizeof(buf)); 22 strncpy(buf, refname, sizeof(buf));
23 commit = lookup_commit(sha1); 23 commit = lookup_commit(sha1);
24 // object is not really parsed at this point, because of some fallout 24 // object is not really parsed at this point, because of some fallout
25 // from previous calls to git functions in cgit_print_log() 25 // from previous calls to git functions in cgit_print_log()
26 commit->object.parsed = 0; 26 commit->object.parsed = 0;
27 if (commit && !parse_commit(commit)){ 27 if (commit && !parse_commit(commit)){
28 info = cgit_parse_commit(commit); 28 info = cgit_parse_commit(commit);
29 html("<tr><td>"); 29 html("<tr><td>");
30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0); 30 cgit_log_link(ref, NULL, NULL, ref, NULL, NULL, 0);
31 html("</td><td>"); 31 html("</td><td>");
32 cgit_print_age(commit->date, -1, NULL); 32 cgit_print_age(commit->date, -1, NULL);
33 html("</td><td>"); 33 html("</td><td>");
34 html_txt(info->author); 34 html_txt(info->author);
35 html("</td><td>"); 35 html("</td><td>");
36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL); 36 cgit_commit_link(info->subject, NULL, NULL, ref, NULL);
37 html("</td></tr>\n"); 37 html("</td></tr>\n");
38 cgit_free_commitinfo(info); 38 cgit_free_commitinfo(info);
39 } else { 39 } else {
40 html("<tr><td>"); 40 html("<tr><td>");
41 html_txt(buf); 41 html_txt(buf);
42 html("</td><td colspan='3'>"); 42 html("</td><td colspan='3'>");
43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1)); 43 htmlf("*** bad ref %s ***", sha1_to_hex(sha1));
44 html("</td></tr>\n"); 44 html("</td></tr>\n");
45 } 45 }
46 free(ref); 46 free(ref);
47 return 0; 47 return 0;
48} 48}
49 49
50
51static void cgit_print_object_ref(struct object *obj)
52{
53 char *page, *arg, *url;
54
55 if (obj->type == OBJ_COMMIT) {
56 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
57 cgit_query_head, sha1_to_hex(obj->sha1));
58 return;
59 } else if (obj->type == OBJ_TREE) {
60 page = "tree";
61 arg = "id";
62 } else {
63 page = "view";
64 arg = "id";
65 }
66
67 url = cgit_pageurl(cgit_query_repo, page,
68 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
69 html_link_open(url, NULL, NULL);
70 htmlf("%s %s", typename(obj->type),
71 sha1_to_hex(obj->sha1));
72 html_link_close();
73}
74
75static void print_tag_header() 50static void print_tag_header()
76{ 51{
77 html("<tr class='nohover'><th class='left'>Tag</th>" 52 html("<tr class='nohover'><th class='left'>Tag</th>"
78 "<th class='left'>Age</th>" 53 "<th class='left'>Age</th>"
79 "<th class='left'>Author</th>" 54 "<th class='left'>Author</th>"
80 "<th class='left'>Reference</th></tr>\n"); 55 "<th class='left'>Reference</th></tr>\n");
81 header = 1; 56 header = 1;
82} 57}
83 58
84static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1, 59static int cgit_print_tag_cb(const char *refname, const unsigned char *sha1,
85 int flags, void *cb_data) 60 int flags, void *cb_data)
86{ 61{
87 struct tag *tag; 62 struct tag *tag;
88 struct taginfo *info; 63 struct taginfo *info;
89 struct object *obj; 64 struct object *obj;
90 char buf[256], *url; 65 char buf[256], *url;
91 66
92 strncpy(buf, refname, sizeof(buf)); 67 strncpy(buf, refname, sizeof(buf));
93 obj = parse_object(sha1); 68 obj = parse_object(sha1);
94 if (!obj) 69 if (!obj)
95 return 1; 70 return 1;
96 if (obj->type == OBJ_TAG) { 71 if (obj->type == OBJ_TAG) {
97 tag = lookup_tag(sha1); 72 tag = lookup_tag(sha1);
98 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 73 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
99 return 2; 74 return 2;
100 if (!header) 75 if (!header)
101 print_tag_header(); 76 print_tag_header();
102 html("<tr><td>"); 77 html("<tr><td>");
103 url = cgit_pageurl(cgit_query_repo, "view", 78 url = cgit_pageurl(cgit_query_repo, "tag",
104 fmt("id=%s", sha1_to_hex(sha1))); 79 fmt("id=%s", refname));
105 html_link_open(url, NULL, NULL); 80 html_link_open(url, NULL, NULL);
106 html_txt(buf); 81 html_txt(buf);
107 html_link_close(); 82 html_link_close();
108 html("</td><td>"); 83 html("</td><td>");
109 if (info->tagger_date > 0) 84 if (info->tagger_date > 0)
110 cgit_print_age(info->tagger_date, -1, NULL); 85 cgit_print_age(info->tagger_date, -1, NULL);
111 html("</td><td>"); 86 html("</td><td>");
112 if (info->tagger) 87 if (info->tagger)
113 html(info->tagger); 88 html(info->tagger);
114 html("</td><td>"); 89 html("</td><td>");
115 cgit_print_object_ref(tag->tagged); 90 cgit_object_link(tag->tagged);
116 html("</td></tr>\n"); 91 html("</td></tr>\n");
117 } else { 92 } else {
118 if (!header) 93 if (!header)
119 print_tag_header(); 94 print_tag_header();
120 html("<tr><td>"); 95 html("<tr><td>");
121 html_txt(buf); 96 html_txt(buf);
122 html("</td><td colspan='2'/><td>"); 97 html("</td><td colspan='2'/><td>");
123 cgit_print_object_ref(obj); 98 cgit_object_link(obj);
124 html("</td></tr>\n"); 99 html("</td></tr>\n");
125 } 100 }
126 return 0; 101 return 0;
127} 102}
128 103
129static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1, 104static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
130 int flags, void *cb_data) 105 int flags, void *cb_data)
131{ 106{
132 struct tag *tag; 107 struct tag *tag;
133 struct taginfo *info; 108 struct taginfo *info;
134 struct object *obj; 109 struct object *obj;
135 char buf[256], *url; 110 char buf[256], *url;
136 unsigned char fileid[20]; 111 unsigned char fileid[20];
137 112
138 if (prefixcmp(refname, "refs/archives")) 113 if (prefixcmp(refname, "refs/archives"))
139 return 0; 114 return 0;
140 strncpy(buf, refname+14, sizeof(buf)); 115 strncpy(buf, refname+14, sizeof(buf));
141 obj = parse_object(sha1); 116 obj = parse_object(sha1);
142 if (!obj) 117 if (!obj)
143 return 1; 118 return 1;
144 if (obj->type == OBJ_TAG) { 119 if (obj->type == OBJ_TAG) {
145 tag = lookup_tag(sha1); 120 tag = lookup_tag(sha1);
146 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 121 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
147 return 0; 122 return 0;
148 hashcpy(fileid, tag->tagged->sha1); 123 hashcpy(fileid, tag->tagged->sha1);
149 } else if (obj->type != OBJ_BLOB) { 124 } else if (obj->type != OBJ_BLOB) {
150 return 0; 125 return 0;
151 } else { 126 } else {
152 hashcpy(fileid, sha1); 127 hashcpy(fileid, sha1);
153 } 128 }
154 if (!header) { 129 if (!header) {
155 html("<table id='downloads'>"); 130 html("<table id='downloads'>");
156 html("<tr><th>Downloads</th></tr>"); 131 html("<tr><th>Downloads</th></tr>");
157 header = 1; 132 header = 1;
158 } 133 }
159 html("<tr><td>"); 134 html("<tr><td>");
160 url = cgit_pageurl(cgit_query_repo, "blob", 135 url = cgit_pageurl(cgit_query_repo, "blob",
161 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 136 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
162 buf)); 137 buf));
163 html_link_open(url, NULL, NULL); 138 html_link_open(url, NULL, NULL);
164 html_txt(buf); 139 html_txt(buf);
165 html_link_close(); 140 html_link_close();
166 html("</td></tr>"); 141 html("</td></tr>");
167 return 0; 142 return 0;
168} 143}
169 144
170static void cgit_print_branches() 145static void cgit_print_branches()
171{ 146{
172 html("<tr class='nohover'><th class='left'>Branch</th>" 147 html("<tr class='nohover'><th class='left'>Branch</th>"
173 "<th class='left'>Idle</th>" 148 "<th class='left'>Idle</th>"
174 "<th class='left'>Author</th>" 149 "<th class='left'>Author</th>"
175 "<th class='left'>Head commit</th></tr>\n"); 150 "<th class='left'>Head commit</th></tr>\n");
176 for_each_branch_ref(cgit_print_branch_cb, NULL); 151 for_each_branch_ref(cgit_print_branch_cb, NULL);
177} 152}
178 153
179static void cgit_print_tags() 154static void cgit_print_tags()
180{ 155{
181 header = 0; 156 header = 0;
182 for_each_tag_ref(cgit_print_tag_cb, NULL); 157 for_each_tag_ref(cgit_print_tag_cb, NULL);
183} 158}
184 159
185static void cgit_print_archives() 160static void cgit_print_archives()
186{ 161{
187 header = 0; 162 header = 0;
diff --git a/ui-tag.c b/ui-tag.c
new file mode 100644
index 0000000..a6989ff
--- a/dev/null
+++ b/ui-tag.c
@@ -0,0 +1,74 @@
1/* ui-tag.c: display a tag
2 *
3 * Copyright (C) 2007 Lars Hjemli
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9#include "cgit.h"
10
11
12static void print_tag_content(char *buf)
13{
14 char *p;
15
16 if (!buf)
17 return;
18
19 html("<div class='commit-subject'>");
20 p = strchr(buf, '\n');
21 if (p)
22 *p = '\0';
23 html_txt(buf);
24 html("</div>");
25 if (p) {
26 html("<div class='commit-msg'>");
27 html_txt(++p);
28 html("</div>");
29 }
30}
31
32void cgit_print_tag(char *revname)
33{
34 unsigned char sha1[20];
35 struct object *obj;
36 struct tag *tag;
37 struct taginfo *info;
38
39 if (get_sha1(revname, sha1)) {
40 cgit_print_error(fmt("Bad tag reference: %s", revname));
41 return;
42 }
43 obj = parse_object(sha1);
44 if (!obj) {
45 cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1)));
46 return;
47 }
48 if (obj->type == OBJ_TAG) {
49 tag = lookup_tag(sha1);
50 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
51 cgit_print_error(fmt("Bad tag object: %s", revname));
52 return;
53 }
54 html("<table class='commit-info'>\n");
55 htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n",
56 revname, sha1_to_hex(sha1));
57 if (info->tagger_date > 0) {
58 html("<tr><td>Tag date</td><td>");
59 cgit_print_date(info->tagger_date, FMT_LONGDATE);
60 html("</td><tr>\n");
61 }
62 if (info->tagger) {
63 html("<tr><td>Tagged by</td><td>");
64 html_txt(info->tagger);
65 html("</td></tr>\n");
66 }
67 html("<tr><td>Tagged object</td><td>");
68 cgit_object_link(tag->tagged);
69 html("</td></tr>\n");
70 html("</table>\n");
71 print_tag_content(info->msg);
72 }
73 return;
74}