summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile5
-rw-r--r--cgit.css11
-rw-r--r--cgit.h7
-rw-r--r--parsing.c25
-rw-r--r--shared.c2
-rw-r--r--ui-log.c21
-rw-r--r--ui-shared.c2
7 files changed, 55 insertions, 18 deletions
diff --git a/Makefile b/Makefile
index 27e966d..685e662 100644
--- a/Makefile
+++ b/Makefile
@@ -1,69 +1,74 @@
1CGIT_VERSION = v0.7.1 1CGIT_VERSION = v0.7.1
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_CONFIG = /etc/cgitrc 4CGIT_CONFIG = /etc/cgitrc
5CACHE_ROOT = /var/cache/cgit 5CACHE_ROOT = /var/cache/cgit
6SHA1_HEADER = <openssl/sha.h> 6SHA1_HEADER = <openssl/sha.h>
7GIT_VER = 1.5.3.5 7GIT_VER = 1.5.3.5
8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 8GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
9 9
10# 10#
11# Let the user override the above settings. 11# Let the user override the above settings.
12# 12#
13-include cgit.conf 13-include cgit.conf
14 14
15 15
16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 16EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \ 17OBJECTS = shared.o cache.o parsing.o html.o ui-shared.o ui-repolist.o \
18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \ 18 ui-summary.o ui-log.o ui-tree.o ui-commit.o ui-diff.o \
19 ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o 19 ui-snapshot.o ui-blob.o ui-tag.o ui-refs.o
20 20
21 21
22ifdef NEEDS_LIBICONV
23 EXTLIBS += -liconv
24endif
25
26
22.PHONY: all git install clean distclean emptycache force-version get-git 27.PHONY: all git install clean distclean emptycache force-version get-git
23 28
24all: cgit git 29all: cgit git
25 30
26VERSION: force-version 31VERSION: force-version
27 @./gen-version.sh "$(CGIT_VERSION)" 32 @./gen-version.sh "$(CGIT_VERSION)"
28-include VERSION 33-include VERSION
29 34
30 35
31CFLAGS += -g -Wall -Igit 36CFLAGS += -g -Wall -Igit
32CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 37CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
33CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 38CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
34CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 39CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
35CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 40CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
36CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 41CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
37 42
38 43
39cgit: cgit.c $(OBJECTS) 44cgit: cgit.c $(OBJECTS)
40 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS) 45 $(CC) $(CFLAGS) cgit.c -o cgit $(OBJECTS) $(EXTLIBS)
41 46
42$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION 47$(OBJECTS): cgit.h git/xdiff/lib.a git/libgit.a VERSION
43 48
44git/xdiff/lib.a: | git 49git/xdiff/lib.a: | git
45 50
46git/libgit.a: | git 51git/libgit.a: | git
47 52
48git: 53git:
49 cd git && $(MAKE) xdiff/lib.a 54 cd git && $(MAKE) xdiff/lib.a
50 cd git && $(MAKE) libgit.a 55 cd git && $(MAKE) libgit.a
51 56
52install: all 57install: all
53 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH) 58 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH)
54 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 59 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
55 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css 60 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css
56 install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png 61 install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png
57 62
58uninstall: 63uninstall:
59 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 64 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
60 rm -f $(CGIT_SCRIPT_PATH)/cgit.css 65 rm -f $(CGIT_SCRIPT_PATH)/cgit.css
61 rm -f $(CGIT_SCRIPT_PATH)/cgit.png 66 rm -f $(CGIT_SCRIPT_PATH)/cgit.png
62 67
63clean: 68clean:
64 rm -f cgit VERSION *.o 69 rm -f cgit VERSION *.o
65 cd git && $(MAKE) clean 70 cd git && $(MAKE) clean
66 71
67distclean: clean 72distclean: clean
68 git clean -d -x 73 git clean -d -x
69 cd git && git clean -d -x 74 cd git && git clean -d -x
diff --git a/cgit.css b/cgit.css
index 1b2e9d6..f1003b4 100644
--- a/cgit.css
+++ b/cgit.css
@@ -64,139 +64,130 @@ table.list tr.nohover:hover {
64 64
65table.list th { 65table.list th {
66 font-weight: bold; 66 font-weight: bold;
67 border-bottom: solid 1px #777; 67 border-bottom: solid 1px #777;
68 padding: 0.1em 0.5em 0.1em 0.5em; 68 padding: 0.1em 0.5em 0.1em 0.5em;
69 vertical-align: baseline; 69 vertical-align: baseline;
70} 70}
71 71
72table.list td { 72table.list td {
73 border: none; 73 border: none;
74 padding: 0.1em 0.5em 0.1em 0.5em; 74 padding: 0.1em 0.5em 0.1em 0.5em;
75} 75}
76 76
77img { 77img {
78 border: none; 78 border: none;
79} 79}
80 80
81div#sidebar { 81div#sidebar {
82 vertical-align: top; 82 vertical-align: top;
83 width: 162px; 83 width: 162px;
84 padding: 0px 0px 0px 0px; 84 padding: 0px 0px 0px 0px;
85 margin: 4px; 85 margin: 4px;
86 float: left; 86 float: left;
87} 87}
88 88
89div#logo { 89div#logo {
90 margin: 0px; 90 margin: 0px;
91 padding: 4px 0px 4px 0px; 91 padding: 4px 0px 4px 0px;
92 text-align: center; 92 text-align: center;
93 background-color: #ccc; 93 background-color: #ccc;
94 border-top: solid 1px #eee; 94 border-top: solid 1px #eee;
95 border-left: solid 1px #eee; 95 border-left: solid 1px #eee;
96 border-right: solid 1px #aaa; 96 border-right: solid 1px #aaa;
97 border-bottom: solid 1px #aaa; 97 border-bottom: solid 1px #aaa;
98} 98}
99 99
100div#sidebar div.infobox { 100div#sidebar div.infobox {
101 margin: 0px 0px 0px 0px; 101 margin: 0px 0px 0px 0px;
102 padding: 0.5em; 102 padding: 0.5em;
103 text-align: left; 103 text-align: left;
104 background-color: #ccc; 104 background-color: #ccc;
105 border-top: solid 1px #eee; 105 border-top: solid 1px #eee;
106 border-left: solid 1px #eee; 106 border-left: solid 1px #eee;
107 border-right: solid 1px #aaa; 107 border-right: solid 1px #aaa;
108 border-bottom: solid 1px #aaa; 108 border-bottom: solid 1px #aaa;
109} 109}
110 110
111div#sidebar div.infobox h1 { 111div#sidebar div.infobox h1 {
112 font-size: 11pt; 112 font-size: 10pt;
113 font-weight: bold; 113 font-weight: bold;
114 margin: 0px; 114 margin: 0px;
115} 115}
116 116
117div#sidebar div.infobox a.menu { 117div#sidebar div.infobox a.menu {
118 display: block; 118 display: block;
119 background-color: #ccc; 119 background-color: #ccc;
120 padding: 0.1em 0.5em; 120 padding: 0.1em 0.5em;
121 text-decoration: none; 121 text-decoration: none;
122} 122}
123 123
124div#sidebar div.infobox a.menu:hover { 124div#sidebar div.infobox a.menu:hover {
125 background-color: #bbb; 125 background-color: #bbb;
126 text-decoration: none; 126 text-decoration: none;
127} 127}
128 128
129div#sidebar div.infobox select { 129div#sidebar div.infobox select {
130 width: 100%; 130 width: 100%;
131 border: solid 1px #aaa;
132 background-color: #bbb;
133 margin: 2px 0px 0px 0px; 131 margin: 2px 0px 0px 0px;
134 padding: 0px;
135} 132}
136 133
137td#branch-dropdown-cell { 134td#branch-dropdown-cell {
138 width: 99%; 135 width: 99%;
139} 136}
140 137
141input#switch-btn { 138input#switch-btn {
142 width: 20px; 139 width: 20px;
143 border: solid 1px #aaa;
144 background-color: #bbb;
145 margin: 2px 0px 0px 0px; 140 margin: 2px 0px 0px 0px;
146 padding: 0px;
147} 141}
148 142
149div#sidebar div.infobox input.txt { 143div#sidebar div.infobox input.txt {
150 width: 100%; 144 width: 100%;
151 border: solid 1px #aaa;
152 background-color: #bbb;
153 margin: 2px 0px 0px 0px; 145 margin: 2px 0px 0px 0px;
154 padding: 0;
155} 146}
156 147
157table#grid { 148table#grid {
158 margin: 0px; 149 margin: 0px;
159} 150}
160 151
161td#content { 152td#content {
162 vertical-align: top; 153 vertical-align: top;
163 padding: 1em 2em 1em 1em; 154 padding: 1em 2em 1em 1em;
164 border: none; 155 border: none;
165} 156}
166 157
167div#summary { 158div#summary {
168 vertical-align: top; 159 vertical-align: top;
169 margin-bottom: 1em; 160 margin-bottom: 1em;
170} 161}
171 162
172table#downloads { 163table#downloads {
173 float: right; 164 float: right;
174 border-collapse: collapse; 165 border-collapse: collapse;
175 border: solid 1px #777; 166 border: solid 1px #777;
176 margin-left: 0.5em; 167 margin-left: 0.5em;
177 margin-bottom: 0.5em; 168 margin-bottom: 0.5em;
178} 169}
179 170
180table#downloads th { 171table#downloads th {
181 background-color: #ccc; 172 background-color: #ccc;
182} 173}
183 174
184div#blob { 175div#blob {
185 border: solid 1px black; 176 border: solid 1px black;
186} 177}
187 178
188div.error { 179div.error {
189 color: red; 180 color: red;
190 font-weight: bold; 181 font-weight: bold;
191 margin: 1em 2em; 182 margin: 1em 2em;
192} 183}
193 184
194a.ls-blob, a.ls-dir, a.ls-mod { 185a.ls-blob, a.ls-dir, a.ls-mod {
195 font-family: monospace; 186 font-family: monospace;
196} 187}
197 188
198td.ls-size { 189td.ls-size {
199 text-align: right; 190 text-align: right;
200} 191}
201 192
202td.ls-size { 193td.ls-size {
diff --git a/cgit.h b/cgit.h
index 163f355..6291c58 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,140 +1,147 @@
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#include <utf8.h>
19 20
20 21
21/* 22/*
22 * The valid cgit repo-commands 23 * The valid cgit repo-commands
23 */ 24 */
24#define CMD_LOG 1 25#define CMD_LOG 1
25#define CMD_COMMIT 2 26#define CMD_COMMIT 2
26#define CMD_DIFF 3 27#define CMD_DIFF 3
27#define CMD_TREE 4 28#define CMD_TREE 4
28#define CMD_BLOB 5 29#define CMD_BLOB 5
29#define CMD_SNAPSHOT 6 30#define CMD_SNAPSHOT 6
30#define CMD_TAG 7 31#define CMD_TAG 7
31#define CMD_REFS 8 32#define CMD_REFS 8
32 33
33/* 34/*
34 * Dateformats used on misc. pages 35 * Dateformats used on misc. pages
35 */ 36 */
36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 37#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
37#define FMT_SHORTDATE "%Y-%m-%d" 38#define FMT_SHORTDATE "%Y-%m-%d"
38 39
39 40
40/* 41/*
41 * Limits used for relative dates 42 * Limits used for relative dates
42 */ 43 */
43#define TM_MIN 60 44#define TM_MIN 60
44#define TM_HOUR (TM_MIN * 60) 45#define TM_HOUR (TM_MIN * 60)
45#define TM_DAY (TM_HOUR * 24) 46#define TM_DAY (TM_HOUR * 24)
46#define TM_WEEK (TM_DAY * 7) 47#define TM_WEEK (TM_DAY * 7)
47#define TM_YEAR (TM_DAY * 365) 48#define TM_YEAR (TM_DAY * 365)
48#define TM_MONTH (TM_YEAR / 12.0) 49#define TM_MONTH (TM_YEAR / 12.0)
49 50
50 51
52/*
53 * Default encoding
54 */
55#define PAGE_ENCODING "UTF-8"
56
51typedef void (*configfn)(const char *name, const char *value); 57typedef void (*configfn)(const char *name, const char *value);
52typedef void (*filepair_fn)(struct diff_filepair *pair); 58typedef void (*filepair_fn)(struct diff_filepair *pair);
53typedef void (*linediff_fn)(char *line, int len); 59typedef void (*linediff_fn)(char *line, int len);
54 60
55struct cacheitem { 61struct cacheitem {
56 char *name; 62 char *name;
57 struct stat st; 63 struct stat st;
58 int ttl; 64 int ttl;
59 int fd; 65 int fd;
60}; 66};
61 67
62struct repoinfo { 68struct repoinfo {
63 char *url; 69 char *url;
64 char *name; 70 char *name;
65 char *path; 71 char *path;
66 char *desc; 72 char *desc;
67 char *owner; 73 char *owner;
68 char *defbranch; 74 char *defbranch;
69 char *group; 75 char *group;
70 char *module_link; 76 char *module_link;
71 char *readme; 77 char *readme;
72 int snapshots; 78 int snapshots;
73 int enable_log_filecount; 79 int enable_log_filecount;
74 int enable_log_linecount; 80 int enable_log_linecount;
75}; 81};
76 82
77struct repolist { 83struct repolist {
78 int length; 84 int length;
79 int count; 85 int count;
80 struct repoinfo *repos; 86 struct repoinfo *repos;
81}; 87};
82 88
83struct commitinfo { 89struct commitinfo {
84 struct commit *commit; 90 struct commit *commit;
85 char *author; 91 char *author;
86 char *author_email; 92 char *author_email;
87 unsigned long author_date; 93 unsigned long author_date;
88 char *committer; 94 char *committer;
89 char *committer_email; 95 char *committer_email;
90 unsigned long committer_date; 96 unsigned long committer_date;
91 char *subject; 97 char *subject;
92 char *msg; 98 char *msg;
99 char *msg_encoding;
93}; 100};
94 101
95struct taginfo { 102struct taginfo {
96 char *tagger; 103 char *tagger;
97 char *tagger_email; 104 char *tagger_email;
98 int tagger_date; 105 int tagger_date;
99 char *msg; 106 char *msg;
100}; 107};
101 108
102struct refinfo { 109struct refinfo {
103 const char *refname; 110 const char *refname;
104 struct object *object; 111 struct object *object;
105 union { 112 union {
106 struct taginfo *tag; 113 struct taginfo *tag;
107 struct commitinfo *commit; 114 struct commitinfo *commit;
108 }; 115 };
109}; 116};
110 117
111struct reflist { 118struct reflist {
112 struct refinfo **refs; 119 struct refinfo **refs;
113 int alloc; 120 int alloc;
114 int count; 121 int count;
115}; 122};
116 123
117extern const char *cgit_version; 124extern const char *cgit_version;
118 125
119extern struct repolist cgit_repolist; 126extern struct repolist cgit_repolist;
120extern struct repoinfo *cgit_repo; 127extern struct repoinfo *cgit_repo;
121extern int cgit_cmd; 128extern int cgit_cmd;
122 129
123extern char *cgit_root_title; 130extern char *cgit_root_title;
124extern char *cgit_css; 131extern char *cgit_css;
125extern char *cgit_logo; 132extern char *cgit_logo;
126extern char *cgit_index_header; 133extern char *cgit_index_header;
127extern char *cgit_index_info; 134extern char *cgit_index_info;
128extern char *cgit_logo_link; 135extern char *cgit_logo_link;
129extern char *cgit_module_link; 136extern char *cgit_module_link;
130extern char *cgit_agefile; 137extern char *cgit_agefile;
131extern char *cgit_virtual_root; 138extern char *cgit_virtual_root;
132extern char *cgit_script_name; 139extern char *cgit_script_name;
133extern char *cgit_cache_root; 140extern char *cgit_cache_root;
134extern char *cgit_repo_group; 141extern char *cgit_repo_group;
135 142
136extern int cgit_nocache; 143extern int cgit_nocache;
137extern int cgit_snapshots; 144extern int cgit_snapshots;
138extern int cgit_enable_index_links; 145extern int cgit_enable_index_links;
139extern int cgit_enable_log_filecount; 146extern int cgit_enable_log_filecount;
140extern int cgit_enable_log_linecount; 147extern int cgit_enable_log_linecount;
diff --git a/parsing.c b/parsing.c
index 30e7648..e8c7ab9 100644
--- a/parsing.c
+++ b/parsing.c
@@ -154,150 +154,175 @@ void cgit_parse_url(const char *url)
154 } 154 }
155 155
156 cmd = strchr(url, '/'); 156 cmd = strchr(url, '/');
157 while (!cgit_repo && cmd) { 157 while (!cgit_repo && cmd) {
158 cmd[0] = '\0'; 158 cmd[0] = '\0';
159 cgit_repo = cgit_get_repoinfo(url); 159 cgit_repo = cgit_get_repoinfo(url);
160 if (cgit_repo == NULL) { 160 if (cgit_repo == NULL) {
161 cmd[0] = '/'; 161 cmd[0] = '/';
162 cmd = strchr(cmd + 1, '/'); 162 cmd = strchr(cmd + 1, '/');
163 continue; 163 continue;
164 } 164 }
165 165
166 cgit_query_repo = cgit_repo->url; 166 cgit_query_repo = cgit_repo->url;
167 p = strchr(cmd + 1, '/'); 167 p = strchr(cmd + 1, '/');
168 if (p) { 168 if (p) {
169 p[0] = '\0'; 169 p[0] = '\0';
170 if (p[1]) 170 if (p[1])
171 cgit_query_path = trim_end(p + 1, '/'); 171 cgit_query_path = trim_end(p + 1, '/');
172 } 172 }
173 cgit_cmd = cgit_get_cmd_index(cmd + 1); 173 cgit_cmd = cgit_get_cmd_index(cmd + 1);
174 cgit_query_page = xstrdup(cmd + 1); 174 cgit_query_page = xstrdup(cmd + 1);
175 return; 175 return;
176 } 176 }
177} 177}
178 178
179char *substr(const char *head, const char *tail) 179char *substr(const char *head, const char *tail)
180{ 180{
181 char *buf; 181 char *buf;
182 182
183 buf = xmalloc(tail - head + 1); 183 buf = xmalloc(tail - head + 1);
184 strncpy(buf, head, tail - head); 184 strncpy(buf, head, tail - head);
185 buf[tail - head] = '\0'; 185 buf[tail - head] = '\0';
186 return buf; 186 return buf;
187} 187}
188 188
189struct commitinfo *cgit_parse_commit(struct commit *commit) 189struct commitinfo *cgit_parse_commit(struct commit *commit)
190{ 190{
191 struct commitinfo *ret; 191 struct commitinfo *ret;
192 char *p = commit->buffer, *t = commit->buffer; 192 char *p = commit->buffer, *t = commit->buffer;
193 193
194 ret = xmalloc(sizeof(*ret)); 194 ret = xmalloc(sizeof(*ret));
195 ret->commit = commit; 195 ret->commit = commit;
196 ret->author = NULL; 196 ret->author = NULL;
197 ret->author_email = NULL; 197 ret->author_email = NULL;
198 ret->committer = NULL; 198 ret->committer = NULL;
199 ret->committer_email = NULL; 199 ret->committer_email = NULL;
200 ret->subject = NULL; 200 ret->subject = NULL;
201 ret->msg = NULL; 201 ret->msg = NULL;
202 ret->msg_encoding = NULL;
202 203
203 if (p == NULL) 204 if (p == NULL)
204 return ret; 205 return ret;
205 206
206 if (strncmp(p, "tree ", 5)) 207 if (strncmp(p, "tree ", 5))
207 die("Bad commit: %s", sha1_to_hex(commit->object.sha1)); 208 die("Bad commit: %s", sha1_to_hex(commit->object.sha1));
208 else 209 else
209 p += 46; // "tree " + hex[40] + "\n" 210 p += 46; // "tree " + hex[40] + "\n"
210 211
211 while (!strncmp(p, "parent ", 7)) 212 while (!strncmp(p, "parent ", 7))
212 p += 48; // "parent " + hex[40] + "\n" 213 p += 48; // "parent " + hex[40] + "\n"
213 214
214 if (!strncmp(p, "author ", 7)) { 215 if (!strncmp(p, "author ", 7)) {
215 p += 7; 216 p += 7;
216 t = strchr(p, '<') - 1; 217 t = strchr(p, '<') - 1;
217 ret->author = substr(p, t); 218 ret->author = substr(p, t);
218 p = t; 219 p = t;
219 t = strchr(t, '>') + 1; 220 t = strchr(t, '>') + 1;
220 ret->author_email = substr(p, t); 221 ret->author_email = substr(p, t);
221 ret->author_date = atol(++t); 222 ret->author_date = atol(++t);
222 p = strchr(t, '\n') + 1; 223 p = strchr(t, '\n') + 1;
223 } 224 }
224 225
225 if (!strncmp(p, "committer ", 9)) { 226 if (!strncmp(p, "committer ", 9)) {
226 p += 9; 227 p += 9;
227 t = strchr(p, '<') - 1; 228 t = strchr(p, '<') - 1;
228 ret->committer = substr(p, t); 229 ret->committer = substr(p, t);
229 p = t; 230 p = t;
230 t = strchr(t, '>') + 1; 231 t = strchr(t, '>') + 1;
231 ret->committer_email = substr(p, t); 232 ret->committer_email = substr(p, t);
232 ret->committer_date = atol(++t); 233 ret->committer_date = atol(++t);
233 p = strchr(t, '\n') + 1; 234 p = strchr(t, '\n') + 1;
234 } 235 }
235 236
237 if (!strncmp(p, "encoding ", 9)) {
238 p += 9;
239 t = strchr(p, '\n') + 1;
240 ret->msg_encoding = substr(p, t);
241 p = t;
242 } else
243 ret->msg_encoding = xstrdup(PAGE_ENCODING);
244
236 while (*p && (*p != '\n')) 245 while (*p && (*p != '\n'))
237 p = strchr(p, '\n') + 1; // skip unknown header fields 246 p = strchr(p, '\n') + 1; // skip unknown header fields
238 247
239 while (*p == '\n') 248 while (*p == '\n')
240 p = strchr(p, '\n') + 1; 249 p = strchr(p, '\n') + 1;
241 250
242 t = strchr(p, '\n'); 251 t = strchr(p, '\n');
243 if (t) { 252 if (t) {
244 if (*t == '\0') 253 if (*t == '\0')
245 ret->subject = "** empty **"; 254 ret->subject = "** empty **";
246 else 255 else
247 ret->subject = substr(p, t); 256 ret->subject = substr(p, t);
248 p = t + 1; 257 p = t + 1;
249 258
250 while (*p == '\n') 259 while (*p == '\n')
251 p = strchr(p, '\n') + 1; 260 p = strchr(p, '\n') + 1;
252 ret->msg = xstrdup(p); 261 ret->msg = xstrdup(p);
253 } else 262 } else
254 ret->subject = substr(p, p+strlen(p)); 263 ret->subject = substr(p, p+strlen(p));
255 264
265 if(strcmp(ret->msg_encoding, PAGE_ENCODING)) {
266 t = reencode_string(ret->subject, PAGE_ENCODING,
267 ret->msg_encoding);
268 if(t) {
269 free(ret->subject);
270 ret->subject = t;
271 }
272
273 t = reencode_string(ret->msg, PAGE_ENCODING,
274 ret->msg_encoding);
275 if(t) {
276 free(ret->msg);
277 ret->msg = t;
278 }
279 }
280
256 return ret; 281 return ret;
257} 282}
258 283
259 284
260struct taginfo *cgit_parse_tag(struct tag *tag) 285struct taginfo *cgit_parse_tag(struct tag *tag)
261{ 286{
262 void *data; 287 void *data;
263 enum object_type type; 288 enum object_type type;
264 unsigned long size; 289 unsigned long size;
265 char *p, *t; 290 char *p, *t;
266 struct taginfo *ret; 291 struct taginfo *ret;
267 292
268 data = read_sha1_file(tag->object.sha1, &type, &size); 293 data = read_sha1_file(tag->object.sha1, &type, &size);
269 if (!data || type != OBJ_TAG) { 294 if (!data || type != OBJ_TAG) {
270 free(data); 295 free(data);
271 return 0; 296 return 0;
272 } 297 }
273 298
274 ret = xmalloc(sizeof(*ret)); 299 ret = xmalloc(sizeof(*ret));
275 ret->tagger = NULL; 300 ret->tagger = NULL;
276 ret->tagger_email = NULL; 301 ret->tagger_email = NULL;
277 ret->tagger_date = 0; 302 ret->tagger_date = 0;
278 ret->msg = NULL; 303 ret->msg = NULL;
279 304
280 p = data; 305 p = data;
281 306
282 while (p && *p) { 307 while (p && *p) {
283 if (*p == '\n') 308 if (*p == '\n')
284 break; 309 break;
285 310
286 if (!strncmp(p, "tagger ", 7)) { 311 if (!strncmp(p, "tagger ", 7)) {
287 p += 7; 312 p += 7;
288 t = strchr(p, '<') - 1; 313 t = strchr(p, '<') - 1;
289 ret->tagger = substr(p, t); 314 ret->tagger = substr(p, t);
290 p = t; 315 p = t;
291 t = strchr(t, '>') + 1; 316 t = strchr(t, '>') + 1;
292 ret->tagger_email = substr(p, t); 317 ret->tagger_email = substr(p, t);
293 ret->tagger_date = atol(++t); 318 ret->tagger_date = atol(++t);
294 } 319 }
295 p = strchr(p, '\n') + 1; 320 p = strchr(p, '\n') + 1;
296 } 321 }
297 322
298 while (p && *p && (*p != '\n')) 323 while (p && *p && (*p != '\n'))
299 p = strchr(p, '\n') + 1; // skip unknown tag fields 324 p = strchr(p, '\n') + 1; // skip unknown tag fields
300 325
301 while (p && (*p == '\n')) 326 while (p && (*p == '\n'))
302 p = strchr(p, '\n') + 1; 327 p = strchr(p, '\n') + 1;
303 if (p && *p) 328 if (p && *p)
diff --git a/shared.c b/shared.c
index 6117f5c..8cb4808 100644
--- a/shared.c
+++ b/shared.c
@@ -222,96 +222,98 @@ void cgit_global_config_cb(const char *name, const char *value)
222 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { 222 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
223 if (*value == '/') 223 if (*value == '/')
224 cgit_repo->readme = xstrdup(value); 224 cgit_repo->readme = xstrdup(value);
225 else 225 else
226 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); 226 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
227 } else if (!strcmp(name, "include")) 227 } else if (!strcmp(name, "include"))
228 cgit_read_config(value, cgit_global_config_cb); 228 cgit_read_config(value, cgit_global_config_cb);
229} 229}
230 230
231void cgit_querystring_cb(const char *name, const char *value) 231void cgit_querystring_cb(const char *name, const char *value)
232{ 232{
233 if (!strcmp(name,"r")) { 233 if (!strcmp(name,"r")) {
234 cgit_query_repo = xstrdup(value); 234 cgit_query_repo = xstrdup(value);
235 cgit_repo = cgit_get_repoinfo(value); 235 cgit_repo = cgit_get_repoinfo(value);
236 } else if (!strcmp(name, "p")) { 236 } else if (!strcmp(name, "p")) {
237 cgit_query_page = xstrdup(value); 237 cgit_query_page = xstrdup(value);
238 cgit_cmd = cgit_get_cmd_index(value); 238 cgit_cmd = cgit_get_cmd_index(value);
239 } else if (!strcmp(name, "url")) { 239 } else if (!strcmp(name, "url")) {
240 cgit_parse_url(value); 240 cgit_parse_url(value);
241 } else if (!strcmp(name, "qt")) { 241 } else if (!strcmp(name, "qt")) {
242 cgit_query_grep = xstrdup(value); 242 cgit_query_grep = xstrdup(value);
243 } else if (!strcmp(name, "q")) { 243 } else if (!strcmp(name, "q")) {
244 cgit_query_search = xstrdup(value); 244 cgit_query_search = xstrdup(value);
245 } else if (!strcmp(name, "h")) { 245 } else if (!strcmp(name, "h")) {
246 cgit_query_head = xstrdup(value); 246 cgit_query_head = xstrdup(value);
247 cgit_query_has_symref = 1; 247 cgit_query_has_symref = 1;
248 } else if (!strcmp(name, "id")) { 248 } else if (!strcmp(name, "id")) {
249 cgit_query_sha1 = xstrdup(value); 249 cgit_query_sha1 = xstrdup(value);
250 cgit_query_has_sha1 = 1; 250 cgit_query_has_sha1 = 1;
251 } else if (!strcmp(name, "id2")) { 251 } else if (!strcmp(name, "id2")) {
252 cgit_query_sha2 = xstrdup(value); 252 cgit_query_sha2 = xstrdup(value);
253 cgit_query_has_sha1 = 1; 253 cgit_query_has_sha1 = 1;
254 } else if (!strcmp(name, "ofs")) { 254 } else if (!strcmp(name, "ofs")) {
255 cgit_query_ofs = atoi(value); 255 cgit_query_ofs = atoi(value);
256 } else if (!strcmp(name, "path")) { 256 } else if (!strcmp(name, "path")) {
257 cgit_query_path = trim_end(value, '/'); 257 cgit_query_path = trim_end(value, '/');
258 } else if (!strcmp(name, "name")) { 258 } else if (!strcmp(name, "name")) {
259 cgit_query_name = xstrdup(value); 259 cgit_query_name = xstrdup(value);
260 } 260 }
261} 261}
262 262
263void *cgit_free_commitinfo(struct commitinfo *info) 263void *cgit_free_commitinfo(struct commitinfo *info)
264{ 264{
265 free(info->author); 265 free(info->author);
266 free(info->author_email); 266 free(info->author_email);
267 free(info->committer); 267 free(info->committer);
268 free(info->committer_email); 268 free(info->committer_email);
269 free(info->subject); 269 free(info->subject);
270 free(info->msg);
271 free(info->msg_encoding);
270 free(info); 272 free(info);
271 return NULL; 273 return NULL;
272} 274}
273 275
274int hextoint(char c) 276int hextoint(char c)
275{ 277{
276 if (c >= 'a' && c <= 'f') 278 if (c >= 'a' && c <= 'f')
277 return 10 + c - 'a'; 279 return 10 + c - 'a';
278 else if (c >= 'A' && c <= 'F') 280 else if (c >= 'A' && c <= 'F')
279 return 10 + c - 'A'; 281 return 10 + c - 'A';
280 else if (c >= '0' && c <= '9') 282 else if (c >= '0' && c <= '9')
281 return c - '0'; 283 return c - '0';
282 else 284 else
283 return -1; 285 return -1;
284} 286}
285 287
286char *trim_end(const char *str, char c) 288char *trim_end(const char *str, char c)
287{ 289{
288 int len; 290 int len;
289 char *s, *t; 291 char *s, *t;
290 292
291 if (str == NULL) 293 if (str == NULL)
292 return NULL; 294 return NULL;
293 t = (char *)str; 295 t = (char *)str;
294 len = strlen(t); 296 len = strlen(t);
295 while(len > 0 && t[len - 1] == c) 297 while(len > 0 && t[len - 1] == c)
296 len--; 298 len--;
297 299
298 if (len == 0) 300 if (len == 0)
299 return NULL; 301 return NULL;
300 302
301 c = t[len]; 303 c = t[len];
302 t[len] = '\0'; 304 t[len] = '\0';
303 s = xstrdup(t); 305 s = xstrdup(t);
304 t[len] = c; 306 t[len] = c;
305 return s; 307 return s;
306} 308}
307 309
308char *strlpart(char *txt, int maxlen) 310char *strlpart(char *txt, int maxlen)
309{ 311{
310 char *result; 312 char *result;
311 313
312 if (!txt) 314 if (!txt)
313 return txt; 315 return txt;
314 316
315 if (strlen(txt) <= maxlen) 317 if (strlen(txt) <= maxlen)
316 return txt; 318 return txt;
317 result = xmalloc(maxlen + 1); 319 result = xmalloc(maxlen + 1);
diff --git a/ui-log.c b/ui-log.c
index 9f5fdf6..e5f3c57 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,133 +1,140 @@
1/* ui-log.c: functions for log output 1/* ui-log.c: functions for log output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11int files, lines; 11int files, add_lines, rem_lines;
12 12
13void count_lines(char *line, int size) 13void count_lines(char *line, int size)
14{ 14{
15 if (size>0 && (line[0] == '+' || line[0] == '-')) 15 if (size <= 0)
16 lines++; 16 return;
17
18 if (line[0] == '+')
19 add_lines++;
20
21 else if (line[0] == '-')
22 rem_lines++;
17} 23}
18 24
19void inspect_files(struct diff_filepair *pair) 25void inspect_files(struct diff_filepair *pair)
20{ 26{
21 files++; 27 files++;
22 if (cgit_repo->enable_log_linecount) 28 if (cgit_repo->enable_log_linecount)
23 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines); 29 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines);
24} 30}
25 31
26void print_commit(struct commit *commit) 32void print_commit(struct commit *commit)
27{ 33{
28 struct commitinfo *info; 34 struct commitinfo *info;
29 35
30 info = cgit_parse_commit(commit); 36 info = cgit_parse_commit(commit);
31 html("<tr><td>"); 37 html("<tr><td>");
32 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 38 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
33 html("</td><td>"); 39 html("</td><td>");
34 cgit_commit_link(info->subject, NULL, NULL, cgit_query_head, 40 cgit_commit_link(info->subject, NULL, NULL, cgit_query_head,
35 sha1_to_hex(commit->object.sha1)); 41 sha1_to_hex(commit->object.sha1));
36 if (cgit_repo->enable_log_filecount) { 42 if (cgit_repo->enable_log_filecount) {
37 files = 0; 43 files = 0;
38 lines = 0; 44 add_lines = 0;
45 rem_lines = 0;
39 cgit_diff_commit(commit, inspect_files); 46 cgit_diff_commit(commit, inspect_files);
40 html("</td><td class='right'>"); 47 html("</td><td class='right'>");
41 htmlf("%d", files); 48 htmlf("%d", files);
42 if (cgit_repo->enable_log_linecount) { 49 if (cgit_repo->enable_log_linecount) {
43 html("</td><td class='right'>"); 50 html("</td><td class='right'>");
44 htmlf("%d", lines); 51 htmlf("-%d/+%d", rem_lines, add_lines);
45 } 52 }
46 } 53 }
47 html("</td><td>"); 54 html("</td><td>");
48 html_txt(info->author); 55 html_txt(info->author);
49 html("</td></tr>\n"); 56 html("</td></tr>\n");
50 cgit_free_commitinfo(info); 57 cgit_free_commitinfo(info);
51} 58}
52 59
53 60
54void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager) 61void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager)
55{ 62{
56 struct rev_info rev; 63 struct rev_info rev;
57 struct commit *commit; 64 struct commit *commit;
58 const char *argv[] = {NULL, tip, NULL, NULL, NULL}; 65 const char *argv[] = {NULL, tip, NULL, NULL, NULL};
59 int argc = 2; 66 int argc = 2;
60 int i; 67 int i;
61 68
62 if (!tip) 69 if (!tip)
63 argv[1] = cgit_query_head; 70 argv[1] = cgit_query_head;
64 71
65 if (grep && pattern && (!strcmp(grep, "grep") || 72 if (grep && pattern && (!strcmp(grep, "grep") ||
66 !strcmp(grep, "author") || 73 !strcmp(grep, "author") ||
67 !strcmp(grep, "committer"))) 74 !strcmp(grep, "committer")))
68 argv[argc++] = fmt("--%s=%s", grep, pattern); 75 argv[argc++] = fmt("--%s=%s", grep, pattern);
69 76
70 if (path) { 77 if (path) {
71 argv[argc++] = "--"; 78 argv[argc++] = "--";
72 argv[argc++] = path; 79 argv[argc++] = path;
73 } 80 }
74 init_revisions(&rev, NULL); 81 init_revisions(&rev, NULL);
75 rev.abbrev = DEFAULT_ABBREV; 82 rev.abbrev = DEFAULT_ABBREV;
76 rev.commit_format = CMIT_FMT_DEFAULT; 83 rev.commit_format = CMIT_FMT_DEFAULT;
77 rev.verbose_header = 1; 84 rev.verbose_header = 1;
78 rev.show_root_diff = 0; 85 rev.show_root_diff = 0;
79 setup_revisions(argc, argv, &rev, NULL); 86 setup_revisions(argc, argv, &rev, NULL);
80 if (rev.grep_filter) { 87 if (rev.grep_filter) {
81 rev.grep_filter->regflags |= REG_ICASE; 88 rev.grep_filter->regflags |= REG_ICASE;
82 compile_grep_patterns(rev.grep_filter); 89 compile_grep_patterns(rev.grep_filter);
83 } 90 }
84 prepare_revision_walk(&rev); 91 prepare_revision_walk(&rev);
85 92
86 html("<table class='list nowrap'>"); 93 html("<table class='list nowrap'>");
87 html("<tr class='nohover'><th class='left'>Age</th>" 94 html("<tr class='nohover'><th class='left'>Age</th>"
88 "<th class='left'>Message</th>"); 95 "<th class='left'>Message</th>");
89 96
90 if (cgit_repo->enable_log_filecount) { 97 if (cgit_repo->enable_log_filecount) {
91 html("<th class='left'>Files</th>"); 98 html("<th class='right'>Files</th>");
92 if (cgit_repo->enable_log_linecount) 99 if (cgit_repo->enable_log_linecount)
93 html("<th class='left'>Lines</th>"); 100 html("<th class='right'>Lines</th>");
94 } 101 }
95 html("<th class='left'>Author</th></tr>\n"); 102 html("<th class='left'>Author</th></tr>\n");
96 103
97 if (ofs<0) 104 if (ofs<0)
98 ofs = 0; 105 ofs = 0;
99 106
100 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { 107 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
101 free(commit->buffer); 108 free(commit->buffer);
102 commit->buffer = NULL; 109 commit->buffer = NULL;
103 free_commit_list(commit->parents); 110 free_commit_list(commit->parents);
104 commit->parents = NULL; 111 commit->parents = NULL;
105 } 112 }
106 113
107 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { 114 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
108 print_commit(commit); 115 print_commit(commit);
109 free(commit->buffer); 116 free(commit->buffer);
110 commit->buffer = NULL; 117 commit->buffer = NULL;
111 free_commit_list(commit->parents); 118 free_commit_list(commit->parents);
112 commit->parents = NULL; 119 commit->parents = NULL;
113 } 120 }
114 html("</table>\n"); 121 html("</table>\n");
115 122
116 if (pager) { 123 if (pager) {
117 html("<div class='pager'>"); 124 html("<div class='pager'>");
118 if (ofs > 0) { 125 if (ofs > 0) {
119 cgit_log_link("[prev]", NULL, NULL, cgit_query_head, 126 cgit_log_link("[prev]", NULL, NULL, cgit_query_head,
120 cgit_query_sha1, cgit_query_path, 127 cgit_query_sha1, cgit_query_path,
121 ofs - cnt, cgit_query_grep, 128 ofs - cnt, cgit_query_grep,
122 cgit_query_search); 129 cgit_query_search);
123 html("&nbsp;"); 130 html("&nbsp;");
124 } 131 }
125 if ((commit = get_revision(&rev)) != NULL) { 132 if ((commit = get_revision(&rev)) != NULL) {
126 cgit_log_link("[next]", NULL, NULL, cgit_query_head, 133 cgit_log_link("[next]", NULL, NULL, cgit_query_head,
127 cgit_query_sha1, cgit_query_path, 134 cgit_query_sha1, cgit_query_path,
128 ofs + cnt, cgit_query_grep, 135 ofs + cnt, cgit_query_grep,
129 cgit_query_search); 136 cgit_query_search);
130 } 137 }
131 html("</div>"); 138 html("</div>");
132 } 139 }
133} 140}
diff --git a/ui-shared.c b/ui-shared.c
index 72a7b44..7c69f60 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -307,97 +307,97 @@ void cgit_print_date(time_t secs, char *format)
307 time = gmtime(&secs); 307 time = gmtime(&secs);
308 strftime(buf, sizeof(buf)-1, format, time); 308 strftime(buf, sizeof(buf)-1, format, time);
309 html_txt(buf); 309 html_txt(buf);
310} 310}
311 311
312void cgit_print_age(time_t t, time_t max_relative, char *format) 312void cgit_print_age(time_t t, time_t max_relative, char *format)
313{ 313{
314 time_t now, secs; 314 time_t now, secs;
315 315
316 time(&now); 316 time(&now);
317 secs = now - t; 317 secs = now - t;
318 318
319 if (secs > max_relative && max_relative >= 0) { 319 if (secs > max_relative && max_relative >= 0) {
320 cgit_print_date(t, format); 320 cgit_print_date(t, format);
321 return; 321 return;
322 } 322 }
323 323
324 if (secs < TM_HOUR * 2) { 324 if (secs < TM_HOUR * 2) {
325 htmlf("<span class='age-mins'>%.0f min.</span>", 325 htmlf("<span class='age-mins'>%.0f min.</span>",
326 secs * 1.0 / TM_MIN); 326 secs * 1.0 / TM_MIN);
327 return; 327 return;
328 } 328 }
329 if (secs < TM_DAY * 2) { 329 if (secs < TM_DAY * 2) {
330 htmlf("<span class='age-hours'>%.0f hours</span>", 330 htmlf("<span class='age-hours'>%.0f hours</span>",
331 secs * 1.0 / TM_HOUR); 331 secs * 1.0 / TM_HOUR);
332 return; 332 return;
333 } 333 }
334 if (secs < TM_WEEK * 2) { 334 if (secs < TM_WEEK * 2) {
335 htmlf("<span class='age-days'>%.0f days</span>", 335 htmlf("<span class='age-days'>%.0f days</span>",
336 secs * 1.0 / TM_DAY); 336 secs * 1.0 / TM_DAY);
337 return; 337 return;
338 } 338 }
339 if (secs < TM_MONTH * 2) { 339 if (secs < TM_MONTH * 2) {
340 htmlf("<span class='age-weeks'>%.0f weeks</span>", 340 htmlf("<span class='age-weeks'>%.0f weeks</span>",
341 secs * 1.0 / TM_WEEK); 341 secs * 1.0 / TM_WEEK);
342 return; 342 return;
343 } 343 }
344 if (secs < TM_YEAR * 2) { 344 if (secs < TM_YEAR * 2) {
345 htmlf("<span class='age-months'>%.0f months</span>", 345 htmlf("<span class='age-months'>%.0f months</span>",
346 secs * 1.0 / TM_MONTH); 346 secs * 1.0 / TM_MONTH);
347 return; 347 return;
348 } 348 }
349 htmlf("<span class='age-years'>%.0f years</span>", 349 htmlf("<span class='age-years'>%.0f years</span>",
350 secs * 1.0 / TM_YEAR); 350 secs * 1.0 / TM_YEAR);
351} 351}
352 352
353void cgit_print_docstart(char *title, struct cacheitem *item) 353void cgit_print_docstart(char *title, struct cacheitem *item)
354{ 354{
355 html("Content-Type: text/html; charset=utf-8\n"); 355 html("Content-Type: text/html; charset=" PAGE_ENCODING "\n");
356 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 356 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
357 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 357 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
358 ttl_seconds(item->ttl))); 358 ttl_seconds(item->ttl)));
359 html("\n"); 359 html("\n");
360 html(cgit_doctype); 360 html(cgit_doctype);
361 html("<html>\n"); 361 html("<html>\n");
362 html("<head>\n"); 362 html("<head>\n");
363 html("<title>"); 363 html("<title>");
364 html_txt(title); 364 html_txt(title);
365 html("</title>\n"); 365 html("</title>\n");
366 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 366 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
367 html("<link rel='stylesheet' type='text/css' href='"); 367 html("<link rel='stylesheet' type='text/css' href='");
368 html_attr(cgit_css); 368 html_attr(cgit_css);
369 html("'/>\n"); 369 html("'/>\n");
370 html("</head>\n"); 370 html("</head>\n");
371 html("<body>\n"); 371 html("<body>\n");
372} 372}
373 373
374void cgit_print_docend() 374void cgit_print_docend()
375{ 375{
376 html("</td>\n</tr>\n<table>\n</body>\n</html>\n"); 376 html("</td>\n</tr>\n<table>\n</body>\n</html>\n");
377} 377}
378 378
379int print_branch_option(const char *refname, const unsigned char *sha1, 379int print_branch_option(const char *refname, const unsigned char *sha1,
380 int flags, void *cb_data) 380 int flags, void *cb_data)
381{ 381{
382 char *name = (char *)refname; 382 char *name = (char *)refname;
383 html_option(name, name, cgit_query_head); 383 html_option(name, name, cgit_query_head);
384 return 0; 384 return 0;
385} 385}
386 386
387int print_archive_ref(const char *refname, const unsigned char *sha1, 387int print_archive_ref(const char *refname, const unsigned char *sha1,
388 int flags, void *cb_data) 388 int flags, void *cb_data)
389{ 389{
390 struct tag *tag; 390 struct tag *tag;
391 struct taginfo *info; 391 struct taginfo *info;
392 struct object *obj; 392 struct object *obj;
393 char buf[256], *url; 393 char buf[256], *url;
394 unsigned char fileid[20]; 394 unsigned char fileid[20];
395 int *header = (int *)cb_data; 395 int *header = (int *)cb_data;
396 396
397 if (prefixcmp(refname, "refs/archives")) 397 if (prefixcmp(refname, "refs/archives"))
398 return 0; 398 return 0;
399 strncpy(buf, refname+14, sizeof(buf)); 399 strncpy(buf, refname+14, sizeof(buf));
400 obj = parse_object(sha1); 400 obj = parse_object(sha1);
401 if (!obj) 401 if (!obj)
402 return 1; 402 return 1;
403 if (obj->type == OBJ_TAG) { 403 if (obj->type == OBJ_TAG) {