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,141 +1,138 @@
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);
diff --git a/cgit.h b/cgit.h
index bd2dd0d..9558362 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,229 +1,229 @@
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);
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,142 +1,144 @@
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);
diff --git a/ui-shared.c b/ui-shared.c
index 15d8254..110c696 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -137,195 +137,195 @@ static char *repolink(char *title, char *class, char *page, char *head,
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}