summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2011-03-05 13:01:59 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2011-03-05 13:01:59 (UTC)
commit1b09cbd303d889ec2636127584d57b7f1b70c25e (patch) (unidiff)
tree2b1188f946451d06af4d9120cbc3ba34de716f21
parent979c460e7f71d153ae79da67b8b21c3412f0fe02 (diff)
parent9e849950dc7c1f2fb6ffa62ab65bd30f35717d13 (diff)
downloadcgit-1b09cbd303d889ec2636127584d57b7f1b70c25e.zip
cgit-1b09cbd303d889ec2636127584d57b7f1b70c25e.tar.gz
cgit-1b09cbd303d889ec2636127584d57b7f1b70c25e.tar.bz2
Merge branch 'stable'
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile4
-rw-r--r--cgit.c5
-rw-r--r--html.c2
-rw-r--r--ui-shared.c2
4 files changed, 7 insertions, 6 deletions
diff --git a/Makefile b/Makefile
index a988751..14b4df4 100644
--- a/Makefile
+++ b/Makefile
@@ -1,244 +1,244 @@
1CGIT_VERSION = v0.8.3.4 1CGIT_VERSION = v0.8.3.5
2CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit 3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH) 4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
5CGIT_CONFIG = /etc/cgitrc 5CGIT_CONFIG = /etc/cgitrc
6CACHE_ROOT = /var/cache/cgit 6CACHE_ROOT = /var/cache/cgit
7prefix = /usr 7prefix = /usr
8libdir = $(prefix)/lib 8libdir = $(prefix)/lib
9filterdir = $(libdir)/cgit/filters 9filterdir = $(libdir)/cgit/filters
10docdir = $(prefix)/share/doc/cgit 10docdir = $(prefix)/share/doc/cgit
11htmldir = $(docdir) 11htmldir = $(docdir)
12pdfdir = $(docdir) 12pdfdir = $(docdir)
13mandir = $(prefix)/share/man 13mandir = $(prefix)/share/man
14SHA1_HEADER = <openssl/sha.h> 14SHA1_HEADER = <openssl/sha.h>
15GIT_VER = 1.7.4 15GIT_VER = 1.7.4
16GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 16GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
17INSTALL = install 17INSTALL = install
18MAN5_TXT = $(wildcard *.5.txt) 18MAN5_TXT = $(wildcard *.5.txt)
19MAN_TXT = $(MAN5_TXT) 19MAN_TXT = $(MAN5_TXT)
20DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT)) 20DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT))
21DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT)) 21DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
22DOC_PDF = $(patsubst %.txt,%.pdf,$(MAN_TXT)) 22DOC_PDF = $(patsubst %.txt,%.pdf,$(MAN_TXT))
23 23
24# Define NO_STRCASESTR if you don't have strcasestr. 24# Define NO_STRCASESTR if you don't have strcasestr.
25# 25#
26# Define NO_OPENSSL to disable linking with OpenSSL and use bundled SHA1 26# Define NO_OPENSSL to disable linking with OpenSSL and use bundled SHA1
27# implementation (slower). 27# implementation (slower).
28# 28#
29# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin). 29# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin).
30# 30#
31# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.) 31# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
32# do not support the 'size specifiers' introduced by C99, namely ll, hh, 32# do not support the 'size specifiers' introduced by C99, namely ll, hh,
33# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t). 33# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
34# some C compilers supported these specifiers prior to C99 as an extension. 34# some C compilers supported these specifiers prior to C99 as an extension.
35# 35#
36 36
37#-include config.mak 37#-include config.mak
38 38
39# 39#
40# Platform specific tweaks 40# Platform specific tweaks
41# 41#
42 42
43uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') 43uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
44uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not') 44uname_O := $(shell sh -c 'uname -o 2>/dev/null || echo not')
45uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not') 45uname_R := $(shell sh -c 'uname -r 2>/dev/null || echo not')
46 46
47ifeq ($(uname_O),Cygwin) 47ifeq ($(uname_O),Cygwin)
48 NO_STRCASESTR = YesPlease 48 NO_STRCASESTR = YesPlease
49 NEEDS_LIBICONV = YesPlease 49 NEEDS_LIBICONV = YesPlease
50endif 50endif
51 51
52# 52#
53# Let the user override the above settings. 53# Let the user override the above settings.
54# 54#
55-include cgit.conf 55-include cgit.conf
56 56
57# 57#
58# Define a way to invoke make in subdirs quietly, shamelessly ripped 58# Define a way to invoke make in subdirs quietly, shamelessly ripped
59# from git.git 59# from git.git
60# 60#
61QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir 61QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
62QUIET_SUBDIR1 = 62QUIET_SUBDIR1 =
63 63
64ifneq ($(findstring $(MAKEFLAGS),w),w) 64ifneq ($(findstring $(MAKEFLAGS),w),w)
65PRINT_DIR = --no-print-directory 65PRINT_DIR = --no-print-directory
66else # "make -w" 66else # "make -w"
67NO_SUBDIR = : 67NO_SUBDIR = :
68endif 68endif
69 69
70ifndef V 70ifndef V
71 QUIET_CC = @echo ' ' CC $@; 71 QUIET_CC = @echo ' ' CC $@;
72 QUIET_MM = @echo ' ' MM $@; 72 QUIET_MM = @echo ' ' MM $@;
73 QUIET_SUBDIR0 = +@subdir= 73 QUIET_SUBDIR0 = +@subdir=
74 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ 74 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
75 $(MAKE) $(PRINT_DIR) -C $$subdir 75 $(MAKE) $(PRINT_DIR) -C $$subdir
76endif 76endif
77 77
78# 78#
79# Define a pattern rule for automatic dependency building 79# Define a pattern rule for automatic dependency building
80# 80#
81%.d: %.c 81%.d: %.c
82 $(QUIET_MM)$(CC) $(CFLAGS) -MM -MP $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@ 82 $(QUIET_MM)$(CC) $(CFLAGS) -MM -MP $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@
83 83
84# 84#
85# Define a pattern rule for silent object building 85# Define a pattern rule for silent object building
86# 86#
87%.o: %.c 87%.o: %.c
88 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $< 88 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
89 89
90 90
91EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lpthread 91EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lpthread
92OBJECTS = 92OBJECTS =
93OBJECTS += cache.o 93OBJECTS += cache.o
94OBJECTS += cgit.o 94OBJECTS += cgit.o
95OBJECTS += cmd.o 95OBJECTS += cmd.o
96OBJECTS += configfile.o 96OBJECTS += configfile.o
97OBJECTS += html.o 97OBJECTS += html.o
98OBJECTS += parsing.o 98OBJECTS += parsing.o
99OBJECTS += scan-tree.o 99OBJECTS += scan-tree.o
100OBJECTS += shared.o 100OBJECTS += shared.o
101OBJECTS += ui-atom.o 101OBJECTS += ui-atom.o
102OBJECTS += ui-blob.o 102OBJECTS += ui-blob.o
103OBJECTS += ui-clone.o 103OBJECTS += ui-clone.o
104OBJECTS += ui-commit.o 104OBJECTS += ui-commit.o
105OBJECTS += ui-diff.o 105OBJECTS += ui-diff.o
106OBJECTS += ui-log.o 106OBJECTS += ui-log.o
107OBJECTS += ui-patch.o 107OBJECTS += ui-patch.o
108OBJECTS += ui-plain.o 108OBJECTS += ui-plain.o
109OBJECTS += ui-refs.o 109OBJECTS += ui-refs.o
110OBJECTS += ui-repolist.o 110OBJECTS += ui-repolist.o
111OBJECTS += ui-shared.o 111OBJECTS += ui-shared.o
112OBJECTS += ui-snapshot.o 112OBJECTS += ui-snapshot.o
113OBJECTS += ui-ssdiff.o 113OBJECTS += ui-ssdiff.o
114OBJECTS += ui-stats.o 114OBJECTS += ui-stats.o
115OBJECTS += ui-summary.o 115OBJECTS += ui-summary.o
116OBJECTS += ui-tag.o 116OBJECTS += ui-tag.o
117OBJECTS += ui-tree.o 117OBJECTS += ui-tree.o
118OBJECTS += vector.o 118OBJECTS += vector.o
119 119
120ifdef NEEDS_LIBICONV 120ifdef NEEDS_LIBICONV
121 EXTLIBS += -liconv 121 EXTLIBS += -liconv
122endif 122endif
123 123
124 124
125.PHONY: all libgit test install uninstall clean force-version get-git \ 125.PHONY: all libgit test install uninstall clean force-version get-git \
126 doc clean-doc install-doc install-man install-html install-pdf \ 126 doc clean-doc install-doc install-man install-html install-pdf \
127 uninstall-doc uninstall-man uninstall-html uninstall-pdf 127 uninstall-doc uninstall-man uninstall-html uninstall-pdf
128 128
129all: cgit 129all: cgit
130 130
131VERSION: force-version 131VERSION: force-version
132 @./gen-version.sh "$(CGIT_VERSION)" 132 @./gen-version.sh "$(CGIT_VERSION)"
133-include VERSION 133-include VERSION
134 134
135 135
136CFLAGS += -g -Wall -Igit 136CFLAGS += -g -Wall -Igit
137CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 137CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
138CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 138CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
139CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 139CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
140CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 140CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
141CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 141CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
142 142
143GIT_OPTIONS = prefix=/usr 143GIT_OPTIONS = prefix=/usr
144 144
145ifdef NO_ICONV 145ifdef NO_ICONV
146 CFLAGS += -DNO_ICONV 146 CFLAGS += -DNO_ICONV
147endif 147endif
148ifdef NO_STRCASESTR 148ifdef NO_STRCASESTR
149 CFLAGS += -DNO_STRCASESTR 149 CFLAGS += -DNO_STRCASESTR
150endif 150endif
151ifdef NO_C99_FORMAT 151ifdef NO_C99_FORMAT
152 CFLAGS += -DNO_C99_FORMAT 152 CFLAGS += -DNO_C99_FORMAT
153endif 153endif
154ifdef NO_OPENSSL 154ifdef NO_OPENSSL
155 CFLAGS += -DNO_OPENSSL 155 CFLAGS += -DNO_OPENSSL
156 GIT_OPTIONS += NO_OPENSSL=1 156 GIT_OPTIONS += NO_OPENSSL=1
157else 157else
158 EXTLIBS += -lcrypto 158 EXTLIBS += -lcrypto
159endif 159endif
160 160
161cgit: $(OBJECTS) libgit 161cgit: $(OBJECTS) libgit
162 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS) 162 $(QUIET_CC)$(CC) $(CFLAGS) $(LDFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
163 163
164cgit.o: VERSION 164cgit.o: VERSION
165 165
166ifneq "$(MAKECMDGOALS)" "clean" 166ifneq "$(MAKECMDGOALS)" "clean"
167 -include $(OBJECTS:.o=.d) 167 -include $(OBJECTS:.o=.d)
168endif 168endif
169 169
170libgit: 170libgit:
171 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) libgit.a 171 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) libgit.a
172 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) xdiff/lib.a 172 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) xdiff/lib.a
173 173
174test: all 174test: all
175 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all 175 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
176 176
177install: all 177install: all
178 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH) 178 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_SCRIPT_PATH)
179 $(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 179 $(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
180 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH) 180 $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
181 $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css 181 $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
182 $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png 182 $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
183 $(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir) 183 $(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
184 $(INSTALL) -m 0755 filters/* $(DESTDIR)$(filterdir) 184 $(INSTALL) -m 0755 filters/* $(DESTDIR)$(filterdir)
185 185
186install-doc: install-man install-html install-pdf 186install-doc: install-man install-html install-pdf
187 187
188install-man: doc-man 188install-man: doc-man
189 $(INSTALL) -m 0755 -d $(DESTDIR)$(mandir)/man5 189 $(INSTALL) -m 0755 -d $(DESTDIR)$(mandir)/man5
190 $(INSTALL) -m 0644 $(DOC_MAN5) $(DESTDIR)$(mandir)/man5 190 $(INSTALL) -m 0644 $(DOC_MAN5) $(DESTDIR)$(mandir)/man5
191 191
192install-html: doc-html 192install-html: doc-html
193 $(INSTALL) -m 0755 -d $(DESTDIR)$(htmldir) 193 $(INSTALL) -m 0755 -d $(DESTDIR)$(htmldir)
194 $(INSTALL) -m 0644 $(DOC_HTML) $(DESTDIR)$(htmldir) 194 $(INSTALL) -m 0644 $(DOC_HTML) $(DESTDIR)$(htmldir)
195 195
196install-pdf: doc-pdf 196install-pdf: doc-pdf
197 $(INSTALL) -m 0755 -d $(DESTDIR)$(pdfdir) 197 $(INSTALL) -m 0755 -d $(DESTDIR)$(pdfdir)
198 $(INSTALL) -m 0644 $(DOC_PDF) $(DESTDIR)$(pdfdir) 198 $(INSTALL) -m 0644 $(DOC_PDF) $(DESTDIR)$(pdfdir)
199 199
200uninstall: 200uninstall:
201 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 201 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
202 rm -f $(CGIT_DATA_PATH)/cgit.css 202 rm -f $(CGIT_DATA_PATH)/cgit.css
203 rm -f $(CGIT_DATA_PATH)/cgit.png 203 rm -f $(CGIT_DATA_PATH)/cgit.png
204 204
205uninstall-doc: uninstall-man uninstall-html uninstall-pdf 205uninstall-doc: uninstall-man uninstall-html uninstall-pdf
206 206
207uninstall-man: 207uninstall-man:
208 @for i in $(DOC_MAN5); do \ 208 @for i in $(DOC_MAN5); do \
209 rm -fv $(DESTDIR)$(mandir)/man5/$$i; \ 209 rm -fv $(DESTDIR)$(mandir)/man5/$$i; \
210 done 210 done
211 211
212uninstall-html: 212uninstall-html:
213 @for i in $(DOC_HTML); do \ 213 @for i in $(DOC_HTML); do \
214 rm -fv $(DESTDIR)$(htmldir)/$$i; \ 214 rm -fv $(DESTDIR)$(htmldir)/$$i; \
215 done 215 done
216 216
217uninstall-pdf: 217uninstall-pdf:
218 @for i in $(DOC_PDF); do \ 218 @for i in $(DOC_PDF); do \
219 rm -fv $(DESTDIR)$(pdfdir)/$$i; \ 219 rm -fv $(DESTDIR)$(pdfdir)/$$i; \
220 done 220 done
221 221
222doc: doc-man doc-html doc-pdf 222doc: doc-man doc-html doc-pdf
223doc-man: doc-man5 223doc-man: doc-man5
224doc-man5: $(DOC_MAN5) 224doc-man5: $(DOC_MAN5)
225doc-html: $(DOC_HTML) 225doc-html: $(DOC_HTML)
226doc-pdf: $(DOC_PDF) 226doc-pdf: $(DOC_PDF)
227 227
228%.5 : %.5.txt 228%.5 : %.5.txt
229 a2x -f manpage $< 229 a2x -f manpage $<
230 230
231$(DOC_HTML): %.html : %.txt 231$(DOC_HTML): %.html : %.txt
232 a2x -f xhtml --stylesheet=cgit-doc.css $< 232 a2x -f xhtml --stylesheet=cgit-doc.css $<
233 233
234$(DOC_PDF): %.pdf : %.txt 234$(DOC_PDF): %.pdf : %.txt
235 a2x -f pdf cgitrc.5.txt 235 a2x -f pdf cgitrc.5.txt
236 236
237clean: clean-doc 237clean: clean-doc
238 rm -f cgit VERSION *.o *.d 238 rm -f cgit VERSION *.o *.d
239 239
240clean-doc: 240clean-doc:
241 rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo 241 rm -f cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
242 242
243get-git: 243get-git:
244 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git 244 curl $(GIT_URL) | tar -xjf - && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index 916feb4..f4dd6ef 100644
--- a/cgit.c
+++ b/cgit.c
@@ -627,165 +627,166 @@ static void process_cached_repolist(const char *path)
627 hash += hash_str(ctx.cfg.project_list); 627 hash += hash_str(ctx.cfg.project_list);
628 cached_rc = xstrdup(fmt("%s/rc-%8lx", ctx.cfg.cache_root, hash)); 628 cached_rc = xstrdup(fmt("%s/rc-%8lx", ctx.cfg.cache_root, hash));
629 629
630 if (stat(cached_rc, &st)) { 630 if (stat(cached_rc, &st)) {
631 /* Nothing is cached, we need to scan without forking. And 631 /* Nothing is cached, we need to scan without forking. And
632 * if we fail to generate a cached repolist, we need to 632 * if we fail to generate a cached repolist, we need to
633 * invoke scan_tree manually. 633 * invoke scan_tree manually.
634 */ 634 */
635 if (generate_cached_repolist(path, cached_rc)) { 635 if (generate_cached_repolist(path, cached_rc)) {
636 if (ctx.cfg.project_list) 636 if (ctx.cfg.project_list)
637 scan_projects(path, ctx.cfg.project_list, 637 scan_projects(path, ctx.cfg.project_list,
638 repo_config); 638 repo_config);
639 else 639 else
640 scan_tree(path, repo_config); 640 scan_tree(path, repo_config);
641 } 641 }
642 return; 642 return;
643 } 643 }
644 644
645 parse_configfile(cached_rc, config_cb); 645 parse_configfile(cached_rc, config_cb);
646 646
647 /* If the cached configfile hasn't expired, lets exit now */ 647 /* If the cached configfile hasn't expired, lets exit now */
648 age = time(NULL) - st.st_mtime; 648 age = time(NULL) - st.st_mtime;
649 if (age <= (ctx.cfg.cache_scanrc_ttl * 60)) 649 if (age <= (ctx.cfg.cache_scanrc_ttl * 60))
650 return; 650 return;
651 651
652 /* The cached repolist has been parsed, but it was old. So lets 652 /* The cached repolist has been parsed, but it was old. So lets
653 * rescan the specified path and generate a new cached repolist 653 * rescan the specified path and generate a new cached repolist
654 * in a child-process to avoid latency for the current request. 654 * in a child-process to avoid latency for the current request.
655 */ 655 */
656 if (fork()) 656 if (fork())
657 return; 657 return;
658 658
659 exit(generate_cached_repolist(path, cached_rc)); 659 exit(generate_cached_repolist(path, cached_rc));
660} 660}
661 661
662static void cgit_parse_args(int argc, const char **argv) 662static void cgit_parse_args(int argc, const char **argv)
663{ 663{
664 int i; 664 int i;
665 int scan = 0; 665 int scan = 0;
666 666
667 for (i = 1; i < argc; i++) { 667 for (i = 1; i < argc; i++) {
668 if (!strncmp(argv[i], "--cache=", 8)) { 668 if (!strncmp(argv[i], "--cache=", 8)) {
669 ctx.cfg.cache_root = xstrdup(argv[i]+8); 669 ctx.cfg.cache_root = xstrdup(argv[i]+8);
670 } 670 }
671 if (!strcmp(argv[i], "--nocache")) { 671 if (!strcmp(argv[i], "--nocache")) {
672 ctx.cfg.nocache = 1; 672 ctx.cfg.nocache = 1;
673 } 673 }
674 if (!strcmp(argv[i], "--nohttp")) { 674 if (!strcmp(argv[i], "--nohttp")) {
675 ctx.env.no_http = "1"; 675 ctx.env.no_http = "1";
676 } 676 }
677 if (!strncmp(argv[i], "--query=", 8)) { 677 if (!strncmp(argv[i], "--query=", 8)) {
678 ctx.qry.raw = xstrdup(argv[i]+8); 678 ctx.qry.raw = xstrdup(argv[i]+8);
679 } 679 }
680 if (!strncmp(argv[i], "--repo=", 7)) { 680 if (!strncmp(argv[i], "--repo=", 7)) {
681 ctx.qry.repo = xstrdup(argv[i]+7); 681 ctx.qry.repo = xstrdup(argv[i]+7);
682 } 682 }
683 if (!strncmp(argv[i], "--page=", 7)) { 683 if (!strncmp(argv[i], "--page=", 7)) {
684 ctx.qry.page = xstrdup(argv[i]+7); 684 ctx.qry.page = xstrdup(argv[i]+7);
685 } 685 }
686 if (!strncmp(argv[i], "--head=", 7)) { 686 if (!strncmp(argv[i], "--head=", 7)) {
687 ctx.qry.head = xstrdup(argv[i]+7); 687 ctx.qry.head = xstrdup(argv[i]+7);
688 ctx.qry.has_symref = 1; 688 ctx.qry.has_symref = 1;
689 } 689 }
690 if (!strncmp(argv[i], "--sha1=", 7)) { 690 if (!strncmp(argv[i], "--sha1=", 7)) {
691 ctx.qry.sha1 = xstrdup(argv[i]+7); 691 ctx.qry.sha1 = xstrdup(argv[i]+7);
692 ctx.qry.has_sha1 = 1; 692 ctx.qry.has_sha1 = 1;
693 } 693 }
694 if (!strncmp(argv[i], "--ofs=", 6)) { 694 if (!strncmp(argv[i], "--ofs=", 6)) {
695 ctx.qry.ofs = atoi(argv[i]+6); 695 ctx.qry.ofs = atoi(argv[i]+6);
696 } 696 }
697 if (!strncmp(argv[i], "--scan-tree=", 12) || 697 if (!strncmp(argv[i], "--scan-tree=", 12) ||
698 !strncmp(argv[i], "--scan-path=", 12)) { 698 !strncmp(argv[i], "--scan-path=", 12)) {
699 /* HACK: the global snapshot bitmask defines the 699 /* HACK: the global snapshot bitmask defines the
700 * set of allowed snapshot formats, but the config 700 * set of allowed snapshot formats, but the config
701 * file hasn't been parsed yet so the mask is 701 * file hasn't been parsed yet so the mask is
702 * currently 0. By setting all bits high before 702 * currently 0. By setting all bits high before
703 * scanning we make sure that any in-repo cgitrc 703 * scanning we make sure that any in-repo cgitrc
704 * snapshot setting is respected by scan_tree(). 704 * snapshot setting is respected by scan_tree().
705 * BTW: we assume that there'll never be more than 705 * BTW: we assume that there'll never be more than
706 * 255 different snapshot formats supported by cgit... 706 * 255 different snapshot formats supported by cgit...
707 */ 707 */
708 ctx.cfg.snapshots = 0xFF; 708 ctx.cfg.snapshots = 0xFF;
709 scan++; 709 scan++;
710 scan_tree(argv[i] + 12, repo_config); 710 scan_tree(argv[i] + 12, repo_config);
711 } 711 }
712 } 712 }
713 if (scan) { 713 if (scan) {
714 qsort(cgit_repolist.repos, cgit_repolist.count, 714 qsort(cgit_repolist.repos, cgit_repolist.count,
715 sizeof(struct cgit_repo), cmp_repos); 715 sizeof(struct cgit_repo), cmp_repos);
716 print_repolist(stdout, &cgit_repolist, 0); 716 print_repolist(stdout, &cgit_repolist, 0);
717 exit(0); 717 exit(0);
718 } 718 }
719} 719}
720 720
721static int calc_ttl() 721static int calc_ttl()
722{ 722{
723 if (!ctx.repo) 723 if (!ctx.repo)
724 return ctx.cfg.cache_root_ttl; 724 return ctx.cfg.cache_root_ttl;
725 725
726 if (!ctx.qry.page) 726 if (!ctx.qry.page)
727 return ctx.cfg.cache_repo_ttl; 727 return ctx.cfg.cache_repo_ttl;
728 728
729 if (ctx.qry.has_symref) 729 if (ctx.qry.has_symref)
730 return ctx.cfg.cache_dynamic_ttl; 730 return ctx.cfg.cache_dynamic_ttl;
731 731
732 if (ctx.qry.has_sha1) 732 if (ctx.qry.has_sha1)
733 return ctx.cfg.cache_static_ttl; 733 return ctx.cfg.cache_static_ttl;
734 734
735 return ctx.cfg.cache_repo_ttl; 735 return ctx.cfg.cache_repo_ttl;
736} 736}
737 737
738int main(int argc, const char **argv) 738int main(int argc, const char **argv)
739{ 739{
740 const char *path; 740 const char *path;
741 char *qry; 741 char *qry;
742 int err, ttl; 742 int err, ttl;
743 743
744 prepare_context(&ctx); 744 prepare_context(&ctx);
745 cgit_repolist.length = 0; 745 cgit_repolist.length = 0;
746 cgit_repolist.count = 0; 746 cgit_repolist.count = 0;
747 cgit_repolist.repos = NULL; 747 cgit_repolist.repos = NULL;
748 748
749 cgit_parse_args(argc, argv); 749 cgit_parse_args(argc, argv);
750 parse_configfile(expand_macros(ctx.env.cgit_config), config_cb); 750 parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
751 ctx.repo = NULL; 751 ctx.repo = NULL;
752 http_parse_querystring(ctx.qry.raw, querystring_cb); 752 http_parse_querystring(ctx.qry.raw, querystring_cb);
753 753
754 /* If virtual-root isn't specified in cgitrc, lets pretend 754 /* If virtual-root isn't specified in cgitrc, lets pretend
755 * that virtual-root equals SCRIPT_NAME. 755 * that virtual-root equals SCRIPT_NAME, minus any possibly
756 * trailing slashes.
756 */ 757 */
757 if (!ctx.cfg.virtual_root) 758 if (!ctx.cfg.virtual_root)
758 ctx.cfg.virtual_root = ctx.cfg.script_name; 759 ctx.cfg.virtual_root = trim_end(ctx.cfg.script_name, '/');
759 760
760 /* If no url parameter is specified on the querystring, lets 761 /* If no url parameter is specified on the querystring, lets
761 * use PATH_INFO as url. This allows cgit to work with virtual 762 * use PATH_INFO as url. This allows cgit to work with virtual
762 * urls without the need for rewriterules in the webserver (as 763 * urls without the need for rewriterules in the webserver (as
763 * long as PATH_INFO is included in the cache lookup key). 764 * long as PATH_INFO is included in the cache lookup key).
764 */ 765 */
765 path = ctx.env.path_info; 766 path = ctx.env.path_info;
766 if (!ctx.qry.url && path) { 767 if (!ctx.qry.url && path) {
767 if (path[0] == '/') 768 if (path[0] == '/')
768 path++; 769 path++;
769 ctx.qry.url = xstrdup(path); 770 ctx.qry.url = xstrdup(path);
770 if (ctx.qry.raw) { 771 if (ctx.qry.raw) {
771 qry = ctx.qry.raw; 772 qry = ctx.qry.raw;
772 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry)); 773 ctx.qry.raw = xstrdup(fmt("%s?%s", path, qry));
773 free(qry); 774 free(qry);
774 } else 775 } else
775 ctx.qry.raw = xstrdup(ctx.qry.url); 776 ctx.qry.raw = xstrdup(ctx.qry.url);
776 cgit_parse_url(ctx.qry.url); 777 cgit_parse_url(ctx.qry.url);
777 } 778 }
778 779
779 ttl = calc_ttl(); 780 ttl = calc_ttl();
780 ctx.page.expires += ttl*60; 781 ctx.page.expires += ttl*60;
781 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")) 782 if (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD"))
782 ctx.cfg.nocache = 1; 783 ctx.cfg.nocache = 1;
783 if (ctx.cfg.nocache) 784 if (ctx.cfg.nocache)
784 ctx.cfg.cache_size = 0; 785 ctx.cfg.cache_size = 0;
785 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root, 786 err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
786 ctx.qry.raw, ttl, process_request, &ctx); 787 ctx.qry.raw, ttl, process_request, &ctx);
787 if (err) 788 if (err)
788 cgit_print_error(fmt("Error processing page: %s (%d)", 789 cgit_print_error(fmt("Error processing page: %s (%d)",
789 strerror(err), err)); 790 strerror(err), err));
790 return err; 791 return err;
791} 792}
diff --git a/html.c b/html.c
index a1fe87d..a60bc13 100644
--- a/html.c
+++ b/html.c
@@ -152,169 +152,169 @@ void html_attr(const char *txt)
152 } 152 }
153 t++; 153 t++;
154 } 154 }
155 if (t!=txt) 155 if (t!=txt)
156 html(txt); 156 html(txt);
157} 157}
158 158
159void html_url_path(const char *txt) 159void html_url_path(const char *txt)
160{ 160{
161 const char *t = txt; 161 const char *t = txt;
162 while(t && *t){ 162 while(t && *t){
163 int c = *t; 163 int c = *t;
164 const char *e = url_escape_table[c]; 164 const char *e = url_escape_table[c];
165 if (e && c!='+' && c!='&' && c!='+') { 165 if (e && c!='+' && c!='&' && c!='+') {
166 html_raw(txt, t - txt); 166 html_raw(txt, t - txt);
167 html_raw(e, 3); 167 html_raw(e, 3);
168 txt = t+1; 168 txt = t+1;
169 } 169 }
170 t++; 170 t++;
171 } 171 }
172 if (t!=txt) 172 if (t!=txt)
173 html(txt); 173 html(txt);
174} 174}
175 175
176void html_url_arg(const char *txt) 176void html_url_arg(const char *txt)
177{ 177{
178 const char *t = txt; 178 const char *t = txt;
179 while(t && *t){ 179 while(t && *t){
180 int c = *t; 180 int c = *t;
181 const char *e = url_escape_table[c]; 181 const char *e = url_escape_table[c];
182 if (e) { 182 if (e) {
183 html_raw(txt, t - txt); 183 html_raw(txt, t - txt);
184 html_raw(e, strlen(e)); 184 html_raw(e, strlen(e));
185 txt = t+1; 185 txt = t+1;
186 } 186 }
187 t++; 187 t++;
188 } 188 }
189 if (t!=txt) 189 if (t!=txt)
190 html(txt); 190 html(txt);
191} 191}
192 192
193void html_hidden(const char *name, const char *value) 193void html_hidden(const char *name, const char *value)
194{ 194{
195 html("<input type='hidden' name='"); 195 html("<input type='hidden' name='");
196 html_attr(name); 196 html_attr(name);
197 html("' value='"); 197 html("' value='");
198 html_attr(value); 198 html_attr(value);
199 html("'/>"); 199 html("'/>");
200} 200}
201 201
202void html_option(const char *value, const char *text, const char *selected_value) 202void html_option(const char *value, const char *text, const char *selected_value)
203{ 203{
204 html("<option value='"); 204 html("<option value='");
205 html_attr(value); 205 html_attr(value);
206 html("'"); 206 html("'");
207 if (selected_value && !strcmp(selected_value, value)) 207 if (selected_value && !strcmp(selected_value, value))
208 html(" selected='selected'"); 208 html(" selected='selected'");
209 html(">"); 209 html(">");
210 html_txt(text); 210 html_txt(text);
211 html("</option>\n"); 211 html("</option>\n");
212} 212}
213 213
214void html_link_open(const char *url, const char *title, const char *class) 214void html_link_open(const char *url, const char *title, const char *class)
215{ 215{
216 html("<a href='"); 216 html("<a href='");
217 html_attr(url); 217 html_attr(url);
218 if (title) { 218 if (title) {
219 html("' title='"); 219 html("' title='");
220 html_attr(title); 220 html_attr(title);
221 } 221 }
222 if (class) { 222 if (class) {
223 html("' class='"); 223 html("' class='");
224 html_attr(class); 224 html_attr(class);
225 } 225 }
226 html("'>"); 226 html("'>");
227} 227}
228 228
229void html_link_close(void) 229void html_link_close(void)
230{ 230{
231 html("</a>"); 231 html("</a>");
232} 232}
233 233
234void html_fileperm(unsigned short mode) 234void html_fileperm(unsigned short mode)
235{ 235{
236 htmlf("%c%c%c", (mode & 4 ? 'r' : '-'), 236 htmlf("%c%c%c", (mode & 4 ? 'r' : '-'),
237 (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-')); 237 (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-'));
238} 238}
239 239
240int html_include(const char *filename) 240int html_include(const char *filename)
241{ 241{
242 FILE *f; 242 FILE *f;
243 char buf[4096]; 243 char buf[4096];
244 size_t len; 244 size_t len;
245 245
246 if (!(f = fopen(filename, "r"))) { 246 if (!(f = fopen(filename, "r"))) {
247 fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n", 247 fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n",
248 filename, strerror(errno), errno); 248 filename, strerror(errno), errno);
249 return -1; 249 return -1;
250 } 250 }
251 while((len = fread(buf, 1, 4096, f)) > 0) 251 while((len = fread(buf, 1, 4096, f)) > 0)
252 html_raw(buf, len); 252 html_raw(buf, len);
253 fclose(f); 253 fclose(f);
254 return 0; 254 return 0;
255} 255}
256 256
257int hextoint(char c) 257int hextoint(char c)
258{ 258{
259 if (c >= 'a' && c <= 'f') 259 if (c >= 'a' && c <= 'f')
260 return 10 + c - 'a'; 260 return 10 + c - 'a';
261 else if (c >= 'A' && c <= 'F') 261 else if (c >= 'A' && c <= 'F')
262 return 10 + c - 'A'; 262 return 10 + c - 'A';
263 else if (c >= '0' && c <= '9') 263 else if (c >= '0' && c <= '9')
264 return c - '0'; 264 return c - '0';
265 else 265 else
266 return -1; 266 return -1;
267} 267}
268 268
269char *convert_query_hexchar(char *txt) 269char *convert_query_hexchar(char *txt)
270{ 270{
271 int d1, d2, n; 271 int d1, d2, n;
272 n = strlen(txt); 272 n = strlen(txt);
273 if (n < 3) { 273 if (n < 3) {
274 *txt = '\0'; 274 *txt = '\0';
275 return txt-1; 275 return txt-1;
276 } 276 }
277 d1 = hextoint(*(txt+1)); 277 d1 = hextoint(*(txt+1));
278 d2 = hextoint(*(txt+2)); 278 d2 = hextoint(*(txt+2));
279 if (d1<0 || d2<0) { 279 if (d1<0 || d2<0) {
280 memmove(txt, txt+3, n-3); 280 memmove(txt, txt+3, n-2);
281 return txt-1; 281 return txt-1;
282 } else { 282 } else {
283 *txt = d1 * 16 + d2; 283 *txt = d1 * 16 + d2;
284 memmove(txt+1, txt+3, n-2); 284 memmove(txt+1, txt+3, n-2);
285 return txt; 285 return txt;
286 } 286 }
287} 287}
288 288
289int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value)) 289int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value))
290{ 290{
291 char *t, *txt, *value = NULL, c; 291 char *t, *txt, *value = NULL, c;
292 292
293 if (!txt_) 293 if (!txt_)
294 return 0; 294 return 0;
295 295
296 t = txt = strdup(txt_); 296 t = txt = strdup(txt_);
297 if (t == NULL) { 297 if (t == NULL) {
298 printf("Out of memory\n"); 298 printf("Out of memory\n");
299 exit(1); 299 exit(1);
300 } 300 }
301 while((c=*t) != '\0') { 301 while((c=*t) != '\0') {
302 if (c=='=') { 302 if (c=='=') {
303 *t = '\0'; 303 *t = '\0';
304 value = t+1; 304 value = t+1;
305 } else if (c=='+') { 305 } else if (c=='+') {
306 *t = ' '; 306 *t = ' ';
307 } else if (c=='%') { 307 } else if (c=='%') {
308 t = convert_query_hexchar(t); 308 t = convert_query_hexchar(t);
309 } else if (c=='&') { 309 } else if (c=='&') {
310 *t = '\0'; 310 *t = '\0';
311 (*fn)(txt, value); 311 (*fn)(txt, value);
312 txt = t+1; 312 txt = t+1;
313 value = NULL; 313 value = NULL;
314 } 314 }
315 t++; 315 t++;
316 } 316 }
317 if (t!=txt) 317 if (t!=txt)
318 (*fn)(txt, value); 318 (*fn)(txt, value);
319 return 0; 319 return 0;
320} 320}
diff --git a/ui-shared.c b/ui-shared.c
index 7efae7a..5aa9119 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -449,257 +449,257 @@ void cgit_self_link(char *name, const char *title, const char *class,
449 ctx->qry.path, ctx->qry.ofs, 449 ctx->qry.path, ctx->qry.ofs,
450 ctx->qry.grep, ctx->qry.search, 450 ctx->qry.grep, ctx->qry.search,
451 ctx->qry.showmsg); 451 ctx->qry.showmsg);
452 else if (!strcmp(ctx->qry.page, "commit")) 452 else if (!strcmp(ctx->qry.page, "commit"))
453 return cgit_commit_link(name, title, class, ctx->qry.head, 453 return cgit_commit_link(name, title, class, ctx->qry.head,
454 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 454 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
455 ctx->qry.path, 0); 455 ctx->qry.path, 0);
456 else if (!strcmp(ctx->qry.page, "patch")) 456 else if (!strcmp(ctx->qry.page, "patch"))
457 return cgit_patch_link(name, title, class, ctx->qry.head, 457 return cgit_patch_link(name, title, class, ctx->qry.head,
458 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 458 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
459 ctx->qry.path); 459 ctx->qry.path);
460 else if (!strcmp(ctx->qry.page, "refs")) 460 else if (!strcmp(ctx->qry.page, "refs"))
461 return cgit_refs_link(name, title, class, ctx->qry.head, 461 return cgit_refs_link(name, title, class, ctx->qry.head,
462 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 462 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
463 ctx->qry.path); 463 ctx->qry.path);
464 else if (!strcmp(ctx->qry.page, "snapshot")) 464 else if (!strcmp(ctx->qry.page, "snapshot"))
465 return cgit_snapshot_link(name, title, class, ctx->qry.head, 465 return cgit_snapshot_link(name, title, class, ctx->qry.head,
466 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 466 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
467 ctx->qry.path); 467 ctx->qry.path);
468 else if (!strcmp(ctx->qry.page, "diff")) 468 else if (!strcmp(ctx->qry.page, "diff"))
469 return cgit_diff_link(name, title, class, ctx->qry.head, 469 return cgit_diff_link(name, title, class, ctx->qry.head,
470 ctx->qry.sha1, ctx->qry.sha2, 470 ctx->qry.sha1, ctx->qry.sha2,
471 ctx->qry.path, 0); 471 ctx->qry.path, 0);
472 else if (!strcmp(ctx->qry.page, "stats")) 472 else if (!strcmp(ctx->qry.page, "stats"))
473 return cgit_stats_link(name, title, class, ctx->qry.head, 473 return cgit_stats_link(name, title, class, ctx->qry.head,
474 ctx->qry.path); 474 ctx->qry.path);
475 475
476 /* Don't known how to make link for this page */ 476 /* Don't known how to make link for this page */
477 repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path); 477 repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path);
478 html("><!-- cgit_self_link() doesn't know how to make link for page '"); 478 html("><!-- cgit_self_link() doesn't know how to make link for page '");
479 html_txt(ctx->qry.page); 479 html_txt(ctx->qry.page);
480 html("' -->"); 480 html("' -->");
481 html_txt(name); 481 html_txt(name);
482 html("</a>"); 482 html("</a>");
483} 483}
484 484
485void cgit_object_link(struct object *obj) 485void cgit_object_link(struct object *obj)
486{ 486{
487 char *page, *shortrev, *fullrev, *name; 487 char *page, *shortrev, *fullrev, *name;
488 488
489 fullrev = sha1_to_hex(obj->sha1); 489 fullrev = sha1_to_hex(obj->sha1);
490 shortrev = xstrdup(fullrev); 490 shortrev = xstrdup(fullrev);
491 shortrev[10] = '\0'; 491 shortrev[10] = '\0';
492 if (obj->type == OBJ_COMMIT) { 492 if (obj->type == OBJ_COMMIT) {
493 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL, 493 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
494 ctx.qry.head, fullrev, NULL, 0); 494 ctx.qry.head, fullrev, NULL, 0);
495 return; 495 return;
496 } else if (obj->type == OBJ_TREE) 496 } else if (obj->type == OBJ_TREE)
497 page = "tree"; 497 page = "tree";
498 else if (obj->type == OBJ_TAG) 498 else if (obj->type == OBJ_TAG)
499 page = "tag"; 499 page = "tag";
500 else 500 else
501 page = "blob"; 501 page = "blob";
502 name = fmt("%s %s...", typename(obj->type), shortrev); 502 name = fmt("%s %s...", typename(obj->type), shortrev);
503 reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL); 503 reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
504} 504}
505 505
506void cgit_print_date(time_t secs, const char *format, int local_time) 506void cgit_print_date(time_t secs, const char *format, int local_time)
507{ 507{
508 char buf[64]; 508 char buf[64];
509 struct tm *time; 509 struct tm *time;
510 510
511 if (!secs) 511 if (!secs)
512 return; 512 return;
513 if(local_time) 513 if(local_time)
514 time = localtime(&secs); 514 time = localtime(&secs);
515 else 515 else
516 time = gmtime(&secs); 516 time = gmtime(&secs);
517 strftime(buf, sizeof(buf)-1, format, time); 517 strftime(buf, sizeof(buf)-1, format, time);
518 html_txt(buf); 518 html_txt(buf);
519} 519}
520 520
521void cgit_print_age(time_t t, time_t max_relative, const char *format) 521void cgit_print_age(time_t t, time_t max_relative, const char *format)
522{ 522{
523 time_t now, secs; 523 time_t now, secs;
524 524
525 if (!t) 525 if (!t)
526 return; 526 return;
527 time(&now); 527 time(&now);
528 secs = now - t; 528 secs = now - t;
529 529
530 if (secs > max_relative && max_relative >= 0) { 530 if (secs > max_relative && max_relative >= 0) {
531 cgit_print_date(t, format, ctx.cfg.local_time); 531 cgit_print_date(t, format, ctx.cfg.local_time);
532 return; 532 return;
533 } 533 }
534 534
535 if (secs < TM_HOUR * 2) { 535 if (secs < TM_HOUR * 2) {
536 htmlf("<span class='age-mins'>%.0f min.</span>", 536 htmlf("<span class='age-mins'>%.0f min.</span>",
537 secs * 1.0 / TM_MIN); 537 secs * 1.0 / TM_MIN);
538 return; 538 return;
539 } 539 }
540 if (secs < TM_DAY * 2) { 540 if (secs < TM_DAY * 2) {
541 htmlf("<span class='age-hours'>%.0f hours</span>", 541 htmlf("<span class='age-hours'>%.0f hours</span>",
542 secs * 1.0 / TM_HOUR); 542 secs * 1.0 / TM_HOUR);
543 return; 543 return;
544 } 544 }
545 if (secs < TM_WEEK * 2) { 545 if (secs < TM_WEEK * 2) {
546 htmlf("<span class='age-days'>%.0f days</span>", 546 htmlf("<span class='age-days'>%.0f days</span>",
547 secs * 1.0 / TM_DAY); 547 secs * 1.0 / TM_DAY);
548 return; 548 return;
549 } 549 }
550 if (secs < TM_MONTH * 2) { 550 if (secs < TM_MONTH * 2) {
551 htmlf("<span class='age-weeks'>%.0f weeks</span>", 551 htmlf("<span class='age-weeks'>%.0f weeks</span>",
552 secs * 1.0 / TM_WEEK); 552 secs * 1.0 / TM_WEEK);
553 return; 553 return;
554 } 554 }
555 if (secs < TM_YEAR * 2) { 555 if (secs < TM_YEAR * 2) {
556 htmlf("<span class='age-months'>%.0f months</span>", 556 htmlf("<span class='age-months'>%.0f months</span>",
557 secs * 1.0 / TM_MONTH); 557 secs * 1.0 / TM_MONTH);
558 return; 558 return;
559 } 559 }
560 htmlf("<span class='age-years'>%.0f years</span>", 560 htmlf("<span class='age-years'>%.0f years</span>",
561 secs * 1.0 / TM_YEAR); 561 secs * 1.0 / TM_YEAR);
562} 562}
563 563
564void cgit_print_http_headers(struct cgit_context *ctx) 564void cgit_print_http_headers(struct cgit_context *ctx)
565{ 565{
566 if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1")) 566 if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1"))
567 return; 567 return;
568 568
569 if (ctx->page.status) 569 if (ctx->page.status)
570 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg); 570 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg);
571 if (ctx->page.mimetype && ctx->page.charset) 571 if (ctx->page.mimetype && ctx->page.charset)
572 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 572 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
573 ctx->page.charset); 573 ctx->page.charset);
574 else if (ctx->page.mimetype) 574 else if (ctx->page.mimetype)
575 htmlf("Content-Type: %s\n", ctx->page.mimetype); 575 htmlf("Content-Type: %s\n", ctx->page.mimetype);
576 if (ctx->page.size) 576 if (ctx->page.size)
577 htmlf("Content-Length: %ld\n", ctx->page.size); 577 htmlf("Content-Length: %zd\n", ctx->page.size);
578 if (ctx->page.filename) 578 if (ctx->page.filename)
579 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 579 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
580 ctx->page.filename); 580 ctx->page.filename);
581 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 581 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
582 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 582 htmlf("Expires: %s\n", http_date(ctx->page.expires));
583 if (ctx->page.etag) 583 if (ctx->page.etag)
584 htmlf("ETag: \"%s\"\n", ctx->page.etag); 584 htmlf("ETag: \"%s\"\n", ctx->page.etag);
585 html("\n"); 585 html("\n");
586 if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD")) 586 if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD"))
587 exit(0); 587 exit(0);
588} 588}
589 589
590void cgit_print_docstart(struct cgit_context *ctx) 590void cgit_print_docstart(struct cgit_context *ctx)
591{ 591{
592 if (ctx->cfg.embedded) { 592 if (ctx->cfg.embedded) {
593 if (ctx->cfg.header) 593 if (ctx->cfg.header)
594 html_include(ctx->cfg.header); 594 html_include(ctx->cfg.header);
595 return; 595 return;
596 } 596 }
597 597
598 char *host = cgit_hosturl(); 598 char *host = cgit_hosturl();
599 html(cgit_doctype); 599 html(cgit_doctype);
600 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 600 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
601 html("<head>\n"); 601 html("<head>\n");
602 html("<title>"); 602 html("<title>");
603 html_txt(ctx->page.title); 603 html_txt(ctx->page.title);
604 html("</title>\n"); 604 html("</title>\n");
605 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 605 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
606 if (ctx->cfg.robots && *ctx->cfg.robots) 606 if (ctx->cfg.robots && *ctx->cfg.robots)
607 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 607 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
608 html("<link rel='stylesheet' type='text/css' href='"); 608 html("<link rel='stylesheet' type='text/css' href='");
609 html_attr(ctx->cfg.css); 609 html_attr(ctx->cfg.css);
610 html("'/>\n"); 610 html("'/>\n");
611 if (ctx->cfg.favicon) { 611 if (ctx->cfg.favicon) {
612 html("<link rel='shortcut icon' href='"); 612 html("<link rel='shortcut icon' href='");
613 html_attr(ctx->cfg.favicon); 613 html_attr(ctx->cfg.favicon);
614 html("'/>\n"); 614 html("'/>\n");
615 } 615 }
616 if (host && ctx->repo) { 616 if (host && ctx->repo) {
617 html("<link rel='alternate' title='Atom feed' href='"); 617 html("<link rel='alternate' title='Atom feed' href='");
618 html(cgit_httpscheme()); 618 html(cgit_httpscheme());
619 html_attr(cgit_hosturl()); 619 html_attr(cgit_hosturl());
620 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath, 620 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath,
621 fmt("h=%s", ctx->qry.head))); 621 fmt("h=%s", ctx->qry.head)));
622 html("' type='application/atom+xml'/>\n"); 622 html("' type='application/atom+xml'/>\n");
623 } 623 }
624 if (ctx->cfg.head_include) 624 if (ctx->cfg.head_include)
625 html_include(ctx->cfg.head_include); 625 html_include(ctx->cfg.head_include);
626 html("</head>\n"); 626 html("</head>\n");
627 html("<body>\n"); 627 html("<body>\n");
628 if (ctx->cfg.header) 628 if (ctx->cfg.header)
629 html_include(ctx->cfg.header); 629 html_include(ctx->cfg.header);
630} 630}
631 631
632void cgit_print_docend() 632void cgit_print_docend()
633{ 633{
634 html("</div> <!-- class=content -->\n"); 634 html("</div> <!-- class=content -->\n");
635 if (ctx.cfg.embedded) { 635 if (ctx.cfg.embedded) {
636 html("</div> <!-- id=cgit -->\n"); 636 html("</div> <!-- id=cgit -->\n");
637 if (ctx.cfg.footer) 637 if (ctx.cfg.footer)
638 html_include(ctx.cfg.footer); 638 html_include(ctx.cfg.footer);
639 return; 639 return;
640 } 640 }
641 if (ctx.cfg.footer) 641 if (ctx.cfg.footer)
642 html_include(ctx.cfg.footer); 642 html_include(ctx.cfg.footer);
643 else { 643 else {
644 htmlf("<div class='footer'>generated by cgit %s at ", 644 htmlf("<div class='footer'>generated by cgit %s at ",
645 cgit_version); 645 cgit_version);
646 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 646 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
647 html("</div>\n"); 647 html("</div>\n");
648 } 648 }
649 html("</div> <!-- id=cgit -->\n"); 649 html("</div> <!-- id=cgit -->\n");
650 html("</body>\n</html>\n"); 650 html("</body>\n</html>\n");
651} 651}
652 652
653int print_branch_option(const char *refname, const unsigned char *sha1, 653int print_branch_option(const char *refname, const unsigned char *sha1,
654 int flags, void *cb_data) 654 int flags, void *cb_data)
655{ 655{
656 char *name = (char *)refname; 656 char *name = (char *)refname;
657 html_option(name, name, ctx.qry.head); 657 html_option(name, name, ctx.qry.head);
658 return 0; 658 return 0;
659} 659}
660 660
661int print_archive_ref(const char *refname, const unsigned char *sha1, 661int print_archive_ref(const char *refname, const unsigned char *sha1,
662 int flags, void *cb_data) 662 int flags, void *cb_data)
663{ 663{
664 struct tag *tag; 664 struct tag *tag;
665 struct taginfo *info; 665 struct taginfo *info;
666 struct object *obj; 666 struct object *obj;
667 char buf[256], *url; 667 char buf[256], *url;
668 unsigned char fileid[20]; 668 unsigned char fileid[20];
669 int *header = (int *)cb_data; 669 int *header = (int *)cb_data;
670 670
671 if (prefixcmp(refname, "refs/archives")) 671 if (prefixcmp(refname, "refs/archives"))
672 return 0; 672 return 0;
673 strncpy(buf, refname+14, sizeof(buf)); 673 strncpy(buf, refname+14, sizeof(buf));
674 obj = parse_object(sha1); 674 obj = parse_object(sha1);
675 if (!obj) 675 if (!obj)
676 return 1; 676 return 1;
677 if (obj->type == OBJ_TAG) { 677 if (obj->type == OBJ_TAG) {
678 tag = lookup_tag(sha1); 678 tag = lookup_tag(sha1);
679 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 679 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
680 return 0; 680 return 0;
681 hashcpy(fileid, tag->tagged->sha1); 681 hashcpy(fileid, tag->tagged->sha1);
682 } else if (obj->type != OBJ_BLOB) { 682 } else if (obj->type != OBJ_BLOB) {
683 return 0; 683 return 0;
684 } else { 684 } else {
685 hashcpy(fileid, sha1); 685 hashcpy(fileid, sha1);
686 } 686 }
687 if (!*header) { 687 if (!*header) {
688 html("<h1>download</h1>\n"); 688 html("<h1>download</h1>\n");
689 *header = 1; 689 *header = 1;
690 } 690 }
691 url = cgit_pageurl(ctx.qry.repo, "blob", 691 url = cgit_pageurl(ctx.qry.repo, "blob",
692 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 692 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
693 buf)); 693 buf));
694 html_link_open(url, NULL, "menu"); 694 html_link_open(url, NULL, "menu");
695 html_txt(strlpart(buf, 20)); 695 html_txt(strlpart(buf, 20));
696 html_link_close(); 696 html_link_close();
697 return 0; 697 return 0;
698} 698}
699 699
700void cgit_add_hidden_formfields(int incl_head, int incl_search, 700void cgit_add_hidden_formfields(int incl_head, int incl_search,
701 const char *page) 701 const char *page)
702{ 702{
703 char *url; 703 char *url;
704 704
705 if (!ctx.cfg.virtual_root) { 705 if (!ctx.cfg.virtual_root) {