summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2007-02-08 12:53:13 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2007-02-08 12:58:58 (UTC)
commitab2ab95f09994560f62fd631f07d3b6e3577aa6e (patch) (unidiff)
tree846763c1bcb78bd27dc37c99e5f6d703ca5ab179
parent14d360df60f059b9b5b045fc6df1eec6f0966d9a (diff)
downloadcgit-ab2ab95f09994560f62fd631f07d3b6e3577aa6e.zip
cgit-ab2ab95f09994560f62fd631f07d3b6e3577aa6e.tar.gz
cgit-ab2ab95f09994560f62fd631f07d3b6e3577aa6e.tar.bz2
Add support for snapshots
Make a link from the commit viewer to a snapshot of the corresponding tree. Currently only zip-format is supported. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile3
-rw-r--r--cgit.c39
-rw-r--r--cgit.h10
-rw-r--r--git.h27
-rw-r--r--shared.c17
-rw-r--r--ui-commit.c7
-rw-r--r--ui-shared.c11
-rw-r--r--ui-snapshot.c47
8 files changed, 153 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index d826f97..b1c3a4e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,41 +1,42 @@
1CGIT_VERSION = 0.2 1CGIT_VERSION = 0.2
2 2
3prefix = /var/www/htdocs/cgit 3prefix = /var/www/htdocs/cgit
4gitsrc = ../git 4gitsrc = ../git
5 5
6CACHE_ROOT = /var/cache/cgit 6CACHE_ROOT = /var/cache/cgit
7EXTLIBS = $(gitsrc)/libgit.a $(gitsrc)/xdiff/lib.a -lz -lcrypto 7EXTLIBS = $(gitsrc)/libgit.a $(gitsrc)/xdiff/lib.a -lz -lcrypto
8OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 8OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
9 ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c ui-diff.o 9 ui-summary.o ui-log.o ui-view.c ui-tree.c ui-commit.c ui-diff.o \
10 ui-snapshot.o
10 11
11CFLAGS += -Wall 12CFLAGS += -Wall
12 13
13ifdef DEBUG 14ifdef DEBUG
14 CFLAGS += -g 15 CFLAGS += -g
15endif 16endif
16 17
17all: cgit 18all: cgit
18 19
19install: all clean-cache 20install: all clean-cache
20 mkdir -p $(prefix) 21 mkdir -p $(prefix)
21 install cgit $(prefix)/cgit.cgi 22 install cgit $(prefix)/cgit.cgi
22 install cgit.css $(prefix)/cgit.css 23 install cgit.css $(prefix)/cgit.css
23 24
24cgit: cgit.c cgit.h git.h $(OBJECTS) $(gitsrc)/libgit.a 25cgit: cgit.c cgit.h git.h $(OBJECTS) $(gitsrc)/libgit.a
25 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \ 26 $(CC) $(CFLAGS) -DCGIT_VERSION='"$(CGIT_VERSION)"' cgit.c -o cgit \
26 $(OBJECTS) $(EXTLIBS) 27 $(OBJECTS) $(EXTLIBS)
27 28
28$(OBJECTS): cgit.h git.h 29$(OBJECTS): cgit.h git.h
29 30
30ui-diff.o: xdiff.h 31ui-diff.o: xdiff.h
31 32
32$(gitsrc)/libgit.a: 33$(gitsrc)/libgit.a:
33 $(MAKE) -C $(gitsrc) 34 $(MAKE) -C $(gitsrc)
34 35
35 36
36.PHONY: clean 37.PHONY: clean
37clean: 38clean:
38 rm -f cgit *.o 39 rm -f cgit *.o
39 40
40clean-cache: 41clean-cache:
41 rm -rf $(CACHE_ROOT)/* 42 rm -rf $(CACHE_ROOT)/*
diff --git a/cgit.c b/cgit.c
index 0fa203c..2795ecc 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,219 +1,244 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11const char cgit_version[] = CGIT_VERSION; 11const char cgit_version[] = CGIT_VERSION;
12 12
13 13
14static struct repoinfo *cgit_get_repoinfo(char *url) 14static struct repoinfo *cgit_get_repoinfo(char *url)
15{ 15{
16 int i; 16 int i;
17 struct repoinfo *repo; 17 struct repoinfo *repo;
18 18
19 for (i=0; i<cgit_repolist.count; i++) { 19 for (i=0; i<cgit_repolist.count; i++) {
20 repo = &cgit_repolist.repos[i]; 20 repo = &cgit_repolist.repos[i];
21 if (!strcmp(repo->url, url)) 21 if (!strcmp(repo->url, url))
22 return repo; 22 return repo;
23 } 23 }
24 return NULL; 24 return NULL;
25} 25}
26 26
27 27
28static int cgit_prepare_cache(struct cacheitem *item) 28static int cgit_prepare_cache(struct cacheitem *item)
29{ 29{
30 if (!cgit_query_repo) { 30 if (!cgit_query_repo) {
31 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root)); 31 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root));
32 item->ttl = cgit_cache_root_ttl; 32 item->ttl = cgit_cache_root_ttl;
33 return 1; 33 return 1;
34 } 34 }
35 cgit_repo = cgit_get_repoinfo(cgit_query_repo); 35 cgit_repo = cgit_get_repoinfo(cgit_query_repo);
36 if (!cgit_repo) { 36 if (!cgit_repo) {
37 char *title = fmt("%s - %s", cgit_root_title, "Bad request"); 37 char *title = fmt("%s - %s", cgit_root_title, "Bad request");
38 cgit_print_docstart(title, item); 38 cgit_print_docstart(title, item);
39 cgit_print_pageheader(title, 0); 39 cgit_print_pageheader(title, 0);
40 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo)); 40 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo));
41 cgit_print_docend(); 41 cgit_print_docend();
42 return 0; 42 return 0;
43 } 43 }
44 44
45 if (!cgit_query_page) { 45 if (!cgit_query_page) {
46 item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root, 46 item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root,
47 cgit_repo->url)); 47 cgit_repo->url));
48 item->ttl = cgit_cache_repo_ttl; 48 item->ttl = cgit_cache_repo_ttl;
49 } else { 49 } else {
50 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root, 50 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
51 cgit_repo->url, cgit_query_page, 51 cgit_repo->url, cgit_query_page,
52 cache_safe_filename(cgit_querystring))); 52 cache_safe_filename(cgit_querystring)));
53 if (cgit_query_has_symref) 53 if (cgit_query_has_symref)
54 item->ttl = cgit_cache_dynamic_ttl; 54 item->ttl = cgit_cache_dynamic_ttl;
55 else if (cgit_query_has_sha1) 55 else if (cgit_query_has_sha1)
56 item->ttl = cgit_cache_static_ttl; 56 item->ttl = cgit_cache_static_ttl;
57 else 57 else
58 item->ttl = cgit_cache_repo_ttl; 58 item->ttl = cgit_cache_repo_ttl;
59 } 59 }
60 return 1; 60 return 1;
61} 61}
62 62
63static void cgit_print_repo_page(struct cacheitem *item) 63static void cgit_print_repo_page(struct cacheitem *item)
64{ 64{
65 char *title; 65 char *title;
66 int show_search; 66 int show_search;
67 67
68 if (chdir(cgit_repo->path)) { 68 if (chdir(cgit_repo->path)) {
69 title = fmt("%s - %s", cgit_root_title, "Bad request"); 69 title = fmt("%s - %s", cgit_root_title, "Bad request");
70 cgit_print_docstart(title, item); 70 cgit_print_docstart(title, item);
71 cgit_print_pageheader(title, 0); 71 cgit_print_pageheader(title, 0);
72 cgit_print_error(fmt("Unable to scan repository: %s", 72 cgit_print_error(fmt("Unable to scan repository: %s",
73 strerror(errno))); 73 strerror(errno)));
74 cgit_print_docend(); 74 cgit_print_docend();
75 return; 75 return;
76 } 76 }
77 77
78 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 78 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
79 show_search = 0; 79 show_search = 0;
80 setenv("GIT_DIR", cgit_repo->path, 1); 80 setenv("GIT_DIR", cgit_repo->path, 1);
81
82 if (cgit_query_page && !strcmp(cgit_query_page, "snapshot")) {
83 cgit_print_snapshot(item, cgit_query_sha1, "zip",
84 cgit_repo->url, cgit_query_name);
85 return;
86 }
87
81 if (cgit_query_page && !strcmp(cgit_query_page, "log")) 88 if (cgit_query_page && !strcmp(cgit_query_page, "log"))
82 show_search = 1; 89 show_search = 1;
83 cgit_print_docstart(title, item); 90 cgit_print_docstart(title, item);
84 cgit_print_pageheader(title, show_search); 91 cgit_print_pageheader(title, show_search);
85 if (!cgit_query_page) { 92 if (!cgit_query_page) {
86 cgit_print_summary(); 93 cgit_print_summary();
87 } else if (!strcmp(cgit_query_page, "log")) { 94 } else if (!strcmp(cgit_query_page, "log")) {
88 cgit_print_log(cgit_query_head, cgit_query_ofs, 100, cgit_query_search); 95 cgit_print_log(cgit_query_head, cgit_query_ofs, 100,
96 cgit_query_search);
89 } else if (!strcmp(cgit_query_page, "tree")) { 97 } else if (!strcmp(cgit_query_page, "tree")) {
90 cgit_print_tree(cgit_query_sha1, cgit_query_path); 98 cgit_print_tree(cgit_query_sha1, cgit_query_path);
91 } else if (!strcmp(cgit_query_page, "commit")) { 99 } else if (!strcmp(cgit_query_page, "commit")) {
92 cgit_print_commit(cgit_query_sha1); 100 cgit_print_commit(cgit_query_sha1);
93 } else if (!strcmp(cgit_query_page, "view")) { 101 } else if (!strcmp(cgit_query_page, "view")) {
94 cgit_print_view(cgit_query_sha1); 102 cgit_print_view(cgit_query_sha1);
95 } else if (!strcmp(cgit_query_page, "diff")) { 103 } else if (!strcmp(cgit_query_page, "diff")) {
96 cgit_print_diff(cgit_query_sha1, cgit_query_sha2); 104 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
105 } else {
106 cgit_print_error("Invalid request");
97 } 107 }
98 cgit_print_docend(); 108 cgit_print_docend();
99} 109}
100 110
101static void cgit_fill_cache(struct cacheitem *item) 111static void cgit_fill_cache(struct cacheitem *item, int use_cache)
102{ 112{
103 static char buf[PATH_MAX]; 113 static char buf[PATH_MAX];
114 int stdout2;
104 115
105 getcwd(buf, sizeof(buf)); 116 getcwd(buf, sizeof(buf));
106 htmlfd = item->fd;
107 item->st.st_mtime = time(NULL); 117 item->st.st_mtime = time(NULL);
118
119 if (use_cache) {
120 stdout2 = chk_positive(dup(STDOUT_FILENO),
121 "Preserving STDOUT");
122 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
123 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
124 }
125
108 if (cgit_query_repo) 126 if (cgit_query_repo)
109 cgit_print_repo_page(item); 127 cgit_print_repo_page(item);
110 else 128 else
111 cgit_print_repolist(item); 129 cgit_print_repolist(item);
130
131 if (use_cache) {
132 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
133 chk_positive(dup2(stdout2, STDOUT_FILENO),
134 "Restoring original STDOUT");
135 chk_zero(close(stdout2), "Closing temporary STDOUT");
136 }
137
112 chdir(buf); 138 chdir(buf);
113} 139}
114 140
115static void cgit_check_cache(struct cacheitem *item) 141static void cgit_check_cache(struct cacheitem *item)
116{ 142{
117 int i = 0; 143 int i = 0;
118 144
119 top: 145 top:
120 if (++i > cgit_max_lock_attempts) { 146 if (++i > cgit_max_lock_attempts) {
121 die("cgit_refresh_cache: unable to lock %s: %s", 147 die("cgit_refresh_cache: unable to lock %s: %s",
122 item->name, strerror(errno)); 148 item->name, strerror(errno));
123 } 149 }
124 if (!cache_exist(item)) { 150 if (!cache_exist(item)) {
125 if (!cache_lock(item)) { 151 if (!cache_lock(item)) {
126 sleep(1); 152 sleep(1);
127 goto top; 153 goto top;
128 } 154 }
129 if (!cache_exist(item)) { 155 if (!cache_exist(item)) {
130 cgit_fill_cache(item); 156 cgit_fill_cache(item, 1);
131 cache_unlock(item); 157 cache_unlock(item);
132 } else { 158 } else {
133 cache_cancel_lock(item); 159 cache_cancel_lock(item);
134 } 160 }
135 } else if (cache_expired(item) && cache_lock(item)) { 161 } else if (cache_expired(item) && cache_lock(item)) {
136 if (cache_expired(item)) { 162 if (cache_expired(item)) {
137 cgit_fill_cache(item); 163 cgit_fill_cache(item, 1);
138 cache_unlock(item); 164 cache_unlock(item);
139 } else { 165 } else {
140 cache_cancel_lock(item); 166 cache_cancel_lock(item);
141 } 167 }
142 } 168 }
143} 169}
144 170
145static void cgit_print_cache(struct cacheitem *item) 171static void cgit_print_cache(struct cacheitem *item)
146{ 172{
147 static char buf[4096]; 173 static char buf[4096];
148 ssize_t i; 174 ssize_t i;
149 175
150 int fd = open(item->name, O_RDONLY); 176 int fd = open(item->name, O_RDONLY);
151 if (fd<0) 177 if (fd<0)
152 die("Unable to open cached file %s", item->name); 178 die("Unable to open cached file %s", item->name);
153 179
154 while((i=read(fd, buf, sizeof(buf))) > 0) 180 while((i=read(fd, buf, sizeof(buf))) > 0)
155 write(STDOUT_FILENO, buf, i); 181 write(STDOUT_FILENO, buf, i);
156 182
157 close(fd); 183 close(fd);
158} 184}
159 185
160static void cgit_parse_args(int argc, const char **argv) 186static void cgit_parse_args(int argc, const char **argv)
161{ 187{
162 int i; 188 int i;
163 189
164 for (i = 1; i < argc; i++) { 190 for (i = 1; i < argc; i++) {
165 if (!strncmp(argv[i], "--cache=", 8)) { 191 if (!strncmp(argv[i], "--cache=", 8)) {
166 cgit_cache_root = xstrdup(argv[i]+8); 192 cgit_cache_root = xstrdup(argv[i]+8);
167 } 193 }
168 if (!strcmp(argv[i], "--nocache")) { 194 if (!strcmp(argv[i], "--nocache")) {
169 cgit_nocache = 1; 195 cgit_nocache = 1;
170 } 196 }
171 if (!strncmp(argv[i], "--query=", 8)) { 197 if (!strncmp(argv[i], "--query=", 8)) {
172 cgit_querystring = xstrdup(argv[i]+8); 198 cgit_querystring = xstrdup(argv[i]+8);
173 } 199 }
174 if (!strncmp(argv[i], "--repo=", 7)) { 200 if (!strncmp(argv[i], "--repo=", 7)) {
175 cgit_query_repo = xstrdup(argv[i]+7); 201 cgit_query_repo = xstrdup(argv[i]+7);
176 } 202 }
177 if (!strncmp(argv[i], "--page=", 7)) { 203 if (!strncmp(argv[i], "--page=", 7)) {
178 cgit_query_page = xstrdup(argv[i]+7); 204 cgit_query_page = xstrdup(argv[i]+7);
179 } 205 }
180 if (!strncmp(argv[i], "--head=", 7)) { 206 if (!strncmp(argv[i], "--head=", 7)) {
181 cgit_query_head = xstrdup(argv[i]+7); 207 cgit_query_head = xstrdup(argv[i]+7);
182 cgit_query_has_symref = 1; 208 cgit_query_has_symref = 1;
183 } 209 }
184 if (!strncmp(argv[i], "--sha1=", 7)) { 210 if (!strncmp(argv[i], "--sha1=", 7)) {
185 cgit_query_sha1 = xstrdup(argv[i]+7); 211 cgit_query_sha1 = xstrdup(argv[i]+7);
186 cgit_query_has_sha1 = 1; 212 cgit_query_has_sha1 = 1;
187 } 213 }
188 if (!strncmp(argv[i], "--ofs=", 6)) { 214 if (!strncmp(argv[i], "--ofs=", 6)) {
189 cgit_query_ofs = atoi(argv[i]+6); 215 cgit_query_ofs = atoi(argv[i]+6);
190 } 216 }
191 } 217 }
192} 218}
193 219
194int main(int argc, const char **argv) 220int main(int argc, const char **argv)
195{ 221{
196 struct cacheitem item; 222 struct cacheitem item;
197 223
198 htmlfd = STDOUT_FILENO; 224 htmlfd = STDOUT_FILENO;
199 item.st.st_mtime = time(NULL); 225 item.st.st_mtime = time(NULL);
200 cgit_repolist.length = 0; 226 cgit_repolist.length = 0;
201 cgit_repolist.count = 0; 227 cgit_repolist.count = 0;
202 cgit_repolist.repos = NULL; 228 cgit_repolist.repos = NULL;
203 229
204 cgit_read_config("/etc/cgitrc", cgit_global_config_cb); 230 cgit_read_config("/etc/cgitrc", cgit_global_config_cb);
205 if (getenv("QUERY_STRING")) 231 if (getenv("QUERY_STRING"))
206 cgit_querystring = xstrdup(getenv("QUERY_STRING")); 232 cgit_querystring = xstrdup(getenv("QUERY_STRING"));
207 cgit_parse_args(argc, argv); 233 cgit_parse_args(argc, argv);
208 cgit_parse_query(cgit_querystring, cgit_querystring_cb); 234 cgit_parse_query(cgit_querystring, cgit_querystring_cb);
209 if (!cgit_prepare_cache(&item)) 235 if (!cgit_prepare_cache(&item))
210 return 0; 236 return 0;
211 if (cgit_nocache) { 237 if (cgit_nocache) {
212 item.fd = STDOUT_FILENO; 238 cgit_fill_cache(&item, 0);
213 cgit_fill_cache(&item);
214 } else { 239 } else {
215 cgit_check_cache(&item); 240 cgit_check_cache(&item);
216 cgit_print_cache(&item); 241 cgit_print_cache(&item);
217 } 242 }
218 return 0; 243 return 0;
219} 244}
diff --git a/cgit.h b/cgit.h
index 2a3cd5a..03c2fdb 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,142 +1,152 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4#include "git.h" 4#include "git.h"
5#include <openssl/sha.h> 5#include <openssl/sha.h>
6#include <ctype.h> 6#include <ctype.h>
7#include <sched.h> 7#include <sched.h>
8 8
9typedef void (*configfn)(const char *name, const char *value); 9typedef void (*configfn)(const char *name, const char *value);
10 10
11struct cacheitem { 11struct cacheitem {
12 char *name; 12 char *name;
13 struct stat st; 13 struct stat st;
14 int ttl; 14 int ttl;
15 int fd; 15 int fd;
16}; 16};
17 17
18struct repoinfo { 18struct repoinfo {
19 char *url; 19 char *url;
20 char *name; 20 char *name;
21 char *path; 21 char *path;
22 char *desc; 22 char *desc;
23 char *owner; 23 char *owner;
24}; 24};
25 25
26struct repolist { 26struct repolist {
27 int length; 27 int length;
28 int count; 28 int count;
29 struct repoinfo *repos; 29 struct repoinfo *repos;
30}; 30};
31 31
32struct commitinfo { 32struct commitinfo {
33 struct commit *commit; 33 struct commit *commit;
34 char *author; 34 char *author;
35 char *author_email; 35 char *author_email;
36 unsigned long author_date; 36 unsigned long author_date;
37 char *committer; 37 char *committer;
38 char *committer_email; 38 char *committer_email;
39 unsigned long committer_date; 39 unsigned long committer_date;
40 char *subject; 40 char *subject;
41 char *msg; 41 char *msg;
42}; 42};
43 43
44struct taginfo { 44struct taginfo {
45 char *tagger; 45 char *tagger;
46 char *tagger_email; 46 char *tagger_email;
47 int tagger_date; 47 int tagger_date;
48 char *msg; 48 char *msg;
49}; 49};
50 50
51extern const char cgit_version[]; 51extern const char cgit_version[];
52 52
53extern struct repolist cgit_repolist; 53extern struct repolist cgit_repolist;
54extern struct repoinfo *cgit_repo; 54extern struct repoinfo *cgit_repo;
55 55
56extern char *cgit_root_title; 56extern char *cgit_root_title;
57extern char *cgit_css; 57extern char *cgit_css;
58extern char *cgit_logo; 58extern char *cgit_logo;
59extern char *cgit_logo_link; 59extern char *cgit_logo_link;
60extern char *cgit_virtual_root; 60extern char *cgit_virtual_root;
61extern char *cgit_cache_root; 61extern char *cgit_cache_root;
62 62
63extern int cgit_nocache; 63extern int cgit_nocache;
64extern int cgit_max_lock_attempts; 64extern int cgit_max_lock_attempts;
65extern int cgit_cache_root_ttl; 65extern int cgit_cache_root_ttl;
66extern int cgit_cache_repo_ttl; 66extern int cgit_cache_repo_ttl;
67extern int cgit_cache_dynamic_ttl; 67extern int cgit_cache_dynamic_ttl;
68extern int cgit_cache_static_ttl; 68extern int cgit_cache_static_ttl;
69extern int cgit_cache_max_create_time; 69extern int cgit_cache_max_create_time;
70 70
71extern int cgit_max_msg_len; 71extern int cgit_max_msg_len;
72 72
73extern char *cgit_repo_name; 73extern char *cgit_repo_name;
74extern char *cgit_repo_desc; 74extern char *cgit_repo_desc;
75extern char *cgit_repo_owner; 75extern char *cgit_repo_owner;
76 76
77extern int cgit_query_has_symref; 77extern int cgit_query_has_symref;
78extern int cgit_query_has_sha1; 78extern int cgit_query_has_sha1;
79 79
80extern char *cgit_querystring; 80extern char *cgit_querystring;
81extern char *cgit_query_repo; 81extern char *cgit_query_repo;
82extern char *cgit_query_page; 82extern char *cgit_query_page;
83extern char *cgit_query_search; 83extern char *cgit_query_search;
84extern char *cgit_query_head; 84extern char *cgit_query_head;
85extern char *cgit_query_sha1; 85extern char *cgit_query_sha1;
86extern char *cgit_query_sha2; 86extern char *cgit_query_sha2;
87extern char *cgit_query_path; 87extern char *cgit_query_path;
88extern char *cgit_query_name;
88extern int cgit_query_ofs; 89extern int cgit_query_ofs;
89 90
90extern int htmlfd; 91extern int htmlfd;
91 92
92extern void cgit_global_config_cb(const char *name, const char *value); 93extern void cgit_global_config_cb(const char *name, const char *value);
93extern void cgit_repo_config_cb(const char *name, const char *value); 94extern void cgit_repo_config_cb(const char *name, const char *value);
94extern void cgit_querystring_cb(const char *name, const char *value); 95extern void cgit_querystring_cb(const char *name, const char *value);
95 96
97extern int chk_zero(int result, char *msg);
98extern int chk_positive(int result, char *msg);
99
96extern int hextoint(char c); 100extern int hextoint(char c);
97 101
98extern void *cgit_free_commitinfo(struct commitinfo *info); 102extern void *cgit_free_commitinfo(struct commitinfo *info);
99 103
100extern char *fmt(const char *format,...); 104extern char *fmt(const char *format,...);
101 105
102extern void html(const char *txt); 106extern void html(const char *txt);
103extern void htmlf(const char *format,...); 107extern void htmlf(const char *format,...);
104extern void html_txt(char *txt); 108extern void html_txt(char *txt);
105extern void html_ntxt(int len, char *txt); 109extern void html_ntxt(int len, char *txt);
106extern void html_attr(char *txt); 110extern void html_attr(char *txt);
107extern void html_hidden(char *name, char *value); 111extern void html_hidden(char *name, char *value);
108extern void html_link_open(char *url, char *title, char *class); 112extern void html_link_open(char *url, char *title, char *class);
109extern void html_link_close(void); 113extern void html_link_close(void);
110extern void html_filemode(unsigned short mode); 114extern void html_filemode(unsigned short mode);
111 115
112extern int cgit_read_config(const char *filename, configfn fn); 116extern int cgit_read_config(const char *filename, configfn fn);
113extern int cgit_parse_query(char *txt, configfn fn); 117extern int cgit_parse_query(char *txt, configfn fn);
114extern struct commitinfo *cgit_parse_commit(struct commit *commit); 118extern struct commitinfo *cgit_parse_commit(struct commit *commit);
115extern struct taginfo *cgit_parse_tag(struct tag *tag); 119extern struct taginfo *cgit_parse_tag(struct tag *tag);
116 120
117extern char *cache_safe_filename(const char *unsafe); 121extern char *cache_safe_filename(const char *unsafe);
118extern int cache_lock(struct cacheitem *item); 122extern int cache_lock(struct cacheitem *item);
119extern int cache_unlock(struct cacheitem *item); 123extern int cache_unlock(struct cacheitem *item);
120extern int cache_cancel_lock(struct cacheitem *item); 124extern int cache_cancel_lock(struct cacheitem *item);
121extern int cache_exist(struct cacheitem *item); 125extern int cache_exist(struct cacheitem *item);
122extern int cache_expired(struct cacheitem *item); 126extern int cache_expired(struct cacheitem *item);
123 127
124extern char *cgit_repourl(const char *reponame); 128extern char *cgit_repourl(const char *reponame);
125extern char *cgit_pageurl(const char *reponame, const char *pagename, 129extern char *cgit_pageurl(const char *reponame, const char *pagename,
126 const char *query); 130 const char *query);
127 131
128extern void cgit_print_error(char *msg); 132extern void cgit_print_error(char *msg);
129extern void cgit_print_date(unsigned long secs); 133extern void cgit_print_date(unsigned long secs);
130extern void cgit_print_docstart(char *title, struct cacheitem *item); 134extern void cgit_print_docstart(char *title, struct cacheitem *item);
131extern void cgit_print_docend(); 135extern void cgit_print_docend();
132extern void cgit_print_pageheader(char *title, int show_search); 136extern void cgit_print_pageheader(char *title, int show_search);
137extern void cgit_print_snapshot_start(const char *mimetype,
138 const char *filename,
139 struct cacheitem *item);
133 140
134extern void cgit_print_repolist(struct cacheitem *item); 141extern void cgit_print_repolist(struct cacheitem *item);
135extern void cgit_print_summary(); 142extern void cgit_print_summary();
136extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep); 143extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep);
137extern void cgit_print_view(const char *hex); 144extern void cgit_print_view(const char *hex);
138extern void cgit_print_tree(const char *hex, char *path); 145extern void cgit_print_tree(const char *hex, char *path);
139extern void cgit_print_commit(const char *hex); 146extern void cgit_print_commit(const char *hex);
140extern void cgit_print_diff(const char *old_hex, const char *new_hex); 147extern void cgit_print_diff(const char *old_hex, const char *new_hex);
148extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
149 const char *format, const char *prefix,
150 const char *filename);
141 151
142#endif /* CGIT_H */ 152#endif /* CGIT_H */
diff --git a/git.h b/git.h
index eca48d5..a1d1c4b 100644
--- a/git.h
+++ b/git.h
@@ -1,672 +1,699 @@
1#ifndef GIT_H 1#ifndef GIT_H
2#define GIT_H 2#define GIT_H
3 3
4 4
5/* 5/*
6 * from git:git-compat-util.h 6 * from git:git-compat-util.h
7 */ 7 */
8 8
9 9
10#ifndef FLEX_ARRAY 10#ifndef FLEX_ARRAY
11#if defined(__GNUC__) && (__GNUC__ < 3) 11#if defined(__GNUC__) && (__GNUC__ < 3)
12#define FLEX_ARRAY 0 12#define FLEX_ARRAY 0
13#else 13#else
14#define FLEX_ARRAY /* empty */ 14#define FLEX_ARRAY /* empty */
15#endif 15#endif
16#endif 16#endif
17 17
18 18
19#include <unistd.h> 19#include <unistd.h>
20#include <stdio.h> 20#include <stdio.h>
21#include <sys/stat.h> 21#include <sys/stat.h>
22#include <fcntl.h> 22#include <fcntl.h>
23#include <stddef.h> 23#include <stddef.h>
24#include <stdlib.h> 24#include <stdlib.h>
25#include <stdarg.h> 25#include <stdarg.h>
26#include <string.h> 26#include <string.h>
27#include <errno.h> 27#include <errno.h>
28#include <limits.h> 28#include <limits.h>
29#include <sys/param.h> 29#include <sys/param.h>
30#include <netinet/in.h> 30#include <netinet/in.h>
31#include <sys/types.h> 31#include <sys/types.h>
32#include <dirent.h> 32#include <dirent.h>
33#include <time.h> 33#include <time.h>
34#include <regex.h> 34#include <regex.h>
35 35
36/* On most systems <limits.h> would have given us this, but 36/* On most systems <limits.h> would have given us this, but
37 * not on some systems (e.g. GNU/Hurd). 37 * not on some systems (e.g. GNU/Hurd).
38 */ 38 */
39#ifndef PATH_MAX 39#ifndef PATH_MAX
40#define PATH_MAX 4096 40#define PATH_MAX 4096
41#endif 41#endif
42 42
43#ifdef __GNUC__ 43#ifdef __GNUC__
44#define NORETURN __attribute__((__noreturn__)) 44#define NORETURN __attribute__((__noreturn__))
45#else 45#else
46#define NORETURN 46#define NORETURN
47#ifndef __attribute__ 47#ifndef __attribute__
48#define __attribute__(x) 48#define __attribute__(x)
49#endif 49#endif
50#endif 50#endif
51 51
52 52
53extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2))); 53extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
54 54
55 55
56static inline char* xstrdup(const char *str) 56static inline char* xstrdup(const char *str)
57{ 57{
58 char *ret = strdup(str); 58 char *ret = strdup(str);
59 if (!ret) 59 if (!ret)
60 die("Out of memory, strdup failed"); 60 die("Out of memory, strdup failed");
61 return ret; 61 return ret;
62} 62}
63 63
64static inline void *xmalloc(size_t size) 64static inline void *xmalloc(size_t size)
65{ 65{
66 void *ret = malloc(size); 66 void *ret = malloc(size);
67 if (!ret && !size) 67 if (!ret && !size)
68 ret = malloc(1); 68 ret = malloc(1);
69 if (!ret) 69 if (!ret)
70 die("Out of memory, malloc failed"); 70 die("Out of memory, malloc failed");
71#ifdef XMALLOC_POISON 71#ifdef XMALLOC_POISON
72 memset(ret, 0xA5, size); 72 memset(ret, 0xA5, size);
73#endif 73#endif
74 return ret; 74 return ret;
75} 75}
76 76
77static inline void *xrealloc(void *ptr, size_t size) 77static inline void *xrealloc(void *ptr, size_t size)
78{ 78{
79 void *ret = realloc(ptr, size); 79 void *ret = realloc(ptr, size);
80 if (!ret && !size) 80 if (!ret && !size)
81 ret = realloc(ptr, 1); 81 ret = realloc(ptr, 1);
82 if (!ret) 82 if (!ret)
83 die("Out of memory, realloc failed"); 83 die("Out of memory, realloc failed");
84 return ret; 84 return ret;
85} 85}
86 86
87static inline void *xcalloc(size_t nmemb, size_t size) 87static inline void *xcalloc(size_t nmemb, size_t size)
88{ 88{
89 void *ret = calloc(nmemb, size); 89 void *ret = calloc(nmemb, size);
90 if (!ret && (!nmemb || !size)) 90 if (!ret && (!nmemb || !size))
91 ret = calloc(1, 1); 91 ret = calloc(1, 1);
92 if (!ret) 92 if (!ret)
93 die("Out of memory, calloc failed"); 93 die("Out of memory, calloc failed");
94 return ret; 94 return ret;
95} 95}
96 96
97static inline ssize_t xread(int fd, void *buf, size_t len) 97static inline ssize_t xread(int fd, void *buf, size_t len)
98{ 98{
99 ssize_t nr; 99 ssize_t nr;
100 while (1) { 100 while (1) {
101 nr = read(fd, buf, len); 101 nr = read(fd, buf, len);
102 if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) 102 if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
103 continue; 103 continue;
104 return nr; 104 return nr;
105 } 105 }
106} 106}
107 107
108static inline ssize_t xwrite(int fd, const void *buf, size_t len) 108static inline ssize_t xwrite(int fd, const void *buf, size_t len)
109{ 109{
110 ssize_t nr; 110 ssize_t nr;
111 while (1) { 111 while (1) {
112 nr = write(fd, buf, len); 112 nr = write(fd, buf, len);
113 if ((nr < 0) && (errno == EAGAIN || errno == EINTR)) 113 if ((nr < 0) && (errno == EAGAIN || errno == EINTR))
114 continue; 114 continue;
115 return nr; 115 return nr;
116 } 116 }
117} 117}
118 118
119 119
120 120
121 121
122/* 122/*
123 * from git:cache.h 123 * from git:cache.h
124 */ 124 */
125 125
126 126
127enum object_type { 127enum object_type {
128 OBJ_NONE = 0, 128 OBJ_NONE = 0,
129 OBJ_COMMIT = 1, 129 OBJ_COMMIT = 1,
130 OBJ_TREE = 2, 130 OBJ_TREE = 2,
131 OBJ_BLOB = 3, 131 OBJ_BLOB = 3,
132 OBJ_TAG = 4, 132 OBJ_TAG = 4,
133 /* 5 for future expansion */ 133 /* 5 for future expansion */
134 OBJ_OFS_DELTA = 6, 134 OBJ_OFS_DELTA = 6,
135 OBJ_REF_DELTA = 7, 135 OBJ_REF_DELTA = 7,
136 OBJ_BAD, 136 OBJ_BAD,
137}; 137};
138 138
139 139
140/* Convert to/from hex/sha1 representation */ 140/* Convert to/from hex/sha1 representation */
141#define MINIMUM_ABBREV 4 141#define MINIMUM_ABBREV 4
142#define DEFAULT_ABBREV 7 142#define DEFAULT_ABBREV 7
143 143
144extern const unsigned char null_sha1[20]; 144extern const unsigned char null_sha1[20];
145 145
146extern int sha1_object_info(const unsigned char *, char *, unsigned long *); 146extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
147 147
148extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size); 148extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
149 149
150extern int get_sha1(const char *str, unsigned char *sha1); 150extern int get_sha1(const char *str, unsigned char *sha1);
151extern int get_sha1_hex(const char *hex, unsigned char *sha1); 151extern int get_sha1_hex(const char *hex, unsigned char *sha1);
152 extern char *sha1_to_hex(const unsigned char *sha1);/* static buffer result! */ 152 extern char *sha1_to_hex(const unsigned char *sha1);/* static buffer result! */
153 153
154static inline int is_null_sha1(const unsigned char *sha1) 154static inline int is_null_sha1(const unsigned char *sha1)
155{ 155{
156 return !memcmp(sha1, null_sha1, 20); 156 return !memcmp(sha1, null_sha1, 20);
157} 157}
158static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2) 158static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
159{ 159{
160 return memcmp(sha1, sha2, 20); 160 return memcmp(sha1, sha2, 20);
161} 161}
162static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src) 162static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
163{ 163{
164 memcpy(sha_dst, sha_src, 20); 164 memcpy(sha_dst, sha_src, 20);
165} 165}
166static inline void hashclr(unsigned char *hash) 166static inline void hashclr(unsigned char *hash)
167{ 167{
168 memset(hash, 0, 20); 168 memset(hash, 0, 20);
169} 169}
170 170
171 171
172/* 172/*
173 * from git:grep.h 173 * from git:grep.h
174 */ 174 */
175 175
176enum grep_pat_token { 176enum grep_pat_token {
177 GREP_PATTERN, 177 GREP_PATTERN,
178 GREP_PATTERN_HEAD, 178 GREP_PATTERN_HEAD,
179 GREP_PATTERN_BODY, 179 GREP_PATTERN_BODY,
180 GREP_AND, 180 GREP_AND,
181 GREP_OPEN_PAREN, 181 GREP_OPEN_PAREN,
182 GREP_CLOSE_PAREN, 182 GREP_CLOSE_PAREN,
183 GREP_NOT, 183 GREP_NOT,
184 GREP_OR, 184 GREP_OR,
185}; 185};
186 186
187enum grep_context { 187enum grep_context {
188 GREP_CONTEXT_HEAD, 188 GREP_CONTEXT_HEAD,
189 GREP_CONTEXT_BODY, 189 GREP_CONTEXT_BODY,
190}; 190};
191 191
192struct grep_pat { 192struct grep_pat {
193 struct grep_pat *next; 193 struct grep_pat *next;
194 const char *origin; 194 const char *origin;
195 int no; 195 int no;
196 enum grep_pat_token token; 196 enum grep_pat_token token;
197 const char *pattern; 197 const char *pattern;
198 regex_t regexp; 198 regex_t regexp;
199}; 199};
200 200
201enum grep_expr_node { 201enum grep_expr_node {
202 GREP_NODE_ATOM, 202 GREP_NODE_ATOM,
203 GREP_NODE_NOT, 203 GREP_NODE_NOT,
204 GREP_NODE_AND, 204 GREP_NODE_AND,
205 GREP_NODE_OR, 205 GREP_NODE_OR,
206}; 206};
207 207
208struct grep_opt { 208struct grep_opt {
209 struct grep_pat *pattern_list; 209 struct grep_pat *pattern_list;
210 struct grep_pat **pattern_tail; 210 struct grep_pat **pattern_tail;
211 struct grep_expr *pattern_expression; 211 struct grep_expr *pattern_expression;
212 int prefix_length; 212 int prefix_length;
213 regex_t regexp; 213 regex_t regexp;
214 unsigned linenum:1; 214 unsigned linenum:1;
215 unsigned invert:1; 215 unsigned invert:1;
216 unsigned status_only:1; 216 unsigned status_only:1;
217 unsigned name_only:1; 217 unsigned name_only:1;
218 unsigned unmatch_name_only:1; 218 unsigned unmatch_name_only:1;
219 unsigned count:1; 219 unsigned count:1;
220 unsigned word_regexp:1; 220 unsigned word_regexp:1;
221 unsigned fixed:1; 221 unsigned fixed:1;
222 unsigned all_match:1; 222 unsigned all_match:1;
223#define GREP_BINARY_DEFAULT 0 223#define GREP_BINARY_DEFAULT 0
224#define GREP_BINARY_NOMATCH 1 224#define GREP_BINARY_NOMATCH 1
225#define GREP_BINARY_TEXT 2 225#define GREP_BINARY_TEXT 2
226 unsigned binary:2; 226 unsigned binary:2;
227 unsigned extended:1; 227 unsigned extended:1;
228 unsigned relative:1; 228 unsigned relative:1;
229 unsigned pathname:1; 229 unsigned pathname:1;
230 int regflags; 230 int regflags;
231 unsigned pre_context; 231 unsigned pre_context;
232 unsigned post_context; 232 unsigned post_context;
233}; 233};
234 234
235 235
236extern void compile_grep_patterns(struct grep_opt *opt); 236extern void compile_grep_patterns(struct grep_opt *opt);
237extern void free_grep_patterns(struct grep_opt *opt); 237extern void free_grep_patterns(struct grep_opt *opt);
238 238
239 239
240/* 240/*
241 * from git:object.h 241 * from git:object.h
242 */ 242 */
243 243
244extern const char *type_names[9]; 244extern const char *type_names[9];
245 245
246struct object_list { 246struct object_list {
247 struct object *item; 247 struct object *item;
248 struct object_list *next; 248 struct object_list *next;
249}; 249};
250 250
251struct object_refs { 251struct object_refs {
252 unsigned count; 252 unsigned count;
253 struct object *base; 253 struct object *base;
254 struct object *ref[FLEX_ARRAY]; /* more */ 254 struct object *ref[FLEX_ARRAY]; /* more */
255}; 255};
256 256
257struct object_array { 257struct object_array {
258 unsigned int nr; 258 unsigned int nr;
259 unsigned int alloc; 259 unsigned int alloc;
260 struct object_array_entry { 260 struct object_array_entry {
261 struct object *item; 261 struct object *item;
262 const char *name; 262 const char *name;
263 } *objects; 263 } *objects;
264}; 264};
265 265
266#define TYPE_BITS 3 266#define TYPE_BITS 3
267#define FLAG_BITS 27 267#define FLAG_BITS 27
268 268
269/* 269/*
270 * The object type is stored in 3 bits. 270 * The object type is stored in 3 bits.
271 */ 271 */
272struct object { 272struct object {
273 unsigned parsed : 1; 273 unsigned parsed : 1;
274 unsigned used : 1; 274 unsigned used : 1;
275 unsigned type : TYPE_BITS; 275 unsigned type : TYPE_BITS;
276 unsigned flags : FLAG_BITS; 276 unsigned flags : FLAG_BITS;
277 unsigned char sha1[20]; 277 unsigned char sha1[20];
278}; 278};
279 279
280 280
281/** Returns the object, having parsed it to find out what it is. **/ 281/** Returns the object, having parsed it to find out what it is. **/
282struct object *parse_object(const unsigned char *sha1); 282struct object *parse_object(const unsigned char *sha1);
283 283
284 284
285/* 285/*
286 * from git:tree.h 286 * from git:tree.h
287 */ 287 */
288 288
289struct tree { 289struct tree {
290 struct object object; 290 struct object object;
291 void *buffer; 291 void *buffer;
292 unsigned long size; 292 unsigned long size;
293}; 293};
294 294
295 295
296struct tree *lookup_tree(const unsigned char *sha1); 296struct tree *lookup_tree(const unsigned char *sha1);
297int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size); 297int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
298int parse_tree(struct tree *tree); 298int parse_tree(struct tree *tree);
299struct tree *parse_tree_indirect(const unsigned char *sha1); 299struct tree *parse_tree_indirect(const unsigned char *sha1);
300 300
301typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const char *, unsigned int, int); 301typedef int (*read_tree_fn_t)(const unsigned char *, const char *, int, const char *, unsigned int, int);
302 302
303extern int read_tree_recursive(struct tree *tree, 303extern int read_tree_recursive(struct tree *tree,
304 const char *base, int baselen, 304 const char *base, int baselen,
305 int stage, const char **match, 305 int stage, const char **match,
306 read_tree_fn_t fn); 306 read_tree_fn_t fn);
307 307
308extern int read_tree(struct tree *tree, int stage, const char **paths); 308extern int read_tree(struct tree *tree, int stage, const char **paths);
309 309
310 310
311/* from git:commit.h */ 311/* from git:commit.h */
312 312
313struct commit_list { 313struct commit_list {
314 struct commit *item; 314 struct commit *item;
315 struct commit_list *next; 315 struct commit_list *next;
316}; 316};
317 317
318struct commit { 318struct commit {
319 struct object object; 319 struct object object;
320 void *util; 320 void *util;
321 unsigned long date; 321 unsigned long date;
322 struct commit_list *parents; 322 struct commit_list *parents;
323 struct tree *tree; 323 struct tree *tree;
324 char *buffer; 324 char *buffer;
325}; 325};
326 326
327 327
328struct commit *lookup_commit(const unsigned char *sha1); 328struct commit *lookup_commit(const unsigned char *sha1);
329struct commit *lookup_commit_reference(const unsigned char *sha1); 329struct commit *lookup_commit_reference(const unsigned char *sha1);
330struct commit *lookup_commit_reference_gently(const unsigned char *sha1, 330struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
331 int quiet); 331 int quiet);
332 332
333int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size); 333int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
334int parse_commit(struct commit *item); 334int parse_commit(struct commit *item);
335 335
336struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p); 336struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
337struct commit_list * insert_by_date(struct commit *item, struct commit_list **list); 337struct commit_list * insert_by_date(struct commit *item, struct commit_list **list);
338 338
339void free_commit_list(struct commit_list *list); 339void free_commit_list(struct commit_list *list);
340 340
341void sort_by_date(struct commit_list **list); 341void sort_by_date(struct commit_list **list);
342 342
343/* Commit formats */ 343/* Commit formats */
344enum cmit_fmt { 344enum cmit_fmt {
345 CMIT_FMT_RAW, 345 CMIT_FMT_RAW,
346 CMIT_FMT_MEDIUM, 346 CMIT_FMT_MEDIUM,
347 CMIT_FMT_DEFAULT = CMIT_FMT_MEDIUM, 347 CMIT_FMT_DEFAULT = CMIT_FMT_MEDIUM,
348 CMIT_FMT_SHORT, 348 CMIT_FMT_SHORT,
349 CMIT_FMT_FULL, 349 CMIT_FMT_FULL,
350 CMIT_FMT_FULLER, 350 CMIT_FMT_FULLER,
351 CMIT_FMT_ONELINE, 351 CMIT_FMT_ONELINE,
352 CMIT_FMT_EMAIL, 352 CMIT_FMT_EMAIL,
353 353
354 CMIT_FMT_UNSPECIFIED, 354 CMIT_FMT_UNSPECIFIED,
355}; 355};
356 356
357extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, int relative_date); 357extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject, const char *after_subject, int relative_date);
358 358
359 359
360typedef void (*topo_sort_set_fn_t)(struct commit*, void *data); 360typedef void (*topo_sort_set_fn_t)(struct commit*, void *data);
361typedef void* (*topo_sort_get_fn_t)(struct commit*); 361typedef void* (*topo_sort_get_fn_t)(struct commit*);
362 362
363 363
364 364
365/* 365/*
366 * from git:tag.h 366 * from git:tag.h
367 */ 367 */
368 368
369extern const char *tag_type; 369extern const char *tag_type;
370 370
371struct tag { 371struct tag {
372 struct object object; 372 struct object object;
373 struct object *tagged; 373 struct object *tagged;
374 char *tag; 374 char *tag;
375 char *signature; /* not actually implemented */ 375 char *signature; /* not actually implemented */
376}; 376};
377 377
378extern struct tag *lookup_tag(const unsigned char *sha1); 378extern struct tag *lookup_tag(const unsigned char *sha1);
379extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size); 379extern int parse_tag_buffer(struct tag *item, void *data, unsigned long size);
380extern int parse_tag(struct tag *item); 380extern int parse_tag(struct tag *item);
381extern struct object *deref_tag(struct object *, const char *, int); 381extern struct object *deref_tag(struct object *, const char *, int);
382 382
383 383
384/* 384/*
385 * from git:diffcore.h 385 * from git:diffcore.h
386 */ 386 */
387 387
388struct diff_filespec { 388struct diff_filespec {
389 unsigned char sha1[20]; 389 unsigned char sha1[20];
390 char *path; 390 char *path;
391 void *data; 391 void *data;
392 void *cnt_data; 392 void *cnt_data;
393 unsigned long size; 393 unsigned long size;
394 int xfrm_flags; /* for use by the xfrm */ 394 int xfrm_flags; /* for use by the xfrm */
395 unsigned short mode; /* file mode */ 395 unsigned short mode; /* file mode */
396 unsigned sha1_valid : 1; /* if true, use sha1 and trust mode; 396 unsigned sha1_valid : 1; /* if true, use sha1 and trust mode;
397 * if false, use the name and read from 397 * if false, use the name and read from
398 * the filesystem. 398 * the filesystem.
399 */ 399 */
400#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0) 400#define DIFF_FILE_VALID(spec) (((spec)->mode) != 0)
401 unsigned should_free : 1; /* data should be free()'ed */ 401 unsigned should_free : 1; /* data should be free()'ed */
402 unsigned should_munmap : 1; /* data should be munmap()'ed */ 402 unsigned should_munmap : 1; /* data should be munmap()'ed */
403}; 403};
404 404
405struct diff_filepair { 405struct diff_filepair {
406 struct diff_filespec *one; 406 struct diff_filespec *one;
407 struct diff_filespec *two; 407 struct diff_filespec *two;
408 unsigned short int score; 408 unsigned short int score;
409 char status; /* M C R N D U (see Documentation/diff-format.txt) */ 409 char status; /* M C R N D U (see Documentation/diff-format.txt) */
410 unsigned source_stays : 1; /* all of R/C are copies */ 410 unsigned source_stays : 1; /* all of R/C are copies */
411 unsigned broken_pair : 1; 411 unsigned broken_pair : 1;
412 unsigned renamed_pair : 1; 412 unsigned renamed_pair : 1;
413}; 413};
414 414
415#define DIFF_PAIR_UNMERGED(p) \ 415#define DIFF_PAIR_UNMERGED(p) \
416 (!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two)) 416 (!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
417 417
418#define DIFF_PAIR_RENAME(p) ((p)->renamed_pair) 418#define DIFF_PAIR_RENAME(p) ((p)->renamed_pair)
419 419
420#define DIFF_PAIR_BROKEN(p) \ 420#define DIFF_PAIR_BROKEN(p) \
421 ( (!DIFF_FILE_VALID((p)->one) != !DIFF_FILE_VALID((p)->two)) && \ 421 ( (!DIFF_FILE_VALID((p)->one) != !DIFF_FILE_VALID((p)->two)) && \
422 ((p)->broken_pair != 0) ) 422 ((p)->broken_pair != 0) )
423 423
424#define DIFF_PAIR_TYPE_CHANGED(p) \ 424#define DIFF_PAIR_TYPE_CHANGED(p) \
425 ((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode)) 425 ((S_IFMT & (p)->one->mode) != (S_IFMT & (p)->two->mode))
426 426
427#define DIFF_PAIR_MODE_CHANGED(p) ((p)->one->mode != (p)->two->mode) 427#define DIFF_PAIR_MODE_CHANGED(p) ((p)->one->mode != (p)->two->mode)
428 428
429extern void diff_free_filepair(struct diff_filepair *); 429extern void diff_free_filepair(struct diff_filepair *);
430 430
431extern int diff_unmodified_pair(struct diff_filepair *); 431extern int diff_unmodified_pair(struct diff_filepair *);
432 432
433struct diff_queue_struct { 433struct diff_queue_struct {
434 struct diff_filepair **queue; 434 struct diff_filepair **queue;
435 int alloc; 435 int alloc;
436 int nr; 436 int nr;
437}; 437};
438 438
439 439
440/* 440/*
441 * from git:diff.h 441 * from git:diff.h
442 */ 442 */
443 443
444 444
445struct rev_info; 445struct rev_info;
446struct diff_options; 446struct diff_options;
447struct diff_queue_struct; 447struct diff_queue_struct;
448 448
449typedef void (*change_fn_t)(struct diff_options *options, 449typedef void (*change_fn_t)(struct diff_options *options,
450 unsigned old_mode, unsigned new_mode, 450 unsigned old_mode, unsigned new_mode,
451 const unsigned char *old_sha1, 451 const unsigned char *old_sha1,
452 const unsigned char *new_sha1, 452 const unsigned char *new_sha1,
453 const char *base, const char *path); 453 const char *base, const char *path);
454 454
455typedef void (*add_remove_fn_t)(struct diff_options *options, 455typedef void (*add_remove_fn_t)(struct diff_options *options,
456 int addremove, unsigned mode, 456 int addremove, unsigned mode,
457 const unsigned char *sha1, 457 const unsigned char *sha1,
458 const char *base, const char *path); 458 const char *base, const char *path);
459 459
460typedef void (*diff_format_fn_t)(struct diff_queue_struct *q, 460typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
461 struct diff_options *options, void *data); 461 struct diff_options *options, void *data);
462 462
463 #define DIFF_FORMAT_RAW 0x0001 463 #define DIFF_FORMAT_RAW 0x0001
464 #define DIFF_FORMAT_DIFFSTAT0x0002 464 #define DIFF_FORMAT_DIFFSTAT0x0002
465 #define DIFF_FORMAT_NUMSTAT0x0004 465 #define DIFF_FORMAT_NUMSTAT0x0004
466 #define DIFF_FORMAT_SUMMARY0x0008 466 #define DIFF_FORMAT_SUMMARY0x0008
467 #define DIFF_FORMAT_PATCH0x0010 467 #define DIFF_FORMAT_PATCH0x0010
468 468
469/* These override all above */ 469/* These override all above */
470 #define DIFF_FORMAT_NAME0x0100 470 #define DIFF_FORMAT_NAME0x0100
471 #define DIFF_FORMAT_NAME_STATUS0x0200 471 #define DIFF_FORMAT_NAME_STATUS0x0200
472 #define DIFF_FORMAT_CHECKDIFF0x0400 472 #define DIFF_FORMAT_CHECKDIFF0x0400
473 473
474/* Same as output_format = 0 but we know that -s flag was given 474/* Same as output_format = 0 but we know that -s flag was given
475 * and we should not give default value to output_format. 475 * and we should not give default value to output_format.
476 */ 476 */
477 #define DIFF_FORMAT_NO_OUTPUT0x0800 477 #define DIFF_FORMAT_NO_OUTPUT0x0800
478 478
479 #define DIFF_FORMAT_CALLBACK0x1000 479 #define DIFF_FORMAT_CALLBACK0x1000
480 480
481struct diff_options { 481struct diff_options {
482 const char *filter; 482 const char *filter;
483 const char *orderfile; 483 const char *orderfile;
484 const char *pickaxe; 484 const char *pickaxe;
485 const char *single_follow; 485 const char *single_follow;
486 unsigned recursive:1, 486 unsigned recursive:1,
487 tree_in_recursive:1, 487 tree_in_recursive:1,
488 binary:1, 488 binary:1,
489 text:1, 489 text:1,
490 full_index:1, 490 full_index:1,
491 silent_on_remove:1, 491 silent_on_remove:1,
492 find_copies_harder:1, 492 find_copies_harder:1,
493 color_diff:1, 493 color_diff:1,
494 color_diff_words:1; 494 color_diff_words:1;
495 int context; 495 int context;
496 int break_opt; 496 int break_opt;
497 int detect_rename; 497 int detect_rename;
498 int line_termination; 498 int line_termination;
499 int output_format; 499 int output_format;
500 int pickaxe_opts; 500 int pickaxe_opts;
501 int rename_score; 501 int rename_score;
502 int reverse_diff; 502 int reverse_diff;
503 int rename_limit; 503 int rename_limit;
504 int setup; 504 int setup;
505 int abbrev; 505 int abbrev;
506 const char *msg_sep; 506 const char *msg_sep;
507 const char *stat_sep; 507 const char *stat_sep;
508 long xdl_opts; 508 long xdl_opts;
509 509
510 int stat_width; 510 int stat_width;
511 int stat_name_width; 511 int stat_name_width;
512 512
513 int nr_paths; 513 int nr_paths;
514 const char **paths; 514 const char **paths;
515 int *pathlens; 515 int *pathlens;
516 change_fn_t change; 516 change_fn_t change;
517 add_remove_fn_t add_remove; 517 add_remove_fn_t add_remove;
518 diff_format_fn_t format_callback; 518 diff_format_fn_t format_callback;
519 void *format_callback_data; 519 void *format_callback_data;
520}; 520};
521 521
522enum color_diff { 522enum color_diff {
523 DIFF_RESET = 0, 523 DIFF_RESET = 0,
524 DIFF_PLAIN = 1, 524 DIFF_PLAIN = 1,
525 DIFF_METAINFO = 2, 525 DIFF_METAINFO = 2,
526 DIFF_FRAGINFO = 3, 526 DIFF_FRAGINFO = 3,
527 DIFF_FILE_OLD = 4, 527 DIFF_FILE_OLD = 4,
528 DIFF_FILE_NEW = 5, 528 DIFF_FILE_NEW = 5,
529 DIFF_COMMIT = 6, 529 DIFF_COMMIT = 6,
530 DIFF_WHITESPACE = 7, 530 DIFF_WHITESPACE = 7,
531}; 531};
532 532
533 533
534extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new, 534extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
535 const char *base, struct diff_options *opt); 535 const char *base, struct diff_options *opt);
536 536
537extern int diff_root_tree_sha1(const unsigned char *new, const char *base, 537extern int diff_root_tree_sha1(const unsigned char *new, const char *base,
538 struct diff_options *opt); 538 struct diff_options *opt);
539 539
540extern int git_diff_ui_config(const char *var, const char *value); 540extern int git_diff_ui_config(const char *var, const char *value);
541extern void diff_setup(struct diff_options *); 541extern void diff_setup(struct diff_options *);
542extern int diff_opt_parse(struct diff_options *, const char **, int); 542extern int diff_opt_parse(struct diff_options *, const char **, int);
543extern int diff_setup_done(struct diff_options *); 543extern int diff_setup_done(struct diff_options *);
544 544
545 545
546extern void diffcore_std(struct diff_options *); 546extern void diffcore_std(struct diff_options *);
547extern void diff_flush(struct diff_options*); 547extern void diff_flush(struct diff_options*);
548 548
549 549
550/* diff-raw status letters */ 550/* diff-raw status letters */
551 #define DIFF_STATUS_ADDED 'A' 551 #define DIFF_STATUS_ADDED 'A'
552 #define DIFF_STATUS_COPIED 'C' 552 #define DIFF_STATUS_COPIED 'C'
553 #define DIFF_STATUS_DELETED 'D' 553 #define DIFF_STATUS_DELETED 'D'
554 #define DIFF_STATUS_MODIFIED 'M' 554 #define DIFF_STATUS_MODIFIED 'M'
555 #define DIFF_STATUS_RENAMED 'R' 555 #define DIFF_STATUS_RENAMED 'R'
556 #define DIFF_STATUS_TYPE_CHANGED'T' 556 #define DIFF_STATUS_TYPE_CHANGED'T'
557 #define DIFF_STATUS_UNKNOWN 'X' 557 #define DIFF_STATUS_UNKNOWN 'X'
558 #define DIFF_STATUS_UNMERGED 'U' 558 #define DIFF_STATUS_UNMERGED 'U'
559 559
560 560
561 561
562/* 562/*
563 * from git:refs.g 563 * from git:refs.g
564 */ 564 */
565 565
566typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data); 566typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
567extern int head_ref(each_ref_fn, void *); 567extern int head_ref(each_ref_fn, void *);
568extern int for_each_ref(each_ref_fn, void *); 568extern int for_each_ref(each_ref_fn, void *);
569extern int for_each_tag_ref(each_ref_fn, void *); 569extern int for_each_tag_ref(each_ref_fn, void *);
570extern int for_each_branch_ref(each_ref_fn, void *); 570extern int for_each_branch_ref(each_ref_fn, void *);
571extern int for_each_remote_ref(each_ref_fn, void *); 571extern int for_each_remote_ref(each_ref_fn, void *);
572 572
573 573
574 574
575/* 575/*
576 * from git:revision.h 576 * from git:revision.h
577 */ 577 */
578 578
579struct rev_info; 579struct rev_info;
580struct log_info; 580struct log_info;
581 581
582typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit); 582typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit);
583 583
584struct rev_info { 584struct rev_info {
585 /* Starting list */ 585 /* Starting list */
586 struct commit_list *commits; 586 struct commit_list *commits;
587 struct object_array pending; 587 struct object_array pending;
588 588
589 /* Basic information */ 589 /* Basic information */
590 const char *prefix; 590 const char *prefix;
591 void *prune_data; 591 void *prune_data;
592 prune_fn_t *prune_fn; 592 prune_fn_t *prune_fn;
593 593
594 /* Traversal flags */ 594 /* Traversal flags */
595 unsigned intdense:1, 595 unsigned intdense:1,
596 no_merges:1, 596 no_merges:1,
597 no_walk:1, 597 no_walk:1,
598 remove_empty_trees:1, 598 remove_empty_trees:1,
599 simplify_history:1, 599 simplify_history:1,
600 lifo:1, 600 lifo:1,
601 topo_order:1, 601 topo_order:1,
602 tag_objects:1, 602 tag_objects:1,
603 tree_objects:1, 603 tree_objects:1,
604 blob_objects:1, 604 blob_objects:1,
605 edge_hint:1, 605 edge_hint:1,
606 limited:1, 606 limited:1,
607 unpacked:1, /* see also ignore_packed below */ 607 unpacked:1, /* see also ignore_packed below */
608 boundary:1, 608 boundary:1,
609 parents:1; 609 parents:1;
610 610
611 /* Diff flags */ 611 /* Diff flags */
612 unsigned intdiff:1, 612 unsigned intdiff:1,
613 full_diff:1, 613 full_diff:1,
614 show_root_diff:1, 614 show_root_diff:1,
615 no_commit_id:1, 615 no_commit_id:1,
616 verbose_header:1, 616 verbose_header:1,
617 ignore_merges:1, 617 ignore_merges:1,
618 combine_merges:1, 618 combine_merges:1,
619 dense_combined_merges:1, 619 dense_combined_merges:1,
620 always_show_header:1; 620 always_show_header:1;
621 621
622 /* Format info */ 622 /* Format info */
623 unsigned intshown_one:1, 623 unsigned intshown_one:1,
624 abbrev_commit:1, 624 abbrev_commit:1,
625 relative_date:1; 625 relative_date:1;
626 626
627 const char **ignore_packed; /* pretend objects in these are unpacked */ 627 const char **ignore_packed; /* pretend objects in these are unpacked */
628 int num_ignore_packed; 628 int num_ignore_packed;
629 629
630 unsigned intabbrev; 630 unsigned intabbrev;
631 enum cmit_fmtcommit_format; 631 enum cmit_fmtcommit_format;
632 struct log_info *loginfo; 632 struct log_info *loginfo;
633 int nr, total; 633 int nr, total;
634 const char*mime_boundary; 634 const char*mime_boundary;
635 const char*message_id; 635 const char*message_id;
636 const char*ref_message_id; 636 const char*ref_message_id;
637 const char*add_signoff; 637 const char*add_signoff;
638 const char*extra_headers; 638 const char*extra_headers;
639 639
640 /* Filter by commit log message */ 640 /* Filter by commit log message */
641 struct grep_opt*grep_filter; 641 struct grep_opt*grep_filter;
642 642
643 /* special limits */ 643 /* special limits */
644 int max_count; 644 int max_count;
645 unsigned long max_age; 645 unsigned long max_age;
646 unsigned long min_age; 646 unsigned long min_age;
647 647
648 /* diff info for patches and for paths limiting */ 648 /* diff info for patches and for paths limiting */
649 struct diff_options diffopt; 649 struct diff_options diffopt;
650 struct diff_options pruning; 650 struct diff_options pruning;
651 651
652 topo_sort_set_fn_t topo_setter; 652 topo_sort_set_fn_t topo_setter;
653 topo_sort_get_fn_t topo_getter; 653 topo_sort_get_fn_t topo_getter;
654}; 654};
655 655
656 656
657extern void init_revisions(struct rev_info *revs, const char *prefix); 657extern void init_revisions(struct rev_info *revs, const char *prefix);
658extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def); 658extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
659extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename); 659extern int handle_revision_arg(const char *arg, struct rev_info *revs,int flags,int cant_be_filename);
660 660
661extern void prepare_revision_walk(struct rev_info *revs); 661extern void prepare_revision_walk(struct rev_info *revs);
662extern struct commit *get_revision(struct rev_info *revs); 662extern struct commit *get_revision(struct rev_info *revs);
663 663
664 664
665 665
666/* from git:log-tree.h */ 666/* from git:log-tree.h */
667 667
668int log_tree_commit(struct rev_info *, struct commit *); 668int log_tree_commit(struct rev_info *, struct commit *);
669 669
670 670
671 671
672/* from git:archive.h */
673
674struct archiver_args {
675 const char *base;
676 struct tree *tree;
677 const unsigned char *commit_sha1;
678 time_t time;
679 const char **pathspec;
680 unsigned int verbose : 1;
681 void *extra;
682};
683
684typedef int (*write_archive_fn_t)(struct archiver_args *);
685
686typedef void *(*parse_extra_args_fn_t)(int argc, const char **argv);
687
688struct archiver {
689 const char *name;
690 struct archiver_args args;
691 write_archive_fn_t write_archive;
692 parse_extra_args_fn_t parse_extra;
693};
694
695extern int write_tar_archive(struct archiver_args *);
696extern int write_zip_archive(struct archiver_args *);
697extern void *parse_extra_zip_args(int argc, const char **argv);
698
672#endif /* GIT_H */ 699#endif /* GIT_H */
diff --git a/shared.c b/shared.c
index 6fd70a8..5757d0c 100644
--- a/shared.c
+++ b/shared.c
@@ -1,168 +1,185 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback functions
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11struct repolist cgit_repolist; 11struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13 13
14char *cgit_root_title = "Git repository browser"; 14char *cgit_root_title = "Git repository browser";
15char *cgit_css = "/cgit.css"; 15char *cgit_css = "/cgit.css";
16char *cgit_logo = "/git-logo.png"; 16char *cgit_logo = "/git-logo.png";
17char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 17char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
18char *cgit_virtual_root = NULL; 18char *cgit_virtual_root = NULL;
19 19
20char *cgit_cache_root = "/var/cache/cgit"; 20char *cgit_cache_root = "/var/cache/cgit";
21 21
22int cgit_nocache = 0; 22int cgit_nocache = 0;
23int cgit_max_lock_attempts = 5; 23int cgit_max_lock_attempts = 5;
24int cgit_cache_root_ttl = 5; 24int cgit_cache_root_ttl = 5;
25int cgit_cache_repo_ttl = 5; 25int cgit_cache_repo_ttl = 5;
26int cgit_cache_dynamic_ttl = 5; 26int cgit_cache_dynamic_ttl = 5;
27int cgit_cache_static_ttl = -1; 27int cgit_cache_static_ttl = -1;
28int cgit_cache_max_create_time = 5; 28int cgit_cache_max_create_time = 5;
29 29
30int cgit_max_msg_len = 60; 30int cgit_max_msg_len = 60;
31 31
32char *cgit_repo_name = NULL; 32char *cgit_repo_name = NULL;
33char *cgit_repo_desc = NULL; 33char *cgit_repo_desc = NULL;
34char *cgit_repo_owner = NULL; 34char *cgit_repo_owner = NULL;
35 35
36int cgit_query_has_symref = 0; 36int cgit_query_has_symref = 0;
37int cgit_query_has_sha1 = 0; 37int cgit_query_has_sha1 = 0;
38 38
39char *cgit_querystring = NULL; 39char *cgit_querystring = NULL;
40char *cgit_query_repo = NULL; 40char *cgit_query_repo = NULL;
41char *cgit_query_page = NULL; 41char *cgit_query_page = NULL;
42char *cgit_query_head = NULL; 42char *cgit_query_head = NULL;
43char *cgit_query_search = NULL; 43char *cgit_query_search = NULL;
44char *cgit_query_sha1 = NULL; 44char *cgit_query_sha1 = NULL;
45char *cgit_query_sha2 = NULL; 45char *cgit_query_sha2 = NULL;
46char *cgit_query_path = NULL; 46char *cgit_query_path = NULL;
47char *cgit_query_name = NULL;
47int cgit_query_ofs = 0; 48int cgit_query_ofs = 0;
48 49
49int htmlfd = 0; 50int htmlfd = 0;
50 51
52int chk_zero(int result, char *msg)
53{
54 if (result != 0)
55 die("%s: %s", msg, strerror(errno));
56 return result;
57}
58
59int chk_positive(int result, char *msg)
60{
61 if (result <= 0)
62 die("%s: %s", msg, strerror(errno));
63 return result;
64}
65
51struct repoinfo *add_repo(const char *url) 66struct repoinfo *add_repo(const char *url)
52{ 67{
53 struct repoinfo *ret; 68 struct repoinfo *ret;
54 69
55 if (++cgit_repolist.count > cgit_repolist.length) { 70 if (++cgit_repolist.count > cgit_repolist.length) {
56 if (cgit_repolist.length == 0) 71 if (cgit_repolist.length == 0)
57 cgit_repolist.length = 8; 72 cgit_repolist.length = 8;
58 else 73 else
59 cgit_repolist.length *= 2; 74 cgit_repolist.length *= 2;
60 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 75 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
61 cgit_repolist.length * 76 cgit_repolist.length *
62 sizeof(struct repoinfo)); 77 sizeof(struct repoinfo));
63 } 78 }
64 79
65 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 80 ret = &cgit_repolist.repos[cgit_repolist.count-1];
66 ret->url = xstrdup(url); 81 ret->url = xstrdup(url);
67 ret->name = ret->url; 82 ret->name = ret->url;
68 ret->path = NULL; 83 ret->path = NULL;
69 ret->desc = NULL; 84 ret->desc = NULL;
70 ret->owner = NULL; 85 ret->owner = NULL;
71 return ret; 86 return ret;
72} 87}
73 88
74void cgit_global_config_cb(const char *name, const char *value) 89void cgit_global_config_cb(const char *name, const char *value)
75{ 90{
76 if (!strcmp(name, "root-title")) 91 if (!strcmp(name, "root-title"))
77 cgit_root_title = xstrdup(value); 92 cgit_root_title = xstrdup(value);
78 else if (!strcmp(name, "css")) 93 else if (!strcmp(name, "css"))
79 cgit_css = xstrdup(value); 94 cgit_css = xstrdup(value);
80 else if (!strcmp(name, "logo")) 95 else if (!strcmp(name, "logo"))
81 cgit_logo = xstrdup(value); 96 cgit_logo = xstrdup(value);
82 else if (!strcmp(name, "logo-link")) 97 else if (!strcmp(name, "logo-link"))
83 cgit_logo_link = xstrdup(value); 98 cgit_logo_link = xstrdup(value);
84 else if (!strcmp(name, "virtual-root")) 99 else if (!strcmp(name, "virtual-root"))
85 cgit_virtual_root = xstrdup(value); 100 cgit_virtual_root = xstrdup(value);
86 else if (!strcmp(name, "nocache")) 101 else if (!strcmp(name, "nocache"))
87 cgit_nocache = atoi(value); 102 cgit_nocache = atoi(value);
88 else if (!strcmp(name, "cache-root")) 103 else if (!strcmp(name, "cache-root"))
89 cgit_cache_root = xstrdup(value); 104 cgit_cache_root = xstrdup(value);
90 else if (!strcmp(name, "cache-root-ttl")) 105 else if (!strcmp(name, "cache-root-ttl"))
91 cgit_cache_root_ttl = atoi(value); 106 cgit_cache_root_ttl = atoi(value);
92 else if (!strcmp(name, "cache-repo-ttl")) 107 else if (!strcmp(name, "cache-repo-ttl"))
93 cgit_cache_repo_ttl = atoi(value); 108 cgit_cache_repo_ttl = atoi(value);
94 else if (!strcmp(name, "cache-static-ttl")) 109 else if (!strcmp(name, "cache-static-ttl"))
95 cgit_cache_static_ttl = atoi(value); 110 cgit_cache_static_ttl = atoi(value);
96 else if (!strcmp(name, "cache-dynamic-ttl")) 111 else if (!strcmp(name, "cache-dynamic-ttl"))
97 cgit_cache_dynamic_ttl = atoi(value); 112 cgit_cache_dynamic_ttl = atoi(value);
98 else if (!strcmp(name, "max-message-length")) 113 else if (!strcmp(name, "max-message-length"))
99 cgit_max_msg_len = atoi(value); 114 cgit_max_msg_len = atoi(value);
100 else if (!strcmp(name, "repo.url")) 115 else if (!strcmp(name, "repo.url"))
101 cgit_repo = add_repo(value); 116 cgit_repo = add_repo(value);
102 else if (!strcmp(name, "repo.name")) 117 else if (!strcmp(name, "repo.name"))
103 cgit_repo->name = xstrdup(value); 118 cgit_repo->name = xstrdup(value);
104 else if (cgit_repo && !strcmp(name, "repo.path")) 119 else if (cgit_repo && !strcmp(name, "repo.path"))
105 cgit_repo->path = xstrdup(value); 120 cgit_repo->path = xstrdup(value);
106 else if (cgit_repo && !strcmp(name, "repo.desc")) 121 else if (cgit_repo && !strcmp(name, "repo.desc"))
107 cgit_repo->desc = xstrdup(value); 122 cgit_repo->desc = xstrdup(value);
108 else if (cgit_repo && !strcmp(name, "repo.owner")) 123 else if (cgit_repo && !strcmp(name, "repo.owner"))
109 cgit_repo->owner = xstrdup(value); 124 cgit_repo->owner = xstrdup(value);
110} 125}
111 126
112void cgit_repo_config_cb(const char *name, const char *value) 127void cgit_repo_config_cb(const char *name, const char *value)
113{ 128{
114 if (!strcmp(name, "name")) 129 if (!strcmp(name, "name"))
115 cgit_repo_name = xstrdup(value); 130 cgit_repo_name = xstrdup(value);
116 else if (!strcmp(name, "desc")) 131 else if (!strcmp(name, "desc"))
117 cgit_repo_desc = xstrdup(value); 132 cgit_repo_desc = xstrdup(value);
118 else if (!strcmp(name, "owner")) 133 else if (!strcmp(name, "owner"))
119 cgit_repo_owner = xstrdup(value); 134 cgit_repo_owner = xstrdup(value);
120} 135}
121 136
122void cgit_querystring_cb(const char *name, const char *value) 137void cgit_querystring_cb(const char *name, const char *value)
123{ 138{
124 if (!strcmp(name,"r")) { 139 if (!strcmp(name,"r")) {
125 cgit_query_repo = xstrdup(value); 140 cgit_query_repo = xstrdup(value);
126 } else if (!strcmp(name, "p")) { 141 } else if (!strcmp(name, "p")) {
127 cgit_query_page = xstrdup(value); 142 cgit_query_page = xstrdup(value);
128 } else if (!strcmp(name, "q")) { 143 } else if (!strcmp(name, "q")) {
129 cgit_query_search = xstrdup(value); 144 cgit_query_search = xstrdup(value);
130 } else if (!strcmp(name, "h")) { 145 } else if (!strcmp(name, "h")) {
131 cgit_query_head = xstrdup(value); 146 cgit_query_head = xstrdup(value);
132 cgit_query_has_symref = 1; 147 cgit_query_has_symref = 1;
133 } else if (!strcmp(name, "id")) { 148 } else if (!strcmp(name, "id")) {
134 cgit_query_sha1 = xstrdup(value); 149 cgit_query_sha1 = xstrdup(value);
135 cgit_query_has_sha1 = 1; 150 cgit_query_has_sha1 = 1;
136 } else if (!strcmp(name, "id2")) { 151 } else if (!strcmp(name, "id2")) {
137 cgit_query_sha2 = xstrdup(value); 152 cgit_query_sha2 = xstrdup(value);
138 cgit_query_has_sha1 = 1; 153 cgit_query_has_sha1 = 1;
139 } else if (!strcmp(name, "ofs")) { 154 } else if (!strcmp(name, "ofs")) {
140 cgit_query_ofs = atoi(value); 155 cgit_query_ofs = atoi(value);
141 } else if (!strcmp(name, "path")) { 156 } else if (!strcmp(name, "path")) {
142 cgit_query_path = xstrdup(value); 157 cgit_query_path = xstrdup(value);
158 } else if (!strcmp(name, "name")) {
159 cgit_query_name = xstrdup(value);
143 } 160 }
144} 161}
145 162
146void *cgit_free_commitinfo(struct commitinfo *info) 163void *cgit_free_commitinfo(struct commitinfo *info)
147{ 164{
148 free(info->author); 165 free(info->author);
149 free(info->author_email); 166 free(info->author_email);
150 free(info->committer); 167 free(info->committer);
151 free(info->committer_email); 168 free(info->committer_email);
152 free(info->subject); 169 free(info->subject);
153 free(info); 170 free(info);
154 return NULL; 171 return NULL;
155} 172}
156 173
157int hextoint(char c) 174int hextoint(char c)
158{ 175{
159 if (c >= 'a' && c <= 'f') 176 if (c >= 'a' && c <= 'f')
160 return 10 + c - 'a'; 177 return 10 + c - 'a';
161 else if (c >= 'A' && c <= 'F') 178 else if (c >= 'A' && c <= 'F')
162 return 10 + c - 'A'; 179 return 10 + c - 'A';
163 else if (c >= '0' && c <= '9') 180 else if (c >= '0' && c <= '9')
164 return c - '0'; 181 return c - '0';
165 else 182 else
166 return -1; 183 return -1;
167} 184}
168 185
diff --git a/ui-commit.c b/ui-commit.c
index 73fa104..de3f2cf 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,185 +1,192 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
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
11int files = 0; 11int files = 0;
12 12
13void print_filepair(struct diff_filepair *pair) 13void print_filepair(struct diff_filepair *pair)
14{ 14{
15 char *query; 15 char *query;
16 char *class; 16 char *class;
17 17
18 switch (pair->status) { 18 switch (pair->status) {
19 case DIFF_STATUS_ADDED: 19 case DIFF_STATUS_ADDED:
20 class = "add"; 20 class = "add";
21 break; 21 break;
22 case DIFF_STATUS_COPIED: 22 case DIFF_STATUS_COPIED:
23 class = "cpy"; 23 class = "cpy";
24 break; 24 break;
25 case DIFF_STATUS_DELETED: 25 case DIFF_STATUS_DELETED:
26 class = "del"; 26 class = "del";
27 break; 27 break;
28 case DIFF_STATUS_MODIFIED: 28 case DIFF_STATUS_MODIFIED:
29 class = "upd"; 29 class = "upd";
30 break; 30 break;
31 case DIFF_STATUS_RENAMED: 31 case DIFF_STATUS_RENAMED:
32 class = "mov"; 32 class = "mov";
33 break; 33 break;
34 case DIFF_STATUS_TYPE_CHANGED: 34 case DIFF_STATUS_TYPE_CHANGED:
35 class = "typ"; 35 class = "typ";
36 break; 36 break;
37 case DIFF_STATUS_UNKNOWN: 37 case DIFF_STATUS_UNKNOWN:
38 class = "unk"; 38 class = "unk";
39 break; 39 break;
40 case DIFF_STATUS_UNMERGED: 40 case DIFF_STATUS_UNMERGED:
41 class = "stg"; 41 class = "stg";
42 break; 42 break;
43 default: 43 default:
44 die("bug: unhandled diff status %c", pair->status); 44 die("bug: unhandled diff status %c", pair->status);
45 } 45 }
46 46
47 html("<tr>"); 47 html("<tr>");
48 htmlf("<td class='mode'>"); 48 htmlf("<td class='mode'>");
49 if (is_null_sha1(pair->two->sha1)) { 49 if (is_null_sha1(pair->two->sha1)) {
50 html_filemode(pair->one->mode); 50 html_filemode(pair->one->mode);
51 } else { 51 } else {
52 html_filemode(pair->two->mode); 52 html_filemode(pair->two->mode);
53 } 53 }
54 54
55 if (pair->one->mode != pair->two->mode && 55 if (pair->one->mode != pair->two->mode &&
56 !is_null_sha1(pair->one->sha1) && 56 !is_null_sha1(pair->one->sha1) &&
57 !is_null_sha1(pair->two->sha1)) { 57 !is_null_sha1(pair->two->sha1)) {
58 html("<span class='modechange'>["); 58 html("<span class='modechange'>[");
59 html_filemode(pair->one->mode); 59 html_filemode(pair->one->mode);
60 html("]</span>"); 60 html("]</span>");
61 } 61 }
62 htmlf("</td><td class='%s'>", class); 62 htmlf("</td><td class='%s'>", class);
63 query = fmt("id=%s&id2=%s", sha1_to_hex(pair->one->sha1), 63 query = fmt("id=%s&id2=%s", sha1_to_hex(pair->one->sha1),
64 sha1_to_hex(pair->two->sha1)); 64 sha1_to_hex(pair->two->sha1));
65 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query), 65 html_link_open(cgit_pageurl(cgit_query_repo, "diff", query),
66 NULL, NULL); 66 NULL, NULL);
67 if (pair->status == DIFF_STATUS_COPIED || 67 if (pair->status == DIFF_STATUS_COPIED ||
68 pair->status == DIFF_STATUS_RENAMED) { 68 pair->status == DIFF_STATUS_RENAMED) {
69 html_txt(pair->two->path); 69 html_txt(pair->two->path);
70 htmlf("</a> (%s from ", pair->status == DIFF_STATUS_COPIED ? 70 htmlf("</a> (%s from ", pair->status == DIFF_STATUS_COPIED ?
71 "copied" : "renamed"); 71 "copied" : "renamed");
72 query = fmt("id=%s", sha1_to_hex(pair->one->sha1)); 72 query = fmt("id=%s", sha1_to_hex(pair->one->sha1));
73 html_link_open(cgit_pageurl(cgit_query_repo, "view", query), 73 html_link_open(cgit_pageurl(cgit_query_repo, "view", query),
74 NULL, NULL); 74 NULL, NULL);
75 html_txt(pair->one->path); 75 html_txt(pair->one->path);
76 html("</a>)"); 76 html("</a>)");
77 } else { 77 } else {
78 html_txt(pair->two->path); 78 html_txt(pair->two->path);
79 html("</a>"); 79 html("</a>");
80 } 80 }
81 html("<td>"); 81 html("<td>");
82 82
83 //TODO: diffstat graph 83 //TODO: diffstat graph
84 84
85 html("</td></tr>\n"); 85 html("</td></tr>\n");
86 files++; 86 files++;
87} 87}
88 88
89void diff_format_cb(struct diff_queue_struct *q, 89void diff_format_cb(struct diff_queue_struct *q,
90 struct diff_options *options, void *data) 90 struct diff_options *options, void *data)
91{ 91{
92 int i; 92 int i;
93 93
94 for (i = 0; i < q->nr; i++) { 94 for (i = 0; i < q->nr; i++) {
95 if (q->queue[i]->status == 'U') 95 if (q->queue[i]->status == 'U')
96 continue; 96 continue;
97 print_filepair(q->queue[i]); 97 print_filepair(q->queue[i]);
98 } 98 }
99} 99}
100 100
101void cgit_diffstat(struct commit *commit) 101void cgit_diffstat(struct commit *commit)
102{ 102{
103 struct diff_options opt; 103 struct diff_options opt;
104 int ret; 104 int ret;
105 105
106 diff_setup(&opt); 106 diff_setup(&opt);
107 opt.output_format = DIFF_FORMAT_CALLBACK; 107 opt.output_format = DIFF_FORMAT_CALLBACK;
108 opt.detect_rename = 1; 108 opt.detect_rename = 1;
109 opt.recursive = 1; 109 opt.recursive = 1;
110 opt.format_callback = diff_format_cb; 110 opt.format_callback = diff_format_cb;
111 diff_setup_done(&opt); 111 diff_setup_done(&opt);
112 112
113 if (commit->parents) 113 if (commit->parents)
114 ret = diff_tree_sha1(commit->parents->item->object.sha1, 114 ret = diff_tree_sha1(commit->parents->item->object.sha1,
115 commit->object.sha1, 115 commit->object.sha1,
116 "", &opt); 116 "", &opt);
117 else 117 else
118 ret = diff_root_tree_sha1(commit->object.sha1, "", &opt); 118 ret = diff_root_tree_sha1(commit->object.sha1, "", &opt);
119 119
120 diffcore_std(&opt); 120 diffcore_std(&opt);
121 diff_flush(&opt); 121 diff_flush(&opt);
122} 122}
123 123
124void cgit_print_commit(const char *hex) 124void cgit_print_commit(const char *hex)
125{ 125{
126 struct commit *commit; 126 struct commit *commit;
127 struct commitinfo *info; 127 struct commitinfo *info;
128 struct commit_list *p; 128 struct commit_list *p;
129 unsigned char sha1[20]; 129 unsigned char sha1[20];
130 char *query; 130 char *query;
131 char *filename;
131 132
132 if (get_sha1(hex, sha1)) { 133 if (get_sha1(hex, sha1)) {
133 cgit_print_error(fmt("Bad object id: %s", hex)); 134 cgit_print_error(fmt("Bad object id: %s", hex));
134 return; 135 return;
135 } 136 }
136 commit = lookup_commit_reference(sha1); 137 commit = lookup_commit_reference(sha1);
137 if (!commit) { 138 if (!commit) {
138 cgit_print_error(fmt("Bad commit reference: %s", hex)); 139 cgit_print_error(fmt("Bad commit reference: %s", hex));
139 return; 140 return;
140 } 141 }
141 info = cgit_parse_commit(commit); 142 info = cgit_parse_commit(commit);
142 143
143 html("<table class='commit-info'>\n"); 144 html("<table class='commit-info'>\n");
144 html("<tr><th>author</th><td>"); 145 html("<tr><th>author</th><td>");
145 html_txt(info->author); 146 html_txt(info->author);
146 html(" "); 147 html(" ");
147 html_txt(info->author_email); 148 html_txt(info->author_email);
148 html("</td><td class='right'>"); 149 html("</td><td class='right'>");
149 cgit_print_date(info->author_date); 150 cgit_print_date(info->author_date);
150 html("</td></tr>\n"); 151 html("</td></tr>\n");
151 html("<tr><th>committer</th><td>"); 152 html("<tr><th>committer</th><td>");
152 html_txt(info->committer); 153 html_txt(info->committer);
153 html(" "); 154 html(" ");
154 html_txt(info->committer_email); 155 html_txt(info->committer_email);
155 html("</td><td class='right'>"); 156 html("</td><td class='right'>");
156 cgit_print_date(info->committer_date); 157 cgit_print_date(info->committer_date);
157 html("</td></tr>\n"); 158 html("</td></tr>\n");
158 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='"); 159 html("<tr><th>tree</th><td colspan='2' class='sha1'><a href='");
159 query = fmt("id=%s", sha1_to_hex(commit->tree->object.sha1)); 160 query = fmt("id=%s", sha1_to_hex(commit->tree->object.sha1));
160 html_attr(cgit_pageurl(cgit_query_repo, "tree", query)); 161 html_attr(cgit_pageurl(cgit_query_repo, "tree", query));
161 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1)); 162 htmlf("'>%s</a></td></tr>\n", sha1_to_hex(commit->tree->object.sha1));
162 for (p = commit->parents; p ; p = p->next) { 163 for (p = commit->parents; p ; p = p->next) {
163 html("<tr><th>parent</th>" 164 html("<tr><th>parent</th>"
164 "<td colspan='2' class='sha1'>" 165 "<td colspan='2' class='sha1'>"
165 "<a href='"); 166 "<a href='");
166 query = fmt("id=%s", sha1_to_hex(p->item->object.sha1)); 167 query = fmt("id=%s", sha1_to_hex(p->item->object.sha1));
167 html_attr(cgit_pageurl(cgit_query_repo, "commit", query)); 168 html_attr(cgit_pageurl(cgit_query_repo, "commit", query));
168 htmlf("'>%s</a></td></tr>\n", 169 htmlf("'>%s</a></td></tr>\n",
169 sha1_to_hex(p->item->object.sha1)); 170 sha1_to_hex(p->item->object.sha1));
170 } 171 }
172 htmlf("<tr><th>download</th><td colspan='2' class='sha1'><a href='");
173 filename = fmt("%s-%s.zip", cgit_query_repo, hex);
174 html_attr(cgit_pageurl(cgit_query_repo, "snapshot",
175 fmt("id=%s&name=%s", hex, filename)));
176 htmlf("'>%s</a></td></tr>", filename);
177
171 html("</table>\n"); 178 html("</table>\n");
172 html("<div class='commit-subject'>"); 179 html("<div class='commit-subject'>");
173 html_txt(info->subject); 180 html_txt(info->subject);
174 html("</div>"); 181 html("</div>");
175 html("<div class='commit-msg'>"); 182 html("<div class='commit-msg'>");
176 html_txt(info->msg); 183 html_txt(info->msg);
177 html("</div>"); 184 html("</div>");
178 html("<table class='diffstat'>"); 185 html("<table class='diffstat'>");
179 html("<tr><th colspan='3'>Affected files</tr>\n"); 186 html("<tr><th colspan='3'>Affected files</tr>\n");
180 cgit_diffstat(commit); 187 cgit_diffstat(commit);
181 htmlf("<tr><td colspan='3' class='summary'>" 188 htmlf("<tr><td colspan='3' class='summary'>"
182 "%d file%s changed</td></tr>\n", files, files > 1 ? "s" : ""); 189 "%d file%s changed</td></tr>\n", files, files > 1 ? "s" : "");
183 html("</table>"); 190 html("</table>");
184 cgit_free_commitinfo(info); 191 cgit_free_commitinfo(info);
185} 192}
diff --git a/ui-shared.c b/ui-shared.c
index 3322561..172499c 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -1,146 +1,157 @@
1/* ui-shared.c: common web output functions 1/* ui-shared.c: common web output functions
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11const char cgit_doctype[] = 11const char cgit_doctype[] =
12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
14 14
15static char *http_date(time_t t) 15static char *http_date(time_t t)
16{ 16{
17 static char day[][4] = 17 static char day[][4] =
18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
19 static char month[][4] = 19 static char month[][4] =
20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; 21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
22 struct tm *tm = gmtime(&t); 22 struct tm *tm = gmtime(&t);
23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
25 tm->tm_hour, tm->tm_min, tm->tm_sec); 25 tm->tm_hour, tm->tm_min, tm->tm_sec);
26} 26}
27 27
28static long ttl_seconds(long ttl) 28static long ttl_seconds(long ttl)
29{ 29{
30 if (ttl<0) 30 if (ttl<0)
31 return 60 * 60 * 24 * 365; 31 return 60 * 60 * 24 * 365;
32 else 32 else
33 return ttl * 60; 33 return ttl * 60;
34} 34}
35 35
36void cgit_print_error(char *msg) 36void cgit_print_error(char *msg)
37{ 37{
38 html("<div class='error'>"); 38 html("<div class='error'>");
39 html_txt(msg); 39 html_txt(msg);
40 html("</div>\n"); 40 html("</div>\n");
41} 41}
42 42
43char *cgit_repourl(const char *reponame) 43char *cgit_repourl(const char *reponame)
44{ 44{
45 if (cgit_virtual_root) { 45 if (cgit_virtual_root) {
46 return fmt("%s/%s/", cgit_virtual_root, reponame); 46 return fmt("%s/%s/", cgit_virtual_root, reponame);
47 } else { 47 } else {
48 return fmt("?r=%s", reponame); 48 return fmt("?r=%s", reponame);
49 } 49 }
50} 50}
51 51
52char *cgit_pageurl(const char *reponame, const char *pagename, 52char *cgit_pageurl(const char *reponame, const char *pagename,
53 const char *query) 53 const char *query)
54{ 54{
55 if (cgit_virtual_root) { 55 if (cgit_virtual_root) {
56 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame, 56 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame,
57 pagename, query); 57 pagename, query);
58 } else { 58 } else {
59 return fmt("?r=%s&p=%s&%s", reponame, pagename, query); 59 return fmt("?r=%s&p=%s&%s", reponame, pagename, query);
60 } 60 }
61} 61}
62 62
63char *cgit_currurl() 63char *cgit_currurl()
64{ 64{
65 if (!cgit_virtual_root) 65 if (!cgit_virtual_root)
66 return "./cgit.cgi"; 66 return "./cgit.cgi";
67 else if (cgit_query_page) 67 else if (cgit_query_page)
68 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page); 68 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page);
69 else if (cgit_query_repo) 69 else if (cgit_query_repo)
70 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); 70 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
71 else 71 else
72 return fmt("%s/", cgit_virtual_root); 72 return fmt("%s/", cgit_virtual_root);
73} 73}
74 74
75 75
76void cgit_print_date(unsigned long secs) 76void cgit_print_date(unsigned long secs)
77{ 77{
78 char buf[32]; 78 char buf[32];
79 struct tm *time; 79 struct tm *time;
80 80
81 time = gmtime(&secs); 81 time = gmtime(&secs);
82 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", time); 82 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", time);
83 html_txt(buf); 83 html_txt(buf);
84 84
85} 85}
86 86
87void cgit_print_docstart(char *title, struct cacheitem *item) 87void cgit_print_docstart(char *title, struct cacheitem *item)
88{ 88{
89 html("Content-Type: text/html; charset=utf-8\n"); 89 html("Content-Type: text/html; charset=utf-8\n");
90 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 90 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
91 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 91 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
92 ttl_seconds(item->ttl))); 92 ttl_seconds(item->ttl)));
93 html("\n"); 93 html("\n");
94 html(cgit_doctype); 94 html(cgit_doctype);
95 html("<html>\n"); 95 html("<html>\n");
96 html("<head>\n"); 96 html("<head>\n");
97 html("<title>"); 97 html("<title>");
98 html_txt(title); 98 html_txt(title);
99 html("</title>\n"); 99 html("</title>\n");
100 htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version); 100 htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version);
101 html("<link rel='stylesheet' type='text/css' href='"); 101 html("<link rel='stylesheet' type='text/css' href='");
102 html_attr(cgit_css); 102 html_attr(cgit_css);
103 html("'/>\n"); 103 html("'/>\n");
104 html("</head>\n"); 104 html("</head>\n");
105 html("<body>\n"); 105 html("<body>\n");
106} 106}
107 107
108void cgit_print_docend() 108void cgit_print_docend()
109{ 109{
110 html("</td></tr></table>"); 110 html("</td></tr></table>");
111 html("</body>\n</html>\n"); 111 html("</body>\n</html>\n");
112} 112}
113 113
114void cgit_print_pageheader(char *title, int show_search) 114void cgit_print_pageheader(char *title, int show_search)
115{ 115{
116 html("<table id='layout'><tr><td id='header'>"); 116 html("<table id='layout'><tr><td id='header'>");
117 htmlf("<a href='%s'>", cgit_logo_link); 117 htmlf("<a href='%s'>", cgit_logo_link);
118 htmlf("<img id='logo' src='%s'/>\n", cgit_logo); 118 htmlf("<img id='logo' src='%s'/>\n", cgit_logo);
119 htmlf("</a>"); 119 htmlf("</a>");
120 if (show_search) { 120 if (show_search) {
121 html("<form method='get' href='"); 121 html("<form method='get' href='");
122 html_attr(cgit_currurl()); 122 html_attr(cgit_currurl());
123 html("'>"); 123 html("'>");
124 if (!cgit_virtual_root) { 124 if (!cgit_virtual_root) {
125 if (cgit_query_repo) 125 if (cgit_query_repo)
126 html_hidden("r", cgit_query_repo); 126 html_hidden("r", cgit_query_repo);
127 if (cgit_query_page) 127 if (cgit_query_page)
128 html_hidden("p", cgit_query_page); 128 html_hidden("p", cgit_query_page);
129 } 129 }
130 if (cgit_query_head) 130 if (cgit_query_head)
131 html_hidden("h", cgit_query_head); 131 html_hidden("h", cgit_query_head);
132 if (cgit_query_sha1) 132 if (cgit_query_sha1)
133 html_hidden("id", cgit_query_sha1); 133 html_hidden("id", cgit_query_sha1);
134 if (cgit_query_sha2) 134 if (cgit_query_sha2)
135 html_hidden("id2", cgit_query_sha2); 135 html_hidden("id2", cgit_query_sha2);
136 html("<input type='text' name='q' value='"); 136 html("<input type='text' name='q' value='");
137 html_attr(cgit_query_search); 137 html_attr(cgit_query_search);
138 html("'/></form>"); 138 html("'/></form>");
139 } 139 }
140 if (cgit_query_repo) 140 if (cgit_query_repo)
141 htmlf("<a href='%s'>", cgit_repourl(cgit_query_repo)); 141 htmlf("<a href='%s'>", cgit_repourl(cgit_query_repo));
142 html_txt(title); 142 html_txt(title);
143 if (cgit_query_repo) 143 if (cgit_query_repo)
144 html("</a>"); 144 html("</a>");
145 html("</td></tr><tr><td id='content'>"); 145 html("</td></tr><tr><td id='content'>");
146} 146}
147
148void cgit_print_snapshot_start(const char *mimetype, const char *filename,
149 struct cacheitem *item)
150{
151 htmlf("Content-Type: %s\n", mimetype);
152 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
153 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
154 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
155 ttl_seconds(item->ttl)));
156 html("\n");
157}
diff --git a/ui-snapshot.c b/ui-snapshot.c
new file mode 100644
index 0000000..2257d6b
--- a/dev/null
+++ b/ui-snapshot.c
@@ -0,0 +1,47 @@
1/* ui-snapshot.c: generate snapshot of a commit
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
11static void cgit_print_zip(struct cacheitem *item, const char *hex,
12 const char *prefix, const char *filename)
13{
14 struct archiver_args args;
15 struct commit *commit;
16 unsigned char sha1[20];
17
18 if (get_sha1(hex, sha1)) {
19 cgit_print_error(fmt("Bad object id: %s", hex));
20 return;
21 }
22 commit = lookup_commit_reference(sha1);
23
24 if (!commit) {
25 cgit_print_error(fmt("Not a commit reference: %s", hex));
26 return;
27 }
28
29 memset(&args, 0, sizeof(args));
30 args.base = fmt("%s/", prefix);
31 args.tree = commit->tree;
32
33 cgit_print_snapshot_start("application/x-zip", filename, item);
34 write_zip_archive(&args);
35}
36
37
38void cgit_print_snapshot(struct cacheitem *item, const char *hex,
39 const char *format, const char *prefix,
40 const char *filename)
41{
42 if (!strcmp(format, "zip"))
43 cgit_print_zip(item, hex, prefix, filename);
44 else
45 cgit_print_error(fmt("Unsupported snapshot format: %s",
46 format));
47}