summaryrefslogtreecommitdiffabout
Unidiff
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
@@ -480,193 +480,220 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
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}