summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--.gitignore1
-rw-r--r--Makefile29
-rw-r--r--cgit.c3
-rw-r--r--cgit.h2
-rwxr-xr-xgen-version.sh4
-rw-r--r--shared.c2
-rw-r--r--ui-shared.c2
7 files changed, 27 insertions, 16 deletions
diff --git a/.gitignore b/.gitignore
index c4c9ac3..5664962 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
1# Files I don't care to see in git-status/commit 1# Files I don't care to see in git-status/commit
2cgit 2cgit
3VERSION
3*.o 4*.o
4*~ 5*~
diff --git a/Makefile b/Makefile
index c2a5736..e69ad7e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,87 +1,94 @@
1CGIT_VERSION = 0.5
2
3prefix = /var/www/htdocs/cgit 1prefix = /var/www/htdocs/cgit
4
5SHA1_HEADER = <openssl/sha.h> 2SHA1_HEADER = <openssl/sha.h>
6CACHE_ROOT = /var/cache/cgit 3CACHE_ROOT = /var/cache/cgit
7CGIT_CONFIG = /etc/cgitrc 4CGIT_CONFIG = /etc/cgitrc
8CGIT_SCRIPT_NAME = cgit.cgi 5CGIT_SCRIPT_NAME = cgit.cgi
9 6
10# 7#
11# Let the user override the above settings. 8# Let the user override the above settings.
12# 9#
13-include cgit.conf 10-include cgit.conf
14 11
12
13CGIT_VERSION = 0.5
14
15all: cgit
16
17VERSION:
18 gen-version.sh
19
20-include VERSION
21
22
15EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 23EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
16OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 24OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
17 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ 25 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
18 ui-snapshot.o ui-blob.o 26 ui-snapshot.o ui-blob.o
19 27
20CFLAGS += -Wall 28CFLAGS += -Wall
21 29
22ifdef DEBUG 30ifdef DEBUG
23 CFLAGS += -g 31 CFLAGS += -g
24endif 32endif
25 33
26CFLAGS += -Igit 34CFLAGS += -Igit
27CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 35CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
28CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 36CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
29CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 37CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
30CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 38CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
31 39
32 40
33# 41#
34# If make is run on a nongit platform, get the git sources as a tarball. 42# If make is run on a nongit platform, get the git sources as a tarball.
35# 43#
36GITVER = $(shell git version 2>/dev/null || echo nogit) 44GITVER = $(shell git version 2>/dev/null || echo nogit)
37ifeq ($(GITVER),nogit) 45ifeq ($(GITVER),nogit)
38GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2 46GITURL = http://www.kernel.org/pub/software/scm/git/git-1.5.2.tar.bz2
39INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git) 47INITGIT = test -e git/git.c || ((curl "$(GITURL)" | tar -xj) && mv git-1.5.2 git)
40else 48else
41INITGIT = ./submodules.sh -i 49INITGIT = ./submodules.sh -i
42endif 50endif
43 51
44 52
45# 53cgit: cgit.c cgit.h VERSION $(OBJECTS)
46# basic build rules
47#
48all: cgit
49
50cgit: cgit.c cgit.h $(OBJECTS)
51 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 54 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
52 55
53$(OBJECTS): cgit.h git/libgit.a 56$(OBJECTS): cgit.h git/libgit.a
54 57
55git/libgit.a: 58git/libgit.a:
56 $(INITGIT) 59 $(INITGIT)
57 $(MAKE) -C git 60 $(MAKE) -C git
58 61
59# 62#
60# phony targets 63# phony targets
61# 64#
62install: all clean-cache 65install: all clean-cache
63 mkdir -p $(prefix) 66 mkdir -p $(prefix)
64 install cgit $(prefix)/$(CGIT_SCRIPT_NAME) 67 install cgit $(prefix)/$(CGIT_SCRIPT_NAME)
65 install cgit.css $(prefix)/cgit.css 68 install cgit.css $(prefix)/cgit.css
66 69
67clean-cgit: 70clean-cgit:
68 rm -f cgit *.o 71 rm -f cgit VERSION *.o
69 72
70distclean-cgit: clean-cgit 73distclean-cgit: clean-cgit
71 git clean -d -x 74 git clean -d -x
72 75
73clean-sub: 76clean-sub:
74 $(MAKE) -C git clean 77 $(MAKE) -C git clean
75 78
76distclean-sub: clean-sub 79distclean-sub: clean-sub
77 $(shell cd git && git clean -d -x) 80 $(shell cd git && git clean -d -x)
78 81
79clean-cache: 82clean-cache:
80 rm -rf $(CACHE_ROOT)/* 83 rm -rf $(CACHE_ROOT)/*
81 84
82clean: clean-cgit clean-sub 85clean: clean-cgit clean-sub
83 86
84distclean: distclean-cgit distclean-sub 87distclean: distclean-cgit distclean-sub
85 88
89version: clean-cgit
90 ./gen-version.sh
91 make
92
86.PHONY: all install clean clean-cgit clean-sub clean-cache \ 93.PHONY: all install clean clean-cgit clean-sub clean-cache \
87 distclean distclean-cgit distclean-sub 94 distclean distclean-cgit distclean-sub release version
diff --git a/cgit.c b/cgit.c
index 3fc90bf..11dff76 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,250 +1,247 @@
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;
12
13
14static int cgit_prepare_cache(struct cacheitem *item) 11static int cgit_prepare_cache(struct cacheitem *item)
15{ 12{
16 if (!cgit_repo && cgit_query_repo) { 13 if (!cgit_repo && cgit_query_repo) {
17 char *title = fmt("%s - %s", cgit_root_title, "Bad request"); 14 char *title = fmt("%s - %s", cgit_root_title, "Bad request");
18 cgit_print_docstart(title, item); 15 cgit_print_docstart(title, item);
19 cgit_print_pageheader(title, 0); 16 cgit_print_pageheader(title, 0);
20 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo)); 17 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo));
21 cgit_print_docend(); 18 cgit_print_docend();
22 return 0; 19 return 0;
23 } 20 }
24 21
25 if (!cgit_repo) { 22 if (!cgit_repo) {
26 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root)); 23 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root));
27 item->ttl = cgit_cache_root_ttl; 24 item->ttl = cgit_cache_root_ttl;
28 return 1; 25 return 1;
29 } 26 }
30 27
31 if (!cgit_cmd) { 28 if (!cgit_cmd) {
32 item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root, 29 item->name = xstrdup(fmt("%s/%s/index.html", cgit_cache_root,
33 cache_safe_filename(cgit_repo->url))); 30 cache_safe_filename(cgit_repo->url)));
34 item->ttl = cgit_cache_repo_ttl; 31 item->ttl = cgit_cache_repo_ttl;
35 } else { 32 } else {
36 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root, 33 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
37 cache_safe_filename(cgit_repo->url), cgit_query_page, 34 cache_safe_filename(cgit_repo->url), cgit_query_page,
38 cache_safe_filename(cgit_querystring))); 35 cache_safe_filename(cgit_querystring)));
39 if (cgit_query_has_symref) 36 if (cgit_query_has_symref)
40 item->ttl = cgit_cache_dynamic_ttl; 37 item->ttl = cgit_cache_dynamic_ttl;
41 else if (cgit_query_has_sha1) 38 else if (cgit_query_has_sha1)
42 item->ttl = cgit_cache_static_ttl; 39 item->ttl = cgit_cache_static_ttl;
43 else 40 else
44 item->ttl = cgit_cache_repo_ttl; 41 item->ttl = cgit_cache_repo_ttl;
45 } 42 }
46 return 1; 43 return 1;
47} 44}
48 45
49static void cgit_print_repo_page(struct cacheitem *item) 46static void cgit_print_repo_page(struct cacheitem *item)
50{ 47{
51 char *title; 48 char *title;
52 int show_search; 49 int show_search;
53 50
54 if (!cgit_query_head) 51 if (!cgit_query_head)
55 cgit_query_head = cgit_repo->defbranch; 52 cgit_query_head = cgit_repo->defbranch;
56 53
57 if (chdir(cgit_repo->path)) { 54 if (chdir(cgit_repo->path)) {
58 title = fmt("%s - %s", cgit_root_title, "Bad request"); 55 title = fmt("%s - %s", cgit_root_title, "Bad request");
59 cgit_print_docstart(title, item); 56 cgit_print_docstart(title, item);
60 cgit_print_pageheader(title, 0); 57 cgit_print_pageheader(title, 0);
61 cgit_print_error(fmt("Unable to scan repository: %s", 58 cgit_print_error(fmt("Unable to scan repository: %s",
62 strerror(errno))); 59 strerror(errno)));
63 cgit_print_docend(); 60 cgit_print_docend();
64 return; 61 return;
65 } 62 }
66 63
67 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 64 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
68 show_search = 0; 65 show_search = 0;
69 setenv("GIT_DIR", cgit_repo->path, 1); 66 setenv("GIT_DIR", cgit_repo->path, 1);
70 67
71 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 68 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
72 cgit_print_snapshot(item, cgit_query_sha1, "zip", 69 cgit_print_snapshot(item, cgit_query_sha1, "zip",
73 cgit_repo->url, cgit_query_name); 70 cgit_repo->url, cgit_query_name);
74 return; 71 return;
75 } 72 }
76 73
77 if (cgit_cmd == CMD_BLOB) { 74 if (cgit_cmd == CMD_BLOB) {
78 cgit_print_blob(item, cgit_query_sha1, cgit_query_path); 75 cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
79 return; 76 return;
80 } 77 }
81 78
82 show_search = (cgit_cmd == CMD_LOG); 79 show_search = (cgit_cmd == CMD_LOG);
83 cgit_print_docstart(title, item); 80 cgit_print_docstart(title, item);
84 if (!cgit_cmd) { 81 if (!cgit_cmd) {
85 cgit_print_pageheader("summary", show_search); 82 cgit_print_pageheader("summary", show_search);
86 cgit_print_summary(); 83 cgit_print_summary();
87 cgit_print_docend(); 84 cgit_print_docend();
88 return; 85 return;
89 } 86 }
90 87
91 cgit_print_pageheader(cgit_query_page, show_search); 88 cgit_print_pageheader(cgit_query_page, show_search);
92 89
93 switch(cgit_cmd) { 90 switch(cgit_cmd) {
94 case CMD_LOG: 91 case CMD_LOG:
95 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 92 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
96 cgit_max_commit_count, cgit_query_search, 93 cgit_max_commit_count, cgit_query_search,
97 cgit_query_path, 1); 94 cgit_query_path, 1);
98 break; 95 break;
99 case CMD_TREE: 96 case CMD_TREE:
100 cgit_print_tree(cgit_query_sha1, cgit_query_path); 97 cgit_print_tree(cgit_query_sha1, cgit_query_path);
101 break; 98 break;
102 case CMD_COMMIT: 99 case CMD_COMMIT:
103 cgit_print_commit(cgit_query_sha1); 100 cgit_print_commit(cgit_query_sha1);
104 break; 101 break;
105 case CMD_DIFF: 102 case CMD_DIFF:
106 cgit_print_diff(cgit_query_sha1, cgit_query_sha2); 103 cgit_print_diff(cgit_query_sha1, cgit_query_sha2);
107 break; 104 break;
108 default: 105 default:
109 cgit_print_error("Invalid request"); 106 cgit_print_error("Invalid request");
110 } 107 }
111 cgit_print_docend(); 108 cgit_print_docend();
112} 109}
113 110
114static void cgit_fill_cache(struct cacheitem *item, int use_cache) 111static void cgit_fill_cache(struct cacheitem *item, int use_cache)
115{ 112{
116 static char buf[PATH_MAX]; 113 static char buf[PATH_MAX];
117 int stdout2; 114 int stdout2;
118 115
119 getcwd(buf, sizeof(buf)); 116 getcwd(buf, sizeof(buf));
120 item->st.st_mtime = time(NULL); 117 item->st.st_mtime = time(NULL);
121 118
122 if (use_cache) { 119 if (use_cache) {
123 stdout2 = chk_positive(dup(STDOUT_FILENO), 120 stdout2 = chk_positive(dup(STDOUT_FILENO),
124 "Preserving STDOUT"); 121 "Preserving STDOUT");
125 chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); 122 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
126 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); 123 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
127 } 124 }
128 125
129 if (cgit_repo) 126 if (cgit_repo)
130 cgit_print_repo_page(item); 127 cgit_print_repo_page(item);
131 else 128 else
132 cgit_print_repolist(item); 129 cgit_print_repolist(item);
133 130
134 if (use_cache) { 131 if (use_cache) {
135 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT"); 132 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
136 chk_positive(dup2(stdout2, STDOUT_FILENO), 133 chk_positive(dup2(stdout2, STDOUT_FILENO),
137 "Restoring original STDOUT"); 134 "Restoring original STDOUT");
138 chk_zero(close(stdout2), "Closing temporary STDOUT"); 135 chk_zero(close(stdout2), "Closing temporary STDOUT");
139 } 136 }
140 137
141 chdir(buf); 138 chdir(buf);
142} 139}
143 140
144static void cgit_check_cache(struct cacheitem *item) 141static void cgit_check_cache(struct cacheitem *item)
145{ 142{
146 int i = 0; 143 int i = 0;
147 144
148 top: 145 top:
149 if (++i > cgit_max_lock_attempts) { 146 if (++i > cgit_max_lock_attempts) {
150 die("cgit_refresh_cache: unable to lock %s: %s", 147 die("cgit_refresh_cache: unable to lock %s: %s",
151 item->name, strerror(errno)); 148 item->name, strerror(errno));
152 } 149 }
153 if (!cache_exist(item)) { 150 if (!cache_exist(item)) {
154 if (!cache_lock(item)) { 151 if (!cache_lock(item)) {
155 sleep(1); 152 sleep(1);
156 goto top; 153 goto top;
157 } 154 }
158 if (!cache_exist(item)) { 155 if (!cache_exist(item)) {
159 cgit_fill_cache(item, 1); 156 cgit_fill_cache(item, 1);
160 cache_unlock(item); 157 cache_unlock(item);
161 } else { 158 } else {
162 cache_cancel_lock(item); 159 cache_cancel_lock(item);
163 } 160 }
164 } else if (cache_expired(item) && cache_lock(item)) { 161 } else if (cache_expired(item) && cache_lock(item)) {
165 if (cache_expired(item)) { 162 if (cache_expired(item)) {
166 cgit_fill_cache(item, 1); 163 cgit_fill_cache(item, 1);
167 cache_unlock(item); 164 cache_unlock(item);
168 } else { 165 } else {
169 cache_cancel_lock(item); 166 cache_cancel_lock(item);
170 } 167 }
171 } 168 }
172} 169}
173 170
174static void cgit_print_cache(struct cacheitem *item) 171static void cgit_print_cache(struct cacheitem *item)
175{ 172{
176 static char buf[4096]; 173 static char buf[4096];
177 ssize_t i; 174 ssize_t i;
178 175
179 int fd = open(item->name, O_RDONLY); 176 int fd = open(item->name, O_RDONLY);
180 if (fd<0) 177 if (fd<0)
181 die("Unable to open cached file %s", item->name); 178 die("Unable to open cached file %s", item->name);
182 179
183 while((i=read(fd, buf, sizeof(buf))) > 0) 180 while((i=read(fd, buf, sizeof(buf))) > 0)
184 write(STDOUT_FILENO, buf, i); 181 write(STDOUT_FILENO, buf, i);
185 182
186 close(fd); 183 close(fd);
187} 184}
188 185
189static void cgit_parse_args(int argc, const char **argv) 186static void cgit_parse_args(int argc, const char **argv)
190{ 187{
191 int i; 188 int i;
192 189
193 for (i = 1; i < argc; i++) { 190 for (i = 1; i < argc; i++) {
194 if (!strncmp(argv[i], "--cache=", 8)) { 191 if (!strncmp(argv[i], "--cache=", 8)) {
195 cgit_cache_root = xstrdup(argv[i]+8); 192 cgit_cache_root = xstrdup(argv[i]+8);
196 } 193 }
197 if (!strcmp(argv[i], "--nocache")) { 194 if (!strcmp(argv[i], "--nocache")) {
198 cgit_nocache = 1; 195 cgit_nocache = 1;
199 } 196 }
200 if (!strncmp(argv[i], "--query=", 8)) { 197 if (!strncmp(argv[i], "--query=", 8)) {
201 cgit_querystring = xstrdup(argv[i]+8); 198 cgit_querystring = xstrdup(argv[i]+8);
202 } 199 }
203 if (!strncmp(argv[i], "--repo=", 7)) { 200 if (!strncmp(argv[i], "--repo=", 7)) {
204 cgit_query_repo = xstrdup(argv[i]+7); 201 cgit_query_repo = xstrdup(argv[i]+7);
205 } 202 }
206 if (!strncmp(argv[i], "--page=", 7)) { 203 if (!strncmp(argv[i], "--page=", 7)) {
207 cgit_query_page = xstrdup(argv[i]+7); 204 cgit_query_page = xstrdup(argv[i]+7);
208 } 205 }
209 if (!strncmp(argv[i], "--head=", 7)) { 206 if (!strncmp(argv[i], "--head=", 7)) {
210 cgit_query_head = xstrdup(argv[i]+7); 207 cgit_query_head = xstrdup(argv[i]+7);
211 cgit_query_has_symref = 1; 208 cgit_query_has_symref = 1;
212 } 209 }
213 if (!strncmp(argv[i], "--sha1=", 7)) { 210 if (!strncmp(argv[i], "--sha1=", 7)) {
214 cgit_query_sha1 = xstrdup(argv[i]+7); 211 cgit_query_sha1 = xstrdup(argv[i]+7);
215 cgit_query_has_sha1 = 1; 212 cgit_query_has_sha1 = 1;
216 } 213 }
217 if (!strncmp(argv[i], "--ofs=", 6)) { 214 if (!strncmp(argv[i], "--ofs=", 6)) {
218 cgit_query_ofs = atoi(argv[i]+6); 215 cgit_query_ofs = atoi(argv[i]+6);
219 } 216 }
220 } 217 }
221} 218}
222 219
223int main(int argc, const char **argv) 220int main(int argc, const char **argv)
224{ 221{
225 struct cacheitem item; 222 struct cacheitem item;
226 223
227 htmlfd = STDOUT_FILENO; 224 htmlfd = STDOUT_FILENO;
228 item.st.st_mtime = time(NULL); 225 item.st.st_mtime = time(NULL);
229 cgit_repolist.length = 0; 226 cgit_repolist.length = 0;
230 cgit_repolist.count = 0; 227 cgit_repolist.count = 0;
231 cgit_repolist.repos = NULL; 228 cgit_repolist.repos = NULL;
232 229
233 cgit_read_config(CGIT_CONFIG, cgit_global_config_cb); 230 cgit_read_config(CGIT_CONFIG, cgit_global_config_cb);
234 cgit_repo = NULL; 231 cgit_repo = NULL;
235 if (getenv("SCRIPT_NAME")) 232 if (getenv("SCRIPT_NAME"))
236 cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); 233 cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
237 if (getenv("QUERY_STRING")) 234 if (getenv("QUERY_STRING"))
238 cgit_querystring = xstrdup(getenv("QUERY_STRING")); 235 cgit_querystring = xstrdup(getenv("QUERY_STRING"));
239 cgit_parse_args(argc, argv); 236 cgit_parse_args(argc, argv);
240 cgit_parse_query(cgit_querystring, cgit_querystring_cb); 237 cgit_parse_query(cgit_querystring, cgit_querystring_cb);
241 if (!cgit_prepare_cache(&item)) 238 if (!cgit_prepare_cache(&item))
242 return 0; 239 return 0;
243 if (cgit_nocache) { 240 if (cgit_nocache) {
244 cgit_fill_cache(&item, 0); 241 cgit_fill_cache(&item, 0);
245 } else { 242 } else {
246 cgit_check_cache(&item); 243 cgit_check_cache(&item);
247 cgit_print_cache(&item); 244 cgit_print_cache(&item);
248 } 245 }
249 return 0; 246 return 0;
250} 247}
diff --git a/cgit.h b/cgit.h
index bd2dd0d..9558362 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,234 +1,234 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19 19
20 20
21/* 21/*
22 * The valid cgit repo-commands 22 * The valid cgit repo-commands
23 */ 23 */
24#define CMD_LOG 1 24#define CMD_LOG 1
25#define CMD_COMMIT 2 25#define CMD_COMMIT 2
26#define CMD_DIFF 3 26#define CMD_DIFF 3
27#define CMD_TREE 4 27#define CMD_TREE 4
28#define CMD_BLOB 5 28#define CMD_BLOB 5
29#define CMD_SNAPSHOT 6 29#define CMD_SNAPSHOT 6
30 30
31 31
32/* 32/*
33 * Dateformats used on misc. pages 33 * Dateformats used on misc. pages
34 */ 34 */
35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 35#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
36#define FMT_SHORTDATE "%Y-%m-%d" 36#define FMT_SHORTDATE "%Y-%m-%d"
37 37
38 38
39/* 39/*
40 * Limits used for relative dates 40 * Limits used for relative dates
41 */ 41 */
42#define TM_MIN 60 42#define TM_MIN 60
43#define TM_HOUR (TM_MIN * 60) 43#define TM_HOUR (TM_MIN * 60)
44#define TM_DAY (TM_HOUR * 24) 44#define TM_DAY (TM_HOUR * 24)
45#define TM_WEEK (TM_DAY * 7) 45#define TM_WEEK (TM_DAY * 7)
46#define TM_YEAR (TM_DAY * 365) 46#define TM_YEAR (TM_DAY * 365)
47#define TM_MONTH (TM_YEAR / 12.0) 47#define TM_MONTH (TM_YEAR / 12.0)
48 48
49 49
50typedef void (*configfn)(const char *name, const char *value); 50typedef void (*configfn)(const char *name, const char *value);
51typedef void (*filepair_fn)(struct diff_filepair *pair); 51typedef void (*filepair_fn)(struct diff_filepair *pair);
52typedef void (*linediff_fn)(char *line, int len); 52typedef void (*linediff_fn)(char *line, int len);
53 53
54struct cacheitem { 54struct cacheitem {
55 char *name; 55 char *name;
56 struct stat st; 56 struct stat st;
57 int ttl; 57 int ttl;
58 int fd; 58 int fd;
59}; 59};
60 60
61struct repoinfo { 61struct repoinfo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *group; 68 char *group;
69 char *module_link; 69 char *module_link;
70 char *readme; 70 char *readme;
71 int snapshots; 71 int snapshots;
72 int enable_log_filecount; 72 int enable_log_filecount;
73 int enable_log_linecount; 73 int enable_log_linecount;
74}; 74};
75 75
76struct repolist { 76struct repolist {
77 int length; 77 int length;
78 int count; 78 int count;
79 struct repoinfo *repos; 79 struct repoinfo *repos;
80}; 80};
81 81
82struct commitinfo { 82struct commitinfo {
83 struct commit *commit; 83 struct commit *commit;
84 char *author; 84 char *author;
85 char *author_email; 85 char *author_email;
86 unsigned long author_date; 86 unsigned long author_date;
87 char *committer; 87 char *committer;
88 char *committer_email; 88 char *committer_email;
89 unsigned long committer_date; 89 unsigned long committer_date;
90 char *subject; 90 char *subject;
91 char *msg; 91 char *msg;
92}; 92};
93 93
94struct taginfo { 94struct taginfo {
95 char *tagger; 95 char *tagger;
96 char *tagger_email; 96 char *tagger_email;
97 int tagger_date; 97 int tagger_date;
98 char *msg; 98 char *msg;
99}; 99};
100 100
101extern const char cgit_version[]; 101extern const char *cgit_version;
102 102
103extern struct repolist cgit_repolist; 103extern struct repolist cgit_repolist;
104extern struct repoinfo *cgit_repo; 104extern struct repoinfo *cgit_repo;
105extern int cgit_cmd; 105extern int cgit_cmd;
106 106
107extern char *cgit_root_title; 107extern char *cgit_root_title;
108extern char *cgit_css; 108extern char *cgit_css;
109extern char *cgit_logo; 109extern char *cgit_logo;
110extern char *cgit_index_header; 110extern char *cgit_index_header;
111extern char *cgit_logo_link; 111extern char *cgit_logo_link;
112extern char *cgit_module_link; 112extern char *cgit_module_link;
113extern char *cgit_agefile; 113extern char *cgit_agefile;
114extern char *cgit_virtual_root; 114extern char *cgit_virtual_root;
115extern char *cgit_script_name; 115extern char *cgit_script_name;
116extern char *cgit_cache_root; 116extern char *cgit_cache_root;
117extern char *cgit_repo_group; 117extern char *cgit_repo_group;
118 118
119extern int cgit_nocache; 119extern int cgit_nocache;
120extern int cgit_snapshots; 120extern int cgit_snapshots;
121extern int cgit_enable_log_filecount; 121extern int cgit_enable_log_filecount;
122extern int cgit_enable_log_linecount; 122extern int cgit_enable_log_linecount;
123extern int cgit_max_lock_attempts; 123extern int cgit_max_lock_attempts;
124extern int cgit_cache_root_ttl; 124extern int cgit_cache_root_ttl;
125extern int cgit_cache_repo_ttl; 125extern int cgit_cache_repo_ttl;
126extern int cgit_cache_dynamic_ttl; 126extern int cgit_cache_dynamic_ttl;
127extern int cgit_cache_static_ttl; 127extern int cgit_cache_static_ttl;
128extern int cgit_cache_max_create_time; 128extern int cgit_cache_max_create_time;
129extern int cgit_summary_log; 129extern int cgit_summary_log;
130 130
131extern int cgit_max_msg_len; 131extern int cgit_max_msg_len;
132extern int cgit_max_repodesc_len; 132extern int cgit_max_repodesc_len;
133extern int cgit_max_commit_count; 133extern int cgit_max_commit_count;
134 134
135extern int cgit_query_has_symref; 135extern int cgit_query_has_symref;
136extern int cgit_query_has_sha1; 136extern int cgit_query_has_sha1;
137 137
138extern char *cgit_querystring; 138extern char *cgit_querystring;
139extern char *cgit_query_repo; 139extern char *cgit_query_repo;
140extern char *cgit_query_page; 140extern char *cgit_query_page;
141extern char *cgit_query_search; 141extern char *cgit_query_search;
142extern char *cgit_query_head; 142extern char *cgit_query_head;
143extern char *cgit_query_sha1; 143extern char *cgit_query_sha1;
144extern char *cgit_query_sha2; 144extern char *cgit_query_sha2;
145extern char *cgit_query_path; 145extern char *cgit_query_path;
146extern char *cgit_query_name; 146extern char *cgit_query_name;
147extern int cgit_query_ofs; 147extern int cgit_query_ofs;
148 148
149extern int htmlfd; 149extern int htmlfd;
150 150
151extern int cgit_get_cmd_index(const char *cmd); 151extern int cgit_get_cmd_index(const char *cmd);
152extern struct repoinfo *cgit_get_repoinfo(const char *url); 152extern struct repoinfo *cgit_get_repoinfo(const char *url);
153extern void cgit_global_config_cb(const char *name, const char *value); 153extern void cgit_global_config_cb(const char *name, const char *value);
154extern void cgit_repo_config_cb(const char *name, const char *value); 154extern void cgit_repo_config_cb(const char *name, const char *value);
155extern void cgit_querystring_cb(const char *name, const char *value); 155extern void cgit_querystring_cb(const char *name, const char *value);
156 156
157extern int chk_zero(int result, char *msg); 157extern int chk_zero(int result, char *msg);
158extern int chk_positive(int result, char *msg); 158extern int chk_positive(int result, char *msg);
159 159
160extern int hextoint(char c); 160extern int hextoint(char c);
161 161
162extern void *cgit_free_commitinfo(struct commitinfo *info); 162extern void *cgit_free_commitinfo(struct commitinfo *info);
163 163
164extern int cgit_diff_files(const unsigned char *old_sha1, 164extern int cgit_diff_files(const unsigned char *old_sha1,
165 const unsigned char *new_sha1, 165 const unsigned char *new_sha1,
166 linediff_fn fn); 166 linediff_fn fn);
167 167
168extern void cgit_diff_tree(const unsigned char *old_sha1, 168extern void cgit_diff_tree(const unsigned char *old_sha1,
169 const unsigned char *new_sha1, 169 const unsigned char *new_sha1,
170 filepair_fn fn); 170 filepair_fn fn);
171 171
172extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 172extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
173 173
174extern char *fmt(const char *format,...); 174extern char *fmt(const char *format,...);
175 175
176extern void html(const char *txt); 176extern void html(const char *txt);
177extern void htmlf(const char *format,...); 177extern void htmlf(const char *format,...);
178extern void html_txt(char *txt); 178extern void html_txt(char *txt);
179extern void html_ntxt(int len, char *txt); 179extern void html_ntxt(int len, char *txt);
180extern void html_attr(char *txt); 180extern void html_attr(char *txt);
181extern void html_hidden(char *name, char *value); 181extern void html_hidden(char *name, char *value);
182extern void html_link_open(char *url, char *title, char *class); 182extern void html_link_open(char *url, char *title, char *class);
183extern void html_link_close(void); 183extern void html_link_close(void);
184extern void html_filemode(unsigned short mode); 184extern void html_filemode(unsigned short mode);
185extern int html_include(const char *filename); 185extern int html_include(const char *filename);
186 186
187extern int cgit_read_config(const char *filename, configfn fn); 187extern int cgit_read_config(const char *filename, configfn fn);
188extern int cgit_parse_query(char *txt, configfn fn); 188extern int cgit_parse_query(char *txt, configfn fn);
189extern struct commitinfo *cgit_parse_commit(struct commit *commit); 189extern struct commitinfo *cgit_parse_commit(struct commit *commit);
190extern struct taginfo *cgit_parse_tag(struct tag *tag); 190extern struct taginfo *cgit_parse_tag(struct tag *tag);
191extern void cgit_parse_url(const char *url); 191extern void cgit_parse_url(const char *url);
192 192
193extern char *cache_safe_filename(const char *unsafe); 193extern char *cache_safe_filename(const char *unsafe);
194extern int cache_lock(struct cacheitem *item); 194extern int cache_lock(struct cacheitem *item);
195extern int cache_unlock(struct cacheitem *item); 195extern int cache_unlock(struct cacheitem *item);
196extern int cache_cancel_lock(struct cacheitem *item); 196extern int cache_cancel_lock(struct cacheitem *item);
197extern int cache_exist(struct cacheitem *item); 197extern int cache_exist(struct cacheitem *item);
198extern int cache_expired(struct cacheitem *item); 198extern int cache_expired(struct cacheitem *item);
199 199
200extern char *cgit_repourl(const char *reponame); 200extern char *cgit_repourl(const char *reponame);
201extern char *cgit_pageurl(const char *reponame, const char *pagename, 201extern char *cgit_pageurl(const char *reponame, const char *pagename,
202 const char *query); 202 const char *query);
203 203
204extern void cgit_tree_link(char *name, char *title, char *class, char *head, 204extern void cgit_tree_link(char *name, char *title, char *class, char *head,
205 char *rev, char *path); 205 char *rev, char *path);
206extern void cgit_log_link(char *name, char *title, char *class, char *head, 206extern void cgit_log_link(char *name, char *title, char *class, char *head,
207 char *rev, char *path); 207 char *rev, char *path);
208extern void cgit_commit_link(char *name, char *title, char *class, char *head, 208extern void cgit_commit_link(char *name, char *title, char *class, char *head,
209 char *rev); 209 char *rev);
210extern void cgit_diff_link(char *name, char *title, char *class, char *head, 210extern void cgit_diff_link(char *name, char *title, char *class, char *head,
211 char *new_rev, char *old_rev, char *path); 211 char *new_rev, char *old_rev, char *path);
212 212
213extern void cgit_print_error(char *msg); 213extern void cgit_print_error(char *msg);
214extern void cgit_print_date(time_t secs, char *format); 214extern void cgit_print_date(time_t secs, char *format);
215extern void cgit_print_age(time_t t, time_t max_relative, char *format); 215extern void cgit_print_age(time_t t, time_t max_relative, char *format);
216extern void cgit_print_docstart(char *title, struct cacheitem *item); 216extern void cgit_print_docstart(char *title, struct cacheitem *item);
217extern void cgit_print_docend(); 217extern void cgit_print_docend();
218extern void cgit_print_pageheader(char *title, int show_search); 218extern void cgit_print_pageheader(char *title, int show_search);
219extern void cgit_print_snapshot_start(const char *mimetype, 219extern void cgit_print_snapshot_start(const char *mimetype,
220 const char *filename, 220 const char *filename,
221 struct cacheitem *item); 221 struct cacheitem *item);
222 222
223extern void cgit_print_repolist(struct cacheitem *item); 223extern void cgit_print_repolist(struct cacheitem *item);
224extern void cgit_print_summary(); 224extern void cgit_print_summary();
225extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 225extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager);
226extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 226extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
227extern void cgit_print_tree(const char *rev, char *path); 227extern void cgit_print_tree(const char *rev, char *path);
228extern void cgit_print_commit(char *hex); 228extern void cgit_print_commit(char *hex);
229extern void cgit_print_diff(const char *new_hex, const char *old_hex); 229extern void cgit_print_diff(const char *new_hex, const char *old_hex);
230extern void cgit_print_snapshot(struct cacheitem *item, const char *hex, 230extern void cgit_print_snapshot(struct cacheitem *item, const char *hex,
231 const char *format, const char *prefix, 231 const char *format, const char *prefix,
232 const char *filename); 232 const char *filename);
233 233
234#endif /* CGIT_H */ 234#endif /* CGIT_H */
diff --git a/gen-version.sh b/gen-version.sh
new file mode 100755
index 0000000..4c60f60
--- a/dev/null
+++ b/gen-version.sh
@@ -0,0 +1,4 @@
1v=$(git-describe --abbrev=4 HEAD | sed -e 's/-/./g')
2test -z "$v" && exit 1
3echo "CGIT_VERSION = $v"
4echo "CGIT_VERSION = $v" > VERSION
diff --git a/shared.c b/shared.c
index f20fb5c..f7f43b2 100644
--- a/shared.c
+++ b/shared.c
@@ -1,270 +1,272 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback functions
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11struct repolist cgit_repolist; 11struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13int cgit_cmd; 13int cgit_cmd;
14 14
15const char *cgit_version = CGIT_VERSION;
16
15char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
16char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
17char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
18char *cgit_index_header = NULL; 20char *cgit_index_header = NULL;
19char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
20char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; 22char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
21char *cgit_agefile = "info/web/last-modified"; 23char *cgit_agefile = "info/web/last-modified";
22char *cgit_virtual_root = NULL; 24char *cgit_virtual_root = NULL;
23char *cgit_script_name = CGIT_SCRIPT_NAME; 25char *cgit_script_name = CGIT_SCRIPT_NAME;
24char *cgit_cache_root = "/var/cache/cgit"; 26char *cgit_cache_root = "/var/cache/cgit";
25char *cgit_repo_group = NULL; 27char *cgit_repo_group = NULL;
26 28
27int cgit_nocache = 0; 29int cgit_nocache = 0;
28int cgit_snapshots = 0; 30int cgit_snapshots = 0;
29int cgit_enable_log_filecount = 0; 31int cgit_enable_log_filecount = 0;
30int cgit_enable_log_linecount = 0; 32int cgit_enable_log_linecount = 0;
31int cgit_max_lock_attempts = 5; 33int cgit_max_lock_attempts = 5;
32int cgit_cache_root_ttl = 5; 34int cgit_cache_root_ttl = 5;
33int cgit_cache_repo_ttl = 5; 35int cgit_cache_repo_ttl = 5;
34int cgit_cache_dynamic_ttl = 5; 36int cgit_cache_dynamic_ttl = 5;
35int cgit_cache_static_ttl = -1; 37int cgit_cache_static_ttl = -1;
36int cgit_cache_max_create_time = 5; 38int cgit_cache_max_create_time = 5;
37int cgit_summary_log = 0; 39int cgit_summary_log = 0;
38 40
39int cgit_max_msg_len = 60; 41int cgit_max_msg_len = 60;
40int cgit_max_repodesc_len = 60; 42int cgit_max_repodesc_len = 60;
41int cgit_max_commit_count = 50; 43int cgit_max_commit_count = 50;
42 44
43int cgit_query_has_symref = 0; 45int cgit_query_has_symref = 0;
44int cgit_query_has_sha1 = 0; 46int cgit_query_has_sha1 = 0;
45 47
46char *cgit_querystring = NULL; 48char *cgit_querystring = NULL;
47char *cgit_query_repo = NULL; 49char *cgit_query_repo = NULL;
48char *cgit_query_page = NULL; 50char *cgit_query_page = NULL;
49char *cgit_query_head = NULL; 51char *cgit_query_head = NULL;
50char *cgit_query_search = NULL; 52char *cgit_query_search = NULL;
51char *cgit_query_sha1 = NULL; 53char *cgit_query_sha1 = NULL;
52char *cgit_query_sha2 = NULL; 54char *cgit_query_sha2 = NULL;
53char *cgit_query_path = NULL; 55char *cgit_query_path = NULL;
54char *cgit_query_name = NULL; 56char *cgit_query_name = NULL;
55int cgit_query_ofs = 0; 57int cgit_query_ofs = 0;
56 58
57int htmlfd = 0; 59int htmlfd = 0;
58 60
59 61
60int cgit_get_cmd_index(const char *cmd) 62int cgit_get_cmd_index(const char *cmd)
61{ 63{
62 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 64 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
63 "snapshot", NULL}; 65 "snapshot", NULL};
64 int i; 66 int i;
65 67
66 for(i = 0; cmds[i]; i++) 68 for(i = 0; cmds[i]; i++)
67 if (!strcmp(cmd, cmds[i])) 69 if (!strcmp(cmd, cmds[i]))
68 return i + 1; 70 return i + 1;
69 return 0; 71 return 0;
70} 72}
71 73
72int chk_zero(int result, char *msg) 74int chk_zero(int result, char *msg)
73{ 75{
74 if (result != 0) 76 if (result != 0)
75 die("%s: %s", msg, strerror(errno)); 77 die("%s: %s", msg, strerror(errno));
76 return result; 78 return result;
77} 79}
78 80
79int chk_positive(int result, char *msg) 81int chk_positive(int result, char *msg)
80{ 82{
81 if (result <= 0) 83 if (result <= 0)
82 die("%s: %s", msg, strerror(errno)); 84 die("%s: %s", msg, strerror(errno));
83 return result; 85 return result;
84} 86}
85 87
86struct repoinfo *add_repo(const char *url) 88struct repoinfo *add_repo(const char *url)
87{ 89{
88 struct repoinfo *ret; 90 struct repoinfo *ret;
89 91
90 if (++cgit_repolist.count > cgit_repolist.length) { 92 if (++cgit_repolist.count > cgit_repolist.length) {
91 if (cgit_repolist.length == 0) 93 if (cgit_repolist.length == 0)
92 cgit_repolist.length = 8; 94 cgit_repolist.length = 8;
93 else 95 else
94 cgit_repolist.length *= 2; 96 cgit_repolist.length *= 2;
95 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 97 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
96 cgit_repolist.length * 98 cgit_repolist.length *
97 sizeof(struct repoinfo)); 99 sizeof(struct repoinfo));
98 } 100 }
99 101
100 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 102 ret = &cgit_repolist.repos[cgit_repolist.count-1];
101 ret->url = xstrdup(url); 103 ret->url = xstrdup(url);
102 ret->name = ret->url; 104 ret->name = ret->url;
103 ret->path = NULL; 105 ret->path = NULL;
104 ret->desc = NULL; 106 ret->desc = NULL;
105 ret->owner = NULL; 107 ret->owner = NULL;
106 ret->group = cgit_repo_group; 108 ret->group = cgit_repo_group;
107 ret->defbranch = "master"; 109 ret->defbranch = "master";
108 ret->snapshots = cgit_snapshots; 110 ret->snapshots = cgit_snapshots;
109 ret->enable_log_filecount = cgit_enable_log_filecount; 111 ret->enable_log_filecount = cgit_enable_log_filecount;
110 ret->enable_log_linecount = cgit_enable_log_linecount; 112 ret->enable_log_linecount = cgit_enable_log_linecount;
111 ret->module_link = cgit_module_link; 113 ret->module_link = cgit_module_link;
112 ret->readme = NULL; 114 ret->readme = NULL;
113 return ret; 115 return ret;
114} 116}
115 117
116struct repoinfo *cgit_get_repoinfo(const char *url) 118struct repoinfo *cgit_get_repoinfo(const char *url)
117{ 119{
118 int i; 120 int i;
119 struct repoinfo *repo; 121 struct repoinfo *repo;
120 122
121 for (i=0; i<cgit_repolist.count; i++) { 123 for (i=0; i<cgit_repolist.count; i++) {
122 repo = &cgit_repolist.repos[i]; 124 repo = &cgit_repolist.repos[i];
123 if (!strcmp(repo->url, url)) 125 if (!strcmp(repo->url, url))
124 return repo; 126 return repo;
125 } 127 }
126 return NULL; 128 return NULL;
127} 129}
128 130
129void cgit_global_config_cb(const char *name, const char *value) 131void cgit_global_config_cb(const char *name, const char *value)
130{ 132{
131 if (!strcmp(name, "root-title")) 133 if (!strcmp(name, "root-title"))
132 cgit_root_title = xstrdup(value); 134 cgit_root_title = xstrdup(value);
133 else if (!strcmp(name, "css")) 135 else if (!strcmp(name, "css"))
134 cgit_css = xstrdup(value); 136 cgit_css = xstrdup(value);
135 else if (!strcmp(name, "logo")) 137 else if (!strcmp(name, "logo"))
136 cgit_logo = xstrdup(value); 138 cgit_logo = xstrdup(value);
137 else if (!strcmp(name, "index-header")) 139 else if (!strcmp(name, "index-header"))
138 cgit_index_header = xstrdup(value); 140 cgit_index_header = xstrdup(value);
139 else if (!strcmp(name, "logo-link")) 141 else if (!strcmp(name, "logo-link"))
140 cgit_logo_link = xstrdup(value); 142 cgit_logo_link = xstrdup(value);
141 else if (!strcmp(name, "module-link")) 143 else if (!strcmp(name, "module-link"))
142 cgit_module_link = xstrdup(value); 144 cgit_module_link = xstrdup(value);
143 else if (!strcmp(name, "virtual-root")) 145 else if (!strcmp(name, "virtual-root"))
144 cgit_virtual_root = xstrdup(value); 146 cgit_virtual_root = xstrdup(value);
145 else if (!strcmp(name, "nocache")) 147 else if (!strcmp(name, "nocache"))
146 cgit_nocache = atoi(value); 148 cgit_nocache = atoi(value);
147 else if (!strcmp(name, "snapshots")) 149 else if (!strcmp(name, "snapshots"))
148 cgit_snapshots = atoi(value); 150 cgit_snapshots = atoi(value);
149 else if (!strcmp(name, "enable-log-filecount")) 151 else if (!strcmp(name, "enable-log-filecount"))
150 cgit_enable_log_filecount = atoi(value); 152 cgit_enable_log_filecount = atoi(value);
151 else if (!strcmp(name, "enable-log-linecount")) 153 else if (!strcmp(name, "enable-log-linecount"))
152 cgit_enable_log_linecount = atoi(value); 154 cgit_enable_log_linecount = atoi(value);
153 else if (!strcmp(name, "cache-root")) 155 else if (!strcmp(name, "cache-root"))
154 cgit_cache_root = xstrdup(value); 156 cgit_cache_root = xstrdup(value);
155 else if (!strcmp(name, "cache-root-ttl")) 157 else if (!strcmp(name, "cache-root-ttl"))
156 cgit_cache_root_ttl = atoi(value); 158 cgit_cache_root_ttl = atoi(value);
157 else if (!strcmp(name, "cache-repo-ttl")) 159 else if (!strcmp(name, "cache-repo-ttl"))
158 cgit_cache_repo_ttl = atoi(value); 160 cgit_cache_repo_ttl = atoi(value);
159 else if (!strcmp(name, "cache-static-ttl")) 161 else if (!strcmp(name, "cache-static-ttl"))
160 cgit_cache_static_ttl = atoi(value); 162 cgit_cache_static_ttl = atoi(value);
161 else if (!strcmp(name, "cache-dynamic-ttl")) 163 else if (!strcmp(name, "cache-dynamic-ttl"))
162 cgit_cache_dynamic_ttl = atoi(value); 164 cgit_cache_dynamic_ttl = atoi(value);
163 else if (!strcmp(name, "max-message-length")) 165 else if (!strcmp(name, "max-message-length"))
164 cgit_max_msg_len = atoi(value); 166 cgit_max_msg_len = atoi(value);
165 else if (!strcmp(name, "max-repodesc-length")) 167 else if (!strcmp(name, "max-repodesc-length"))
166 cgit_max_repodesc_len = atoi(value); 168 cgit_max_repodesc_len = atoi(value);
167 else if (!strcmp(name, "max-commit-count")) 169 else if (!strcmp(name, "max-commit-count"))
168 cgit_max_commit_count = atoi(value); 170 cgit_max_commit_count = atoi(value);
169 else if (!strcmp(name, "summary-log")) 171 else if (!strcmp(name, "summary-log"))
170 cgit_summary_log = atoi(value); 172 cgit_summary_log = atoi(value);
171 else if (!strcmp(name, "agefile")) 173 else if (!strcmp(name, "agefile"))
172 cgit_agefile = xstrdup(value); 174 cgit_agefile = xstrdup(value);
173 else if (!strcmp(name, "repo.group")) 175 else if (!strcmp(name, "repo.group"))
174 cgit_repo_group = xstrdup(value); 176 cgit_repo_group = xstrdup(value);
175 else if (!strcmp(name, "repo.url")) 177 else if (!strcmp(name, "repo.url"))
176 cgit_repo = add_repo(value); 178 cgit_repo = add_repo(value);
177 else if (!strcmp(name, "repo.name")) 179 else if (!strcmp(name, "repo.name"))
178 cgit_repo->name = xstrdup(value); 180 cgit_repo->name = xstrdup(value);
179 else if (cgit_repo && !strcmp(name, "repo.path")) 181 else if (cgit_repo && !strcmp(name, "repo.path"))
180 cgit_repo->path = xstrdup(value); 182 cgit_repo->path = xstrdup(value);
181 else if (cgit_repo && !strcmp(name, "repo.desc")) 183 else if (cgit_repo && !strcmp(name, "repo.desc"))
182 cgit_repo->desc = xstrdup(value); 184 cgit_repo->desc = xstrdup(value);
183 else if (cgit_repo && !strcmp(name, "repo.owner")) 185 else if (cgit_repo && !strcmp(name, "repo.owner"))
184 cgit_repo->owner = xstrdup(value); 186 cgit_repo->owner = xstrdup(value);
185 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 187 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
186 cgit_repo->defbranch = xstrdup(value); 188 cgit_repo->defbranch = xstrdup(value);
187 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 189 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
188 cgit_repo->snapshots = cgit_snapshots * atoi(value); 190 cgit_repo->snapshots = cgit_snapshots * atoi(value);
189 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 191 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
190 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 192 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
191 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 193 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
192 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); 194 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
193 else if (cgit_repo && !strcmp(name, "repo.module-link")) 195 else if (cgit_repo && !strcmp(name, "repo.module-link"))
194 cgit_repo->module_link= xstrdup(value); 196 cgit_repo->module_link= xstrdup(value);
195 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { 197 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
196 if (*value == '/') 198 if (*value == '/')
197 cgit_repo->readme = xstrdup(value); 199 cgit_repo->readme = xstrdup(value);
198 else 200 else
199 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); 201 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
200 } else if (!strcmp(name, "include")) 202 } else if (!strcmp(name, "include"))
201 cgit_read_config(value, cgit_global_config_cb); 203 cgit_read_config(value, cgit_global_config_cb);
202} 204}
203 205
204void cgit_querystring_cb(const char *name, const char *value) 206void cgit_querystring_cb(const char *name, const char *value)
205{ 207{
206 if (!strcmp(name,"r")) { 208 if (!strcmp(name,"r")) {
207 cgit_query_repo = xstrdup(value); 209 cgit_query_repo = xstrdup(value);
208 cgit_repo = cgit_get_repoinfo(value); 210 cgit_repo = cgit_get_repoinfo(value);
209 } else if (!strcmp(name, "p")) { 211 } else if (!strcmp(name, "p")) {
210 cgit_query_page = xstrdup(value); 212 cgit_query_page = xstrdup(value);
211 cgit_cmd = cgit_get_cmd_index(value); 213 cgit_cmd = cgit_get_cmd_index(value);
212 } else if (!strcmp(name, "url")) { 214 } else if (!strcmp(name, "url")) {
213 cgit_parse_url(value); 215 cgit_parse_url(value);
214 } else if (!strcmp(name, "q")) { 216 } else if (!strcmp(name, "q")) {
215 cgit_query_search = xstrdup(value); 217 cgit_query_search = xstrdup(value);
216 } else if (!strcmp(name, "h")) { 218 } else if (!strcmp(name, "h")) {
217 cgit_query_head = xstrdup(value); 219 cgit_query_head = xstrdup(value);
218 cgit_query_has_symref = 1; 220 cgit_query_has_symref = 1;
219 } else if (!strcmp(name, "id")) { 221 } else if (!strcmp(name, "id")) {
220 cgit_query_sha1 = xstrdup(value); 222 cgit_query_sha1 = xstrdup(value);
221 cgit_query_has_sha1 = 1; 223 cgit_query_has_sha1 = 1;
222 } else if (!strcmp(name, "id2")) { 224 } else if (!strcmp(name, "id2")) {
223 cgit_query_sha2 = xstrdup(value); 225 cgit_query_sha2 = xstrdup(value);
224 cgit_query_has_sha1 = 1; 226 cgit_query_has_sha1 = 1;
225 } else if (!strcmp(name, "ofs")) { 227 } else if (!strcmp(name, "ofs")) {
226 cgit_query_ofs = atoi(value); 228 cgit_query_ofs = atoi(value);
227 } else if (!strcmp(name, "path")) { 229 } else if (!strcmp(name, "path")) {
228 cgit_query_path = xstrdup(value); 230 cgit_query_path = xstrdup(value);
229 } else if (!strcmp(name, "name")) { 231 } else if (!strcmp(name, "name")) {
230 cgit_query_name = xstrdup(value); 232 cgit_query_name = xstrdup(value);
231 } 233 }
232} 234}
233 235
234void *cgit_free_commitinfo(struct commitinfo *info) 236void *cgit_free_commitinfo(struct commitinfo *info)
235{ 237{
236 free(info->author); 238 free(info->author);
237 free(info->author_email); 239 free(info->author_email);
238 free(info->committer); 240 free(info->committer);
239 free(info->committer_email); 241 free(info->committer_email);
240 free(info->subject); 242 free(info->subject);
241 free(info); 243 free(info);
242 return NULL; 244 return NULL;
243} 245}
244 246
245int hextoint(char c) 247int hextoint(char c)
246{ 248{
247 if (c >= 'a' && c <= 'f') 249 if (c >= 'a' && c <= 'f')
248 return 10 + c - 'a'; 250 return 10 + c - 'a';
249 else if (c >= 'A' && c <= 'F') 251 else if (c >= 'A' && c <= 'F')
250 return 10 + c - 'A'; 252 return 10 + c - 'A';
251 else if (c >= '0' && c <= '9') 253 else if (c >= '0' && c <= '9')
252 return c - '0'; 254 return c - '0';
253 else 255 else
254 return -1; 256 return -1;
255} 257}
256 258
257void cgit_diff_tree_cb(struct diff_queue_struct *q, 259void cgit_diff_tree_cb(struct diff_queue_struct *q,
258 struct diff_options *options, void *data) 260 struct diff_options *options, void *data)
259{ 261{
260 int i; 262 int i;
261 263
262 for (i = 0; i < q->nr; i++) { 264 for (i = 0; i < q->nr; i++) {
263 if (q->queue[i]->status == 'U') 265 if (q->queue[i]->status == 'U')
264 continue; 266 continue;
265 ((filepair_fn)data)(q->queue[i]); 267 ((filepair_fn)data)(q->queue[i]);
266 } 268 }
267} 269}
268 270
269static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 271static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
270{ 272{
diff --git a/ui-shared.c b/ui-shared.c
index 15d8254..110c696 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -9,323 +9,323 @@
9#include "cgit.h" 9#include "cgit.h"
10 10
11const char cgit_doctype[] = 11const char cgit_doctype[] =
12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 12"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 13" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
14 14
15static char *http_date(time_t t) 15static char *http_date(time_t t)
16{ 16{
17 static char day[][4] = 17 static char day[][4] =
18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 18 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
19 static char month[][4] = 19 static char month[][4] =
20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 20 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; 21 "Jul", "Aug", "Sep", "Oct", "Now", "Dec"};
22 struct tm *tm = gmtime(&t); 22 struct tm *tm = gmtime(&t);
23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 23 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 24 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
25 tm->tm_hour, tm->tm_min, tm->tm_sec); 25 tm->tm_hour, tm->tm_min, tm->tm_sec);
26} 26}
27 27
28static long ttl_seconds(long ttl) 28static long ttl_seconds(long ttl)
29{ 29{
30 if (ttl<0) 30 if (ttl<0)
31 return 60 * 60 * 24 * 365; 31 return 60 * 60 * 24 * 365;
32 else 32 else
33 return ttl * 60; 33 return ttl * 60;
34} 34}
35 35
36void cgit_print_error(char *msg) 36void cgit_print_error(char *msg)
37{ 37{
38 html("<div class='error'>"); 38 html("<div class='error'>");
39 html_txt(msg); 39 html_txt(msg);
40 html("</div>\n"); 40 html("</div>\n");
41} 41}
42 42
43char *cgit_rooturl() 43char *cgit_rooturl()
44{ 44{
45 if (cgit_virtual_root) 45 if (cgit_virtual_root)
46 return fmt("%s/", cgit_virtual_root); 46 return fmt("%s/", cgit_virtual_root);
47 else 47 else
48 return cgit_script_name; 48 return cgit_script_name;
49} 49}
50 50
51char *cgit_repourl(const char *reponame) 51char *cgit_repourl(const char *reponame)
52{ 52{
53 if (cgit_virtual_root) { 53 if (cgit_virtual_root) {
54 return fmt("%s/%s/", cgit_virtual_root, reponame); 54 return fmt("%s/%s/", cgit_virtual_root, reponame);
55 } else { 55 } else {
56 return fmt("?r=%s", reponame); 56 return fmt("?r=%s", reponame);
57 } 57 }
58} 58}
59 59
60char *cgit_pageurl(const char *reponame, const char *pagename, 60char *cgit_pageurl(const char *reponame, const char *pagename,
61 const char *query) 61 const char *query)
62{ 62{
63 if (cgit_virtual_root) { 63 if (cgit_virtual_root) {
64 if (query) 64 if (query)
65 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/?%s", cgit_virtual_root, reponame,
66 pagename, query); 66 pagename, query);
67 else 67 else
68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame, 68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame,
69 pagename); 69 pagename);
70 } else { 70 } else {
71 if (query) 71 if (query)
72 return fmt("?r=%s&amp;p=%s&amp;%s", reponame, pagename, query); 72 return fmt("?r=%s&amp;p=%s&amp;%s", reponame, pagename, query);
73 else 73 else
74 return fmt("?r=%s&amp;p=%s", reponame, pagename); 74 return fmt("?r=%s&amp;p=%s", reponame, pagename);
75 } 75 }
76} 76}
77 77
78char *cgit_currurl() 78char *cgit_currurl()
79{ 79{
80 if (!cgit_virtual_root) 80 if (!cgit_virtual_root)
81 return cgit_script_name; 81 return cgit_script_name;
82 else if (cgit_query_page) 82 else if (cgit_query_page)
83 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page); 83 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page);
84 else if (cgit_query_repo) 84 else if (cgit_query_repo)
85 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); 85 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
86 else 86 else
87 return fmt("%s/", cgit_virtual_root); 87 return fmt("%s/", cgit_virtual_root);
88} 88}
89 89
90static char *repolink(char *title, char *class, char *page, char *head, 90static char *repolink(char *title, char *class, char *page, char *head,
91 char *path) 91 char *path)
92{ 92{
93 char *delim = "?"; 93 char *delim = "?";
94 94
95 html("<a"); 95 html("<a");
96 if (title) { 96 if (title) {
97 html(" title='"); 97 html(" title='");
98 html_attr(title); 98 html_attr(title);
99 html("'"); 99 html("'");
100 } 100 }
101 if (class) { 101 if (class) {
102 html(" class='"); 102 html(" class='");
103 html_attr(class); 103 html_attr(class);
104 html("'"); 104 html("'");
105 } 105 }
106 html(" href='"); 106 html(" href='");
107 if (cgit_virtual_root) { 107 if (cgit_virtual_root) {
108 html_attr(cgit_virtual_root); 108 html_attr(cgit_virtual_root);
109 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/') 109 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
110 html("/"); 110 html("/");
111 html_attr(cgit_repo->url); 111 html_attr(cgit_repo->url);
112 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 112 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
113 html("/"); 113 html("/");
114 html(page); 114 html(page);
115 html("/"); 115 html("/");
116 if (path) 116 if (path)
117 html_attr(path); 117 html_attr(path);
118 } else { 118 } else {
119 html(cgit_script_name); 119 html(cgit_script_name);
120 html("?url="); 120 html("?url=");
121 html_attr(cgit_repo->url); 121 html_attr(cgit_repo->url);
122 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 122 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
123 html("/"); 123 html("/");
124 html(page); 124 html(page);
125 html("/"); 125 html("/");
126 if (path) 126 if (path)
127 html_attr(path); 127 html_attr(path);
128 delim = "&amp;"; 128 delim = "&amp;";
129 } 129 }
130 if (head && strcmp(head, cgit_repo->defbranch)) { 130 if (head && strcmp(head, cgit_repo->defbranch)) {
131 html(delim); 131 html(delim);
132 html("h="); 132 html("h=");
133 html_attr(head); 133 html_attr(head);
134 delim = "&amp;"; 134 delim = "&amp;";
135 } 135 }
136 return fmt("%s", delim); 136 return fmt("%s", delim);
137} 137}
138 138
139static void reporevlink(char *page, char *name, char *title, char *class, 139static void reporevlink(char *page, char *name, char *title, char *class,
140 char *head, char *rev, char *path) 140 char *head, char *rev, char *path)
141{ 141{
142 char *delim; 142 char *delim;
143 143
144 delim = repolink(title, class, page, head, path); 144 delim = repolink(title, class, page, head, path);
145 if (rev && strcmp(rev, cgit_query_head)) { 145 if (rev && strcmp(rev, cgit_query_head)) {
146 html(delim); 146 html(delim);
147 html("id="); 147 html("id=");
148 html_attr(rev); 148 html_attr(rev);
149 } 149 }
150 html("'>"); 150 html("'>");
151 html_txt(name); 151 html_txt(name);
152 html("</a>"); 152 html("</a>");
153} 153}
154 154
155void cgit_tree_link(char *name, char *title, char *class, char *head, 155void cgit_tree_link(char *name, char *title, char *class, char *head,
156 char *rev, char *path) 156 char *rev, char *path)
157{ 157{
158 reporevlink("tree", name, title, class, head, rev, path); 158 reporevlink("tree", name, title, class, head, rev, path);
159} 159}
160 160
161void cgit_log_link(char *name, char *title, char *class, char *head, 161void cgit_log_link(char *name, char *title, char *class, char *head,
162 char *rev, char *path) 162 char *rev, char *path)
163{ 163{
164 reporevlink("log", name, title, class, head, rev, path); 164 reporevlink("log", name, title, class, head, rev, path);
165} 165}
166 166
167void cgit_commit_link(char *name, char *title, char *class, char *head, 167void cgit_commit_link(char *name, char *title, char *class, char *head,
168 char *rev) 168 char *rev)
169{ 169{
170 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 170 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
171 name[cgit_max_msg_len] = '\0'; 171 name[cgit_max_msg_len] = '\0';
172 name[cgit_max_msg_len - 1] = '.'; 172 name[cgit_max_msg_len - 1] = '.';
173 name[cgit_max_msg_len - 2] = '.'; 173 name[cgit_max_msg_len - 2] = '.';
174 name[cgit_max_msg_len - 3] = '.'; 174 name[cgit_max_msg_len - 3] = '.';
175 } 175 }
176 reporevlink("commit", name, title, class, head, rev, NULL); 176 reporevlink("commit", name, title, class, head, rev, NULL);
177} 177}
178 178
179void cgit_diff_link(char *name, char *title, char *class, char *head, 179void cgit_diff_link(char *name, char *title, char *class, char *head,
180 char *new_rev, char *old_rev, char *path) 180 char *new_rev, char *old_rev, char *path)
181{ 181{
182 char *delim; 182 char *delim;
183 183
184 delim = repolink(title, class, "diff", head, path); 184 delim = repolink(title, class, "diff", head, path);
185 if (new_rev && strcmp(new_rev, cgit_query_head)) { 185 if (new_rev && strcmp(new_rev, cgit_query_head)) {
186 html(delim); 186 html(delim);
187 html("id="); 187 html("id=");
188 html_attr(new_rev); 188 html_attr(new_rev);
189 delim = "&amp;"; 189 delim = "&amp;";
190 } 190 }
191 if (old_rev) { 191 if (old_rev) {
192 html(delim); 192 html(delim);
193 html("id2="); 193 html("id2=");
194 html_attr(old_rev); 194 html_attr(old_rev);
195 } 195 }
196 html("'>"); 196 html("'>");
197 html_txt(name); 197 html_txt(name);
198 html("</a>"); 198 html("</a>");
199} 199}
200 200
201void cgit_print_date(time_t secs, char *format) 201void cgit_print_date(time_t secs, char *format)
202{ 202{
203 char buf[64]; 203 char buf[64];
204 struct tm *time; 204 struct tm *time;
205 205
206 time = gmtime(&secs); 206 time = gmtime(&secs);
207 strftime(buf, sizeof(buf)-1, format, time); 207 strftime(buf, sizeof(buf)-1, format, time);
208 html_txt(buf); 208 html_txt(buf);
209} 209}
210 210
211void cgit_print_age(time_t t, time_t max_relative, char *format) 211void cgit_print_age(time_t t, time_t max_relative, char *format)
212{ 212{
213 time_t now, secs; 213 time_t now, secs;
214 214
215 time(&now); 215 time(&now);
216 secs = now - t; 216 secs = now - t;
217 217
218 if (secs > max_relative && max_relative >= 0) { 218 if (secs > max_relative && max_relative >= 0) {
219 cgit_print_date(t, format); 219 cgit_print_date(t, format);
220 return; 220 return;
221 } 221 }
222 222
223 if (secs < TM_HOUR * 2) { 223 if (secs < TM_HOUR * 2) {
224 htmlf("<span class='age-mins'>%.0f min.</span>", 224 htmlf("<span class='age-mins'>%.0f min.</span>",
225 secs * 1.0 / TM_MIN); 225 secs * 1.0 / TM_MIN);
226 return; 226 return;
227 } 227 }
228 if (secs < TM_DAY * 2) { 228 if (secs < TM_DAY * 2) {
229 htmlf("<span class='age-hours'>%.0f hours</span>", 229 htmlf("<span class='age-hours'>%.0f hours</span>",
230 secs * 1.0 / TM_HOUR); 230 secs * 1.0 / TM_HOUR);
231 return; 231 return;
232 } 232 }
233 if (secs < TM_WEEK * 2) { 233 if (secs < TM_WEEK * 2) {
234 htmlf("<span class='age-days'>%.0f days</span>", 234 htmlf("<span class='age-days'>%.0f days</span>",
235 secs * 1.0 / TM_DAY); 235 secs * 1.0 / TM_DAY);
236 return; 236 return;
237 } 237 }
238 if (secs < TM_MONTH * 2) { 238 if (secs < TM_MONTH * 2) {
239 htmlf("<span class='age-weeks'>%.0f weeks</span>", 239 htmlf("<span class='age-weeks'>%.0f weeks</span>",
240 secs * 1.0 / TM_WEEK); 240 secs * 1.0 / TM_WEEK);
241 return; 241 return;
242 } 242 }
243 if (secs < TM_YEAR * 2) { 243 if (secs < TM_YEAR * 2) {
244 htmlf("<span class='age-months'>%.0f months</span>", 244 htmlf("<span class='age-months'>%.0f months</span>",
245 secs * 1.0 / TM_MONTH); 245 secs * 1.0 / TM_MONTH);
246 return; 246 return;
247 } 247 }
248 htmlf("<span class='age-years'>%.0f years</span>", 248 htmlf("<span class='age-years'>%.0f years</span>",
249 secs * 1.0 / TM_YEAR); 249 secs * 1.0 / TM_YEAR);
250} 250}
251 251
252void cgit_print_docstart(char *title, struct cacheitem *item) 252void cgit_print_docstart(char *title, struct cacheitem *item)
253{ 253{
254 html("Content-Type: text/html; charset=utf-8\n"); 254 html("Content-Type: text/html; charset=utf-8\n");
255 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 255 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
256 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 256 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
257 ttl_seconds(item->ttl))); 257 ttl_seconds(item->ttl)));
258 html("\n"); 258 html("\n");
259 html(cgit_doctype); 259 html(cgit_doctype);
260 html("<html>\n"); 260 html("<html>\n");
261 html("<head>\n"); 261 html("<head>\n");
262 html("<title>"); 262 html("<title>");
263 html_txt(title); 263 html_txt(title);
264 html("</title>\n"); 264 html("</title>\n");
265 htmlf("<meta name='generator' content='cgit v%s'/>\n", cgit_version); 265 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
266 html("<link rel='stylesheet' type='text/css' href='"); 266 html("<link rel='stylesheet' type='text/css' href='");
267 html_attr(cgit_css); 267 html_attr(cgit_css);
268 html("'/>\n"); 268 html("'/>\n");
269 html("</head>\n"); 269 html("</head>\n");
270 html("<body>\n"); 270 html("<body>\n");
271} 271}
272 272
273void cgit_print_docend() 273void cgit_print_docend()
274{ 274{
275 html("</td></tr></table>"); 275 html("</td></tr></table>");
276 html("</body>\n</html>\n"); 276 html("</body>\n</html>\n");
277} 277}
278 278
279void cgit_print_pageheader(char *title, int show_search) 279void cgit_print_pageheader(char *title, int show_search)
280{ 280{
281 html("<table id='layout'>"); 281 html("<table id='layout'>");
282 html("<tr><td id='header'>"); 282 html("<tr><td id='header'>");
283 html(cgit_root_title); 283 html(cgit_root_title);
284 html("</td><td id='logo'>"); 284 html("</td><td id='logo'>");
285 html("<a href='"); 285 html("<a href='");
286 html_attr(cgit_logo_link); 286 html_attr(cgit_logo_link);
287 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 287 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo);
288 html("</td></tr>"); 288 html("</td></tr>");
289 html("<tr><td id='crumb'>"); 289 html("<tr><td id='crumb'>");
290 htmlf("<a href='%s'>root</a>", cgit_rooturl()); 290 htmlf("<a href='%s'>root</a>", cgit_rooturl());
291 if (cgit_query_repo) { 291 if (cgit_query_repo) {
292 htmlf(" : <a href='%s'>", cgit_repourl(cgit_repo->url)); 292 htmlf(" : <a href='%s'>", cgit_repourl(cgit_repo->url));
293 html_txt(cgit_repo->name); 293 html_txt(cgit_repo->name);
294 htmlf("</a> : %s", title); 294 htmlf("</a> : %s", title);
295 } 295 }
296 html("</td>"); 296 html("</td>");
297 html("<td id='search'>"); 297 html("<td id='search'>");
298 if (show_search) { 298 if (show_search) {
299 html("<form method='get' action='"); 299 html("<form method='get' action='");
300 html_attr(cgit_currurl()); 300 html_attr(cgit_currurl());
301 html("'>"); 301 html("'>");
302 if (!cgit_virtual_root) { 302 if (!cgit_virtual_root) {
303 if (cgit_query_repo) 303 if (cgit_query_repo)
304 html_hidden("r", cgit_query_repo); 304 html_hidden("r", cgit_query_repo);
305 if (cgit_query_page) 305 if (cgit_query_page)
306 html_hidden("p", cgit_query_page); 306 html_hidden("p", cgit_query_page);
307 } 307 }
308 if (cgit_query_head) 308 if (cgit_query_head)
309 html_hidden("h", cgit_query_head); 309 html_hidden("h", cgit_query_head);
310 if (cgit_query_sha1) 310 if (cgit_query_sha1)
311 html_hidden("id", cgit_query_sha1); 311 html_hidden("id", cgit_query_sha1);
312 if (cgit_query_sha2) 312 if (cgit_query_sha2)
313 html_hidden("id2", cgit_query_sha2); 313 html_hidden("id2", cgit_query_sha2);
314 html("<input type='text' name='q' value='"); 314 html("<input type='text' name='q' value='");
315 html_attr(cgit_query_search); 315 html_attr(cgit_query_search);
316 html("'/></form>"); 316 html("'/></form>");
317 } 317 }
318 html("</td></tr>"); 318 html("</td></tr>");
319 html("<tr><td id='content' colspan='2'>"); 319 html("<tr><td id='content' colspan='2'>");
320} 320}
321 321
322void cgit_print_snapshot_start(const char *mimetype, const char *filename, 322void cgit_print_snapshot_start(const char *mimetype, const char *filename,
323 struct cacheitem *item) 323 struct cacheitem *item)
324{ 324{
325 htmlf("Content-Type: %s\n", mimetype); 325 htmlf("Content-Type: %s\n", mimetype);
326 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 326 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
327 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 327 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
328 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 328 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
329 ttl_seconds(item->ttl))); 329 ttl_seconds(item->ttl)));
330 html("\n"); 330 html("\n");
331} 331}