summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile1
-rw-r--r--cgit.c1
-rw-r--r--cgit.h1
-rw-r--r--cmd.c7
-rw-r--r--html.c5
-rw-r--r--html.h1
-rw-r--r--ui-plain.c82
-rw-r--r--ui-plain.h6
-rw-r--r--ui-shared.c2
9 files changed, 106 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 78aad10..a305894 100644
--- a/Makefile
+++ b/Makefile
@@ -1,125 +1,126 @@
1CGIT_VERSION = v0.7.2 1CGIT_VERSION = v0.7.2
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.6 7GIT_VER = 1.5.6
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#
16# Define a way to invoke make in subdirs quietly, shamelessly ripped 16# Define a way to invoke make in subdirs quietly, shamelessly ripped
17# from git.git 17# from git.git
18# 18#
19QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir 19QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
20QUIET_SUBDIR1 = 20QUIET_SUBDIR1 =
21 21
22ifneq ($(findstring $(MAKEFLAGS),w),w) 22ifneq ($(findstring $(MAKEFLAGS),w),w)
23PRINT_DIR = --no-print-directory 23PRINT_DIR = --no-print-directory
24else # "make -w" 24else # "make -w"
25NO_SUBDIR = : 25NO_SUBDIR = :
26endif 26endif
27 27
28ifndef V 28ifndef V
29 QUIET_CC = @echo ' ' CC $@; 29 QUIET_CC = @echo ' ' CC $@;
30 QUIET_MM = @echo ' ' MM $@; 30 QUIET_MM = @echo ' ' MM $@;
31 QUIET_SUBDIR0 = +@subdir= 31 QUIET_SUBDIR0 = +@subdir=
32 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \ 32 QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
33 $(MAKE) $(PRINT_DIR) -C $$subdir 33 $(MAKE) $(PRINT_DIR) -C $$subdir
34endif 34endif
35 35
36# 36#
37# Define a pattern rule for automatic dependency building 37# Define a pattern rule for automatic dependency building
38# 38#
39%.d: %.c 39%.d: %.c
40 $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@ 40 $(QUIET_MM)$(CC) $(CFLAGS) -MM $< | sed -e 's/\($*\)\.o:/\1.o $@:/g' >$@
41 41
42# 42#
43# Define a pattern rule for silent object building 43# Define a pattern rule for silent object building
44# 44#
45%.o: %.c 45%.o: %.c
46 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $< 46 $(QUIET_CC)$(CC) -o $*.o -c $(CFLAGS) $<
47 47
48 48
49EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 49EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto
50OBJECTS = 50OBJECTS =
51OBJECTS += cache.o 51OBJECTS += cache.o
52OBJECTS += cgit.o 52OBJECTS += cgit.o
53OBJECTS += cmd.o 53OBJECTS += cmd.o
54OBJECTS += configfile.o 54OBJECTS += configfile.o
55OBJECTS += html.o 55OBJECTS += html.o
56OBJECTS += parsing.o 56OBJECTS += parsing.o
57OBJECTS += shared.o 57OBJECTS += shared.o
58OBJECTS += ui-blob.o 58OBJECTS += ui-blob.o
59OBJECTS += ui-clone.o 59OBJECTS += ui-clone.o
60OBJECTS += ui-commit.o 60OBJECTS += ui-commit.o
61OBJECTS += ui-diff.o 61OBJECTS += ui-diff.o
62OBJECTS += ui-log.o 62OBJECTS += ui-log.o
63OBJECTS += ui-patch.o 63OBJECTS += ui-patch.o
64OBJECTS += ui-plain.o
64OBJECTS += ui-refs.o 65OBJECTS += ui-refs.o
65OBJECTS += ui-repolist.o 66OBJECTS += ui-repolist.o
66OBJECTS += ui-shared.o 67OBJECTS += ui-shared.o
67OBJECTS += ui-snapshot.o 68OBJECTS += ui-snapshot.o
68OBJECTS += ui-summary.o 69OBJECTS += ui-summary.o
69OBJECTS += ui-tag.o 70OBJECTS += ui-tag.o
70OBJECTS += ui-tree.o 71OBJECTS += ui-tree.o
71 72
72ifdef NEEDS_LIBICONV 73ifdef NEEDS_LIBICONV
73 EXTLIBS += -liconv 74 EXTLIBS += -liconv
74endif 75endif
75 76
76 77
77.PHONY: all git test install uninstall clean force-version get-git 78.PHONY: all git test install uninstall clean force-version get-git
78 79
79all: cgit 80all: cgit
80 81
81VERSION: force-version 82VERSION: force-version
82 @./gen-version.sh "$(CGIT_VERSION)" 83 @./gen-version.sh "$(CGIT_VERSION)"
83-include VERSION 84-include VERSION
84 85
85 86
86CFLAGS += -g -Wall -Igit 87CFLAGS += -g -Wall -Igit
87CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)' 88CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER)'
88CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"' 89CFLAGS += -DCGIT_VERSION='"$(CGIT_VERSION)"'
89CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"' 90CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
90CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"' 91CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
91CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"' 92CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
92 93
93 94
94cgit: $(OBJECTS) git/libgit.a git/xdiff/lib.a 95cgit: $(OBJECTS) git/libgit.a git/xdiff/lib.a
95 $(QUIET_CC)$(CC) $(CFLAGS) -o cgit $(OBJECTS) $(EXTLIBS) 96 $(QUIET_CC)$(CC) $(CFLAGS) -o cgit $(OBJECTS) $(EXTLIBS)
96 97
97cgit.o: VERSION 98cgit.o: VERSION
98 99
99-include $(OBJECTS:.o=.d) 100-include $(OBJECTS:.o=.d)
100 101
101git/libgit.a: git 102git/libgit.a: git
102 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) libgit.a 103 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) libgit.a
103 104
104git/xdiff/lib.a: git 105git/xdiff/lib.a: git
105 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) xdiff/lib.a 106 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) xdiff/lib.a
106 107
107test: all 108test: all
108 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all 109 $(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
109 110
110install: all 111install: all
111 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH) 112 mkdir -p $(DESTDIR)$(CGIT_SCRIPT_PATH)
112 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 113 install cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
113 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css 114 install cgit.css $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.css
114 install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png 115 install cgit.png $(DESTDIR)$(CGIT_SCRIPT_PATH)/cgit.png
115 116
116uninstall: 117uninstall:
117 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) 118 rm -f $(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
118 rm -f $(CGIT_SCRIPT_PATH)/cgit.css 119 rm -f $(CGIT_SCRIPT_PATH)/cgit.css
119 rm -f $(CGIT_SCRIPT_PATH)/cgit.png 120 rm -f $(CGIT_SCRIPT_PATH)/cgit.png
120 121
121clean: 122clean:
122 rm -f cgit VERSION *.o *.d 123 rm -f cgit VERSION *.o *.d
123 124
124get-git: 125get-git:
125 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git 126 curl $(GIT_URL) | tar -xj && rm -rf git && mv git-$(GIT_VER) git
diff --git a/cgit.c b/cgit.c
index f49fffa..497337b 100644
--- a/cgit.c
+++ b/cgit.c
@@ -94,192 +94,193 @@ void config_cb(const char *name, const char *value)
94 else if (!strcmp(name, "repo.url")) 94 else if (!strcmp(name, "repo.url"))
95 ctx.repo = cgit_add_repo(value); 95 ctx.repo = cgit_add_repo(value);
96 else if (!strcmp(name, "repo.name")) 96 else if (!strcmp(name, "repo.name"))
97 ctx.repo->name = xstrdup(value); 97 ctx.repo->name = xstrdup(value);
98 else if (ctx.repo && !strcmp(name, "repo.path")) 98 else if (ctx.repo && !strcmp(name, "repo.path"))
99 ctx.repo->path = trim_end(value, '/'); 99 ctx.repo->path = trim_end(value, '/');
100 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 100 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
101 ctx.repo->clone_url = xstrdup(value); 101 ctx.repo->clone_url = xstrdup(value);
102 else if (ctx.repo && !strcmp(name, "repo.desc")) 102 else if (ctx.repo && !strcmp(name, "repo.desc"))
103 ctx.repo->desc = xstrdup(value); 103 ctx.repo->desc = xstrdup(value);
104 else if (ctx.repo && !strcmp(name, "repo.owner")) 104 else if (ctx.repo && !strcmp(name, "repo.owner"))
105 ctx.repo->owner = xstrdup(value); 105 ctx.repo->owner = xstrdup(value);
106 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 106 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
107 ctx.repo->defbranch = xstrdup(value); 107 ctx.repo->defbranch = xstrdup(value);
108 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 108 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
109 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 109 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
110 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 110 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
111 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 111 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
112 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 112 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
113 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 113 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
114 else if (ctx.repo && !strcmp(name, "repo.module-link")) 114 else if (ctx.repo && !strcmp(name, "repo.module-link"))
115 ctx.repo->module_link= xstrdup(value); 115 ctx.repo->module_link= xstrdup(value);
116 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 116 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
117 if (*value == '/') 117 if (*value == '/')
118 ctx.repo->readme = xstrdup(value); 118 ctx.repo->readme = xstrdup(value);
119 else 119 else
120 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 120 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
121 } else if (!strcmp(name, "include")) 121 } else if (!strcmp(name, "include"))
122 parse_configfile(value, config_cb); 122 parse_configfile(value, config_cb);
123} 123}
124 124
125static void querystring_cb(const char *name, const char *value) 125static void querystring_cb(const char *name, const char *value)
126{ 126{
127 if (!strcmp(name,"r")) { 127 if (!strcmp(name,"r")) {
128 ctx.qry.repo = xstrdup(value); 128 ctx.qry.repo = xstrdup(value);
129 ctx.repo = cgit_get_repoinfo(value); 129 ctx.repo = cgit_get_repoinfo(value);
130 } else if (!strcmp(name, "p")) { 130 } else if (!strcmp(name, "p")) {
131 ctx.qry.page = xstrdup(value); 131 ctx.qry.page = xstrdup(value);
132 } else if (!strcmp(name, "url")) { 132 } else if (!strcmp(name, "url")) {
133 cgit_parse_url(value); 133 cgit_parse_url(value);
134 } else if (!strcmp(name, "qt")) { 134 } else if (!strcmp(name, "qt")) {
135 ctx.qry.grep = xstrdup(value); 135 ctx.qry.grep = xstrdup(value);
136 } else if (!strcmp(name, "q")) { 136 } else if (!strcmp(name, "q")) {
137 ctx.qry.search = xstrdup(value); 137 ctx.qry.search = xstrdup(value);
138 } else if (!strcmp(name, "h")) { 138 } else if (!strcmp(name, "h")) {
139 ctx.qry.head = xstrdup(value); 139 ctx.qry.head = xstrdup(value);
140 ctx.qry.has_symref = 1; 140 ctx.qry.has_symref = 1;
141 } else if (!strcmp(name, "id")) { 141 } else if (!strcmp(name, "id")) {
142 ctx.qry.sha1 = xstrdup(value); 142 ctx.qry.sha1 = xstrdup(value);
143 ctx.qry.has_sha1 = 1; 143 ctx.qry.has_sha1 = 1;
144 } else if (!strcmp(name, "id2")) { 144 } else if (!strcmp(name, "id2")) {
145 ctx.qry.sha2 = xstrdup(value); 145 ctx.qry.sha2 = xstrdup(value);
146 ctx.qry.has_sha1 = 1; 146 ctx.qry.has_sha1 = 1;
147 } else if (!strcmp(name, "ofs")) { 147 } else if (!strcmp(name, "ofs")) {
148 ctx.qry.ofs = atoi(value); 148 ctx.qry.ofs = atoi(value);
149 } else if (!strcmp(name, "path")) { 149 } else if (!strcmp(name, "path")) {
150 ctx.qry.path = trim_end(value, '/'); 150 ctx.qry.path = trim_end(value, '/');
151 } else if (!strcmp(name, "name")) { 151 } else if (!strcmp(name, "name")) {
152 ctx.qry.name = xstrdup(value); 152 ctx.qry.name = xstrdup(value);
153 } else if (!strcmp(name, "mimetype")) { 153 } else if (!strcmp(name, "mimetype")) {
154 ctx.qry.mimetype = xstrdup(value); 154 ctx.qry.mimetype = xstrdup(value);
155 } 155 }
156} 156}
157 157
158static void prepare_context(struct cgit_context *ctx) 158static void prepare_context(struct cgit_context *ctx)
159{ 159{
160 memset(ctx, 0, sizeof(ctx)); 160 memset(ctx, 0, sizeof(ctx));
161 ctx->cfg.agefile = "info/web/last-modified"; 161 ctx->cfg.agefile = "info/web/last-modified";
162 ctx->cfg.nocache = 0; 162 ctx->cfg.nocache = 0;
163 ctx->cfg.cache_size = 0; 163 ctx->cfg.cache_size = 0;
164 ctx->cfg.cache_dynamic_ttl = 5; 164 ctx->cfg.cache_dynamic_ttl = 5;
165 ctx->cfg.cache_max_create_time = 5; 165 ctx->cfg.cache_max_create_time = 5;
166 ctx->cfg.cache_repo_ttl = 5; 166 ctx->cfg.cache_repo_ttl = 5;
167 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 167 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
168 ctx->cfg.cache_root_ttl = 5; 168 ctx->cfg.cache_root_ttl = 5;
169 ctx->cfg.cache_static_ttl = -1; 169 ctx->cfg.cache_static_ttl = -1;
170 ctx->cfg.css = "/cgit.css"; 170 ctx->cfg.css = "/cgit.css";
171 ctx->cfg.logo = "/git-logo.png"; 171 ctx->cfg.logo = "/git-logo.png";
172 ctx->cfg.local_time = 0; 172 ctx->cfg.local_time = 0;
173 ctx->cfg.max_repo_count = 50; 173 ctx->cfg.max_repo_count = 50;
174 ctx->cfg.max_commit_count = 50; 174 ctx->cfg.max_commit_count = 50;
175 ctx->cfg.max_lock_attempts = 5; 175 ctx->cfg.max_lock_attempts = 5;
176 ctx->cfg.max_msg_len = 80; 176 ctx->cfg.max_msg_len = 80;
177 ctx->cfg.max_repodesc_len = 80; 177 ctx->cfg.max_repodesc_len = 80;
178 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 178 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
179 ctx->cfg.renamelimit = -1; 179 ctx->cfg.renamelimit = -1;
180 ctx->cfg.robots = "index, nofollow"; 180 ctx->cfg.robots = "index, nofollow";
181 ctx->cfg.root_title = "Git repository browser"; 181 ctx->cfg.root_title = "Git repository browser";
182 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 182 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
183 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 183 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
184 ctx->cfg.summary_branches = 10; 184 ctx->cfg.summary_branches = 10;
185 ctx->cfg.summary_log = 10; 185 ctx->cfg.summary_log = 10;
186 ctx->cfg.summary_tags = 10; 186 ctx->cfg.summary_tags = 10;
187 ctx->page.mimetype = "text/html"; 187 ctx->page.mimetype = "text/html";
188 ctx->page.charset = PAGE_ENCODING; 188 ctx->page.charset = PAGE_ENCODING;
189 ctx->page.filename = NULL; 189 ctx->page.filename = NULL;
190 ctx->page.size = 0;
190 ctx->page.modified = time(NULL); 191 ctx->page.modified = time(NULL);
191 ctx->page.expires = ctx->page.modified; 192 ctx->page.expires = ctx->page.modified;
192} 193}
193 194
194struct refmatch { 195struct refmatch {
195 char *req_ref; 196 char *req_ref;
196 char *first_ref; 197 char *first_ref;
197 int match; 198 int match;
198}; 199};
199 200
200int find_current_ref(const char *refname, const unsigned char *sha1, 201int find_current_ref(const char *refname, const unsigned char *sha1,
201 int flags, void *cb_data) 202 int flags, void *cb_data)
202{ 203{
203 struct refmatch *info; 204 struct refmatch *info;
204 205
205 info = (struct refmatch *)cb_data; 206 info = (struct refmatch *)cb_data;
206 if (!strcmp(refname, info->req_ref)) 207 if (!strcmp(refname, info->req_ref))
207 info->match = 1; 208 info->match = 1;
208 if (!info->first_ref) 209 if (!info->first_ref)
209 info->first_ref = xstrdup(refname); 210 info->first_ref = xstrdup(refname);
210 return info->match; 211 return info->match;
211} 212}
212 213
213char *find_default_branch(struct cgit_repo *repo) 214char *find_default_branch(struct cgit_repo *repo)
214{ 215{
215 struct refmatch info; 216 struct refmatch info;
216 char *ref; 217 char *ref;
217 218
218 info.req_ref = repo->defbranch; 219 info.req_ref = repo->defbranch;
219 info.first_ref = NULL; 220 info.first_ref = NULL;
220 info.match = 0; 221 info.match = 0;
221 for_each_branch_ref(find_current_ref, &info); 222 for_each_branch_ref(find_current_ref, &info);
222 if (info.match) 223 if (info.match)
223 ref = info.req_ref; 224 ref = info.req_ref;
224 else 225 else
225 ref = info.first_ref; 226 ref = info.first_ref;
226 if (ref) 227 if (ref)
227 ref = xstrdup(ref); 228 ref = xstrdup(ref);
228 return ref; 229 return ref;
229} 230}
230 231
231static int prepare_repo_cmd(struct cgit_context *ctx) 232static int prepare_repo_cmd(struct cgit_context *ctx)
232{ 233{
233 char *tmp; 234 char *tmp;
234 unsigned char sha1[20]; 235 unsigned char sha1[20];
235 int nongit = 0; 236 int nongit = 0;
236 237
237 setenv("GIT_DIR", ctx->repo->path, 1); 238 setenv("GIT_DIR", ctx->repo->path, 1);
238 setup_git_directory_gently(&nongit); 239 setup_git_directory_gently(&nongit);
239 if (nongit) { 240 if (nongit) {
240 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 241 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
241 "config error"); 242 "config error");
242 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 243 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
243 ctx->repo = NULL; 244 ctx->repo = NULL;
244 cgit_print_http_headers(ctx); 245 cgit_print_http_headers(ctx);
245 cgit_print_docstart(ctx); 246 cgit_print_docstart(ctx);
246 cgit_print_pageheader(ctx); 247 cgit_print_pageheader(ctx);
247 cgit_print_error(tmp); 248 cgit_print_error(tmp);
248 cgit_print_docend(); 249 cgit_print_docend();
249 return 1; 250 return 1;
250 } 251 }
251 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 252 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
252 253
253 if (!ctx->qry.head) { 254 if (!ctx->qry.head) {
254 ctx->qry.head = find_default_branch(ctx->repo); 255 ctx->qry.head = find_default_branch(ctx->repo);
255 ctx->repo->defbranch = ctx->qry.head; 256 ctx->repo->defbranch = ctx->qry.head;
256 } 257 }
257 258
258 if (!ctx->qry.head) { 259 if (!ctx->qry.head) {
259 cgit_print_http_headers(ctx); 260 cgit_print_http_headers(ctx);
260 cgit_print_docstart(ctx); 261 cgit_print_docstart(ctx);
261 cgit_print_pageheader(ctx); 262 cgit_print_pageheader(ctx);
262 cgit_print_error("Repository seems to be empty"); 263 cgit_print_error("Repository seems to be empty");
263 cgit_print_docend(); 264 cgit_print_docend();
264 return 1; 265 return 1;
265 } 266 }
266 267
267 if (get_sha1(ctx->qry.head, sha1)) { 268 if (get_sha1(ctx->qry.head, sha1)) {
268 tmp = xstrdup(ctx->qry.head); 269 tmp = xstrdup(ctx->qry.head);
269 ctx->qry.head = ctx->repo->defbranch; 270 ctx->qry.head = ctx->repo->defbranch;
270 cgit_print_http_headers(ctx); 271 cgit_print_http_headers(ctx);
271 cgit_print_docstart(ctx); 272 cgit_print_docstart(ctx);
272 cgit_print_pageheader(ctx); 273 cgit_print_pageheader(ctx);
273 cgit_print_error(fmt("Invalid branch: %s", tmp)); 274 cgit_print_error(fmt("Invalid branch: %s", tmp));
274 cgit_print_docend(); 275 cgit_print_docend();
275 return 1; 276 return 1;
276 } 277 }
277 return 0; 278 return 0;
278} 279}
279 280
280static void process_request(void *cbdata) 281static void process_request(void *cbdata)
281{ 282{
282 struct cgit_context *ctx = cbdata; 283 struct cgit_context *ctx = cbdata;
283 struct cgit_cmd *cmd; 284 struct cgit_cmd *cmd;
284 285
285 cmd = cgit_get_cmd(ctx); 286 cmd = cgit_get_cmd(ctx);
diff --git a/cgit.h b/cgit.h
index b01fa31..e2af0c2 100644
--- a/cgit.h
+++ b/cgit.h
@@ -72,168 +72,169 @@ struct commitinfo {
72 struct commit *commit; 72 struct commit *commit;
73 char *author; 73 char *author;
74 char *author_email; 74 char *author_email;
75 unsigned long author_date; 75 unsigned long author_date;
76 char *committer; 76 char *committer;
77 char *committer_email; 77 char *committer_email;
78 unsigned long committer_date; 78 unsigned long committer_date;
79 char *subject; 79 char *subject;
80 char *msg; 80 char *msg;
81 char *msg_encoding; 81 char *msg_encoding;
82}; 82};
83 83
84struct taginfo { 84struct taginfo {
85 char *tagger; 85 char *tagger;
86 char *tagger_email; 86 char *tagger_email;
87 int tagger_date; 87 int tagger_date;
88 char *msg; 88 char *msg;
89}; 89};
90 90
91struct refinfo { 91struct refinfo {
92 const char *refname; 92 const char *refname;
93 struct object *object; 93 struct object *object;
94 union { 94 union {
95 struct taginfo *tag; 95 struct taginfo *tag;
96 struct commitinfo *commit; 96 struct commitinfo *commit;
97 }; 97 };
98}; 98};
99 99
100struct reflist { 100struct reflist {
101 struct refinfo **refs; 101 struct refinfo **refs;
102 int alloc; 102 int alloc;
103 int count; 103 int count;
104}; 104};
105 105
106struct cgit_query { 106struct cgit_query {
107 int has_symref; 107 int has_symref;
108 int has_sha1; 108 int has_sha1;
109 char *raw; 109 char *raw;
110 char *repo; 110 char *repo;
111 char *page; 111 char *page;
112 char *search; 112 char *search;
113 char *grep; 113 char *grep;
114 char *head; 114 char *head;
115 char *sha1; 115 char *sha1;
116 char *sha2; 116 char *sha2;
117 char *path; 117 char *path;
118 char *name; 118 char *name;
119 char *mimetype; 119 char *mimetype;
120 int ofs; 120 int ofs;
121}; 121};
122 122
123struct cgit_config { 123struct cgit_config {
124 char *agefile; 124 char *agefile;
125 char *cache_root; 125 char *cache_root;
126 char *clone_prefix; 126 char *clone_prefix;
127 char *css; 127 char *css;
128 char *favicon; 128 char *favicon;
129 char *footer; 129 char *footer;
130 char *index_header; 130 char *index_header;
131 char *index_info; 131 char *index_info;
132 char *logo; 132 char *logo;
133 char *logo_link; 133 char *logo_link;
134 char *module_link; 134 char *module_link;
135 char *repo_group; 135 char *repo_group;
136 char *robots; 136 char *robots;
137 char *root_title; 137 char *root_title;
138 char *root_desc; 138 char *root_desc;
139 char *root_readme; 139 char *root_readme;
140 char *script_name; 140 char *script_name;
141 char *virtual_root; 141 char *virtual_root;
142 int cache_size; 142 int cache_size;
143 int cache_dynamic_ttl; 143 int cache_dynamic_ttl;
144 int cache_max_create_time; 144 int cache_max_create_time;
145 int cache_repo_ttl; 145 int cache_repo_ttl;
146 int cache_root_ttl; 146 int cache_root_ttl;
147 int cache_static_ttl; 147 int cache_static_ttl;
148 int enable_index_links; 148 int enable_index_links;
149 int enable_log_filecount; 149 int enable_log_filecount;
150 int enable_log_linecount; 150 int enable_log_linecount;
151 int local_time; 151 int local_time;
152 int max_repo_count; 152 int max_repo_count;
153 int max_commit_count; 153 int max_commit_count;
154 int max_lock_attempts; 154 int max_lock_attempts;
155 int max_msg_len; 155 int max_msg_len;
156 int max_repodesc_len; 156 int max_repodesc_len;
157 int nocache; 157 int nocache;
158 int renamelimit; 158 int renamelimit;
159 int snapshots; 159 int snapshots;
160 int summary_branches; 160 int summary_branches;
161 int summary_log; 161 int summary_log;
162 int summary_tags; 162 int summary_tags;
163}; 163};
164 164
165struct cgit_page { 165struct cgit_page {
166 time_t modified; 166 time_t modified;
167 time_t expires; 167 time_t expires;
168 size_t size;
168 char *mimetype; 169 char *mimetype;
169 char *charset; 170 char *charset;
170 char *filename; 171 char *filename;
171 char *title; 172 char *title;
172}; 173};
173 174
174struct cgit_context { 175struct cgit_context {
175 struct cgit_query qry; 176 struct cgit_query qry;
176 struct cgit_config cfg; 177 struct cgit_config cfg;
177 struct cgit_repo *repo; 178 struct cgit_repo *repo;
178 struct cgit_page page; 179 struct cgit_page page;
179}; 180};
180 181
181struct cgit_snapshot_format { 182struct cgit_snapshot_format {
182 const char *suffix; 183 const char *suffix;
183 const char *mimetype; 184 const char *mimetype;
184 write_archive_fn_t write_func; 185 write_archive_fn_t write_func;
185 int bit; 186 int bit;
186}; 187};
187 188
188extern const char *cgit_version; 189extern const char *cgit_version;
189 190
190extern struct cgit_repolist cgit_repolist; 191extern struct cgit_repolist cgit_repolist;
191extern struct cgit_context ctx; 192extern struct cgit_context ctx;
192extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 193extern const struct cgit_snapshot_format cgit_snapshot_formats[];
193 194
194extern struct cgit_repo *cgit_add_repo(const char *url); 195extern struct cgit_repo *cgit_add_repo(const char *url);
195extern struct cgit_repo *cgit_get_repoinfo(const char *url); 196extern struct cgit_repo *cgit_get_repoinfo(const char *url);
196extern void cgit_repo_config_cb(const char *name, const char *value); 197extern void cgit_repo_config_cb(const char *name, const char *value);
197 198
198extern int chk_zero(int result, char *msg); 199extern int chk_zero(int result, char *msg);
199extern int chk_positive(int result, char *msg); 200extern int chk_positive(int result, char *msg);
200extern int chk_non_negative(int result, char *msg); 201extern int chk_non_negative(int result, char *msg);
201 202
202extern char *trim_end(const char *str, char c); 203extern char *trim_end(const char *str, char c);
203extern char *strlpart(char *txt, int maxlen); 204extern char *strlpart(char *txt, int maxlen);
204extern char *strrpart(char *txt, int maxlen); 205extern char *strrpart(char *txt, int maxlen);
205 206
206extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 207extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
207extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 208extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
208 int flags, void *cb_data); 209 int flags, void *cb_data);
209 210
210extern void *cgit_free_commitinfo(struct commitinfo *info); 211extern void *cgit_free_commitinfo(struct commitinfo *info);
211 212
212extern int cgit_diff_files(const unsigned char *old_sha1, 213extern int cgit_diff_files(const unsigned char *old_sha1,
213 const unsigned char *new_sha1, 214 const unsigned char *new_sha1,
214 linediff_fn fn); 215 linediff_fn fn);
215 216
216extern void cgit_diff_tree(const unsigned char *old_sha1, 217extern void cgit_diff_tree(const unsigned char *old_sha1,
217 const unsigned char *new_sha1, 218 const unsigned char *new_sha1,
218 filepair_fn fn, const char *prefix); 219 filepair_fn fn, const char *prefix);
219 220
220extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 221extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
221 222
222extern char *fmt(const char *format,...); 223extern char *fmt(const char *format,...);
223 224
224extern struct commitinfo *cgit_parse_commit(struct commit *commit); 225extern struct commitinfo *cgit_parse_commit(struct commit *commit);
225extern struct taginfo *cgit_parse_tag(struct tag *tag); 226extern struct taginfo *cgit_parse_tag(struct tag *tag);
226extern void cgit_parse_url(const char *url); 227extern void cgit_parse_url(const char *url);
227 228
228extern const char *cgit_repobasename(const char *reponame); 229extern const char *cgit_repobasename(const char *reponame);
229 230
230extern int cgit_parse_snapshots_mask(const char *str); 231extern int cgit_parse_snapshots_mask(const char *str);
231 232
232/* libgit.a either links against or compiles its own implementation of 233/* libgit.a either links against or compiles its own implementation of
233 * strcasestr(), and we'd like to reuse it. Simply re-declaring it 234 * strcasestr(), and we'd like to reuse it. Simply re-declaring it
234 * seems to do the trick. 235 * seems to do the trick.
235 */ 236 */
236extern char *strcasestr(const char *haystack, const char *needle); 237extern char *strcasestr(const char *haystack, const char *needle);
237 238
238 239
239#endif /* CGIT_H */ 240#endif /* CGIT_H */
diff --git a/cmd.c b/cmd.c
index 03e165c..2b34189 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,151 +1,158 @@
1/* cmd.c: the cgit command dispatcher 1/* cmd.c: the cgit command dispatcher
2 * 2 *
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 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#include "cmd.h" 10#include "cmd.h"
11#include "cache.h" 11#include "cache.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13#include "ui-blob.h" 13#include "ui-blob.h"
14#include "ui-clone.h" 14#include "ui-clone.h"
15#include "ui-commit.h" 15#include "ui-commit.h"
16#include "ui-diff.h" 16#include "ui-diff.h"
17#include "ui-log.h" 17#include "ui-log.h"
18#include "ui-patch.h" 18#include "ui-patch.h"
19#include "ui-plain.h"
19#include "ui-refs.h" 20#include "ui-refs.h"
20#include "ui-repolist.h" 21#include "ui-repolist.h"
21#include "ui-snapshot.h" 22#include "ui-snapshot.h"
22#include "ui-summary.h" 23#include "ui-summary.h"
23#include "ui-tag.h" 24#include "ui-tag.h"
24#include "ui-tree.h" 25#include "ui-tree.h"
25 26
26static void HEAD_fn(struct cgit_context *ctx) 27static void HEAD_fn(struct cgit_context *ctx)
27{ 28{
28 cgit_clone_head(ctx); 29 cgit_clone_head(ctx);
29} 30}
30 31
31static void about_fn(struct cgit_context *ctx) 32static void about_fn(struct cgit_context *ctx)
32{ 33{
33 if (ctx->repo) 34 if (ctx->repo)
34 cgit_print_repo_readme(); 35 cgit_print_repo_readme();
35 else 36 else
36 cgit_print_site_readme(); 37 cgit_print_site_readme();
37} 38}
38 39
39static void blob_fn(struct cgit_context *ctx) 40static void blob_fn(struct cgit_context *ctx)
40{ 41{
41 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head); 42 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head);
42} 43}
43 44
44static void commit_fn(struct cgit_context *ctx) 45static void commit_fn(struct cgit_context *ctx)
45{ 46{
46 cgit_print_commit(ctx->qry.sha1); 47 cgit_print_commit(ctx->qry.sha1);
47} 48}
48 49
49static void diff_fn(struct cgit_context *ctx) 50static void diff_fn(struct cgit_context *ctx)
50{ 51{
51 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); 52 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
52} 53}
53 54
54static void info_fn(struct cgit_context *ctx) 55static void info_fn(struct cgit_context *ctx)
55{ 56{
56 cgit_clone_info(ctx); 57 cgit_clone_info(ctx);
57} 58}
58 59
59static void log_fn(struct cgit_context *ctx) 60static void log_fn(struct cgit_context *ctx)
60{ 61{
61 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, 62 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
62 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); 63 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
63} 64}
64 65
65static void ls_cache_fn(struct cgit_context *ctx) 66static void ls_cache_fn(struct cgit_context *ctx)
66{ 67{
67 ctx->page.mimetype = "text/plain"; 68 ctx->page.mimetype = "text/plain";
68 ctx->page.filename = "ls-cache.txt"; 69 ctx->page.filename = "ls-cache.txt";
69 cgit_print_http_headers(ctx); 70 cgit_print_http_headers(ctx);
70 cache_ls(ctx->cfg.cache_root); 71 cache_ls(ctx->cfg.cache_root);
71} 72}
72 73
73static void objects_fn(struct cgit_context *ctx) 74static void objects_fn(struct cgit_context *ctx)
74{ 75{
75 cgit_clone_objects(ctx); 76 cgit_clone_objects(ctx);
76} 77}
77 78
78static void repolist_fn(struct cgit_context *ctx) 79static void repolist_fn(struct cgit_context *ctx)
79{ 80{
80 cgit_print_repolist(); 81 cgit_print_repolist();
81} 82}
82 83
83static void patch_fn(struct cgit_context *ctx) 84static void patch_fn(struct cgit_context *ctx)
84{ 85{
85 cgit_print_patch(ctx->qry.sha1); 86 cgit_print_patch(ctx->qry.sha1);
86} 87}
87 88
89static void plain_fn(struct cgit_context *ctx)
90{
91 cgit_print_plain(ctx);
92}
93
88static void refs_fn(struct cgit_context *ctx) 94static void refs_fn(struct cgit_context *ctx)
89{ 95{
90 cgit_print_refs(); 96 cgit_print_refs();
91} 97}
92 98
93static void snapshot_fn(struct cgit_context *ctx) 99static void snapshot_fn(struct cgit_context *ctx)
94{ 100{
95 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, 101 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1,
96 cgit_repobasename(ctx->repo->url), ctx->qry.path, 102 cgit_repobasename(ctx->repo->url), ctx->qry.path,
97 ctx->repo->snapshots); 103 ctx->repo->snapshots);
98} 104}
99 105
100static void summary_fn(struct cgit_context *ctx) 106static void summary_fn(struct cgit_context *ctx)
101{ 107{
102 cgit_print_summary(); 108 cgit_print_summary();
103} 109}
104 110
105static void tag_fn(struct cgit_context *ctx) 111static void tag_fn(struct cgit_context *ctx)
106{ 112{
107 cgit_print_tag(ctx->qry.sha1); 113 cgit_print_tag(ctx->qry.sha1);
108} 114}
109 115
110static void tree_fn(struct cgit_context *ctx) 116static void tree_fn(struct cgit_context *ctx)
111{ 117{
112 cgit_print_tree(ctx->qry.sha1, ctx->qry.path); 118 cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
113} 119}
114 120
115#define def_cmd(name, want_repo, want_layout) \ 121#define def_cmd(name, want_repo, want_layout) \
116 {#name, name##_fn, want_repo, want_layout} 122 {#name, name##_fn, want_repo, want_layout}
117 123
118struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx) 124struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
119{ 125{
120 static struct cgit_cmd cmds[] = { 126 static struct cgit_cmd cmds[] = {
121 def_cmd(HEAD, 1, 0), 127 def_cmd(HEAD, 1, 0),
122 def_cmd(about, 0, 1), 128 def_cmd(about, 0, 1),
123 def_cmd(blob, 1, 0), 129 def_cmd(blob, 1, 0),
124 def_cmd(commit, 1, 1), 130 def_cmd(commit, 1, 1),
125 def_cmd(diff, 1, 1), 131 def_cmd(diff, 1, 1),
126 def_cmd(info, 1, 0), 132 def_cmd(info, 1, 0),
127 def_cmd(log, 1, 1), 133 def_cmd(log, 1, 1),
128 def_cmd(ls_cache, 0, 0), 134 def_cmd(ls_cache, 0, 0),
129 def_cmd(objects, 1, 0), 135 def_cmd(objects, 1, 0),
130 def_cmd(patch, 1, 0), 136 def_cmd(patch, 1, 0),
137 def_cmd(plain, 1, 0),
131 def_cmd(refs, 1, 1), 138 def_cmd(refs, 1, 1),
132 def_cmd(repolist, 0, 0), 139 def_cmd(repolist, 0, 0),
133 def_cmd(snapshot, 1, 0), 140 def_cmd(snapshot, 1, 0),
134 def_cmd(summary, 1, 1), 141 def_cmd(summary, 1, 1),
135 def_cmd(tag, 1, 1), 142 def_cmd(tag, 1, 1),
136 def_cmd(tree, 1, 1), 143 def_cmd(tree, 1, 1),
137 }; 144 };
138 int i; 145 int i;
139 146
140 if (ctx->qry.page == NULL) { 147 if (ctx->qry.page == NULL) {
141 if (ctx->repo) 148 if (ctx->repo)
142 ctx->qry.page = "summary"; 149 ctx->qry.page = "summary";
143 else 150 else
144 ctx->qry.page = "repolist"; 151 ctx->qry.page = "repolist";
145 } 152 }
146 153
147 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) 154 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
148 if (!strcmp(ctx->qry.page, cmds[i].name)) 155 if (!strcmp(ctx->qry.page, cmds[i].name))
149 return &cmds[i]; 156 return &cmds[i];
150 return NULL; 157 return NULL;
151} 158}
diff --git a/html.c b/html.c
index 1237076..83fc7a9 100644
--- a/html.c
+++ b/html.c
@@ -1,133 +1,138 @@
1/* html.c: helper functions for html output 1/* html.c: helper functions for html 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 <unistd.h> 9#include <unistd.h>
10#include <stdio.h> 10#include <stdio.h>
11#include <stdlib.h> 11#include <stdlib.h>
12#include <stdarg.h> 12#include <stdarg.h>
13#include <string.h> 13#include <string.h>
14#include <errno.h> 14#include <errno.h>
15 15
16int htmlfd = STDOUT_FILENO; 16int htmlfd = STDOUT_FILENO;
17 17
18char *fmt(const char *format, ...) 18char *fmt(const char *format, ...)
19{ 19{
20 static char buf[8][1024]; 20 static char buf[8][1024];
21 static int bufidx; 21 static int bufidx;
22 int len; 22 int len;
23 va_list args; 23 va_list args;
24 24
25 bufidx++; 25 bufidx++;
26 bufidx &= 7; 26 bufidx &= 7;
27 27
28 va_start(args, format); 28 va_start(args, format);
29 len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args); 29 len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
30 va_end(args); 30 va_end(args);
31 if (len>sizeof(buf[bufidx])) { 31 if (len>sizeof(buf[bufidx])) {
32 fprintf(stderr, "[html.c] string truncated: %s\n", format); 32 fprintf(stderr, "[html.c] string truncated: %s\n", format);
33 exit(1); 33 exit(1);
34 } 34 }
35 return buf[bufidx]; 35 return buf[bufidx];
36} 36}
37 37
38void html_raw(const char *data, size_t size)
39{
40 write(htmlfd, data, size);
41}
42
38void html(const char *txt) 43void html(const char *txt)
39{ 44{
40 write(htmlfd, txt, strlen(txt)); 45 write(htmlfd, txt, strlen(txt));
41} 46}
42 47
43void htmlf(const char *format, ...) 48void htmlf(const char *format, ...)
44{ 49{
45 static char buf[65536]; 50 static char buf[65536];
46 va_list args; 51 va_list args;
47 52
48 va_start(args, format); 53 va_start(args, format);
49 vsnprintf(buf, sizeof(buf), format, args); 54 vsnprintf(buf, sizeof(buf), format, args);
50 va_end(args); 55 va_end(args);
51 html(buf); 56 html(buf);
52} 57}
53 58
54void html_status(int code, int more_headers) 59void html_status(int code, int more_headers)
55{ 60{
56 htmlf("Status: %d\n", code); 61 htmlf("Status: %d\n", code);
57 if (!more_headers) 62 if (!more_headers)
58 html("\n"); 63 html("\n");
59} 64}
60 65
61void html_txt(char *txt) 66void html_txt(char *txt)
62{ 67{
63 char *t = txt; 68 char *t = txt;
64 while(t && *t){ 69 while(t && *t){
65 int c = *t; 70 int c = *t;
66 if (c=='<' || c=='>' || c=='&') { 71 if (c=='<' || c=='>' || c=='&') {
67 write(htmlfd, txt, t - txt); 72 write(htmlfd, txt, t - txt);
68 if (c=='>') 73 if (c=='>')
69 html("&gt;"); 74 html("&gt;");
70 else if (c=='<') 75 else if (c=='<')
71 html("&lt;"); 76 html("&lt;");
72 else if (c=='&') 77 else if (c=='&')
73 html("&amp;"); 78 html("&amp;");
74 txt = t+1; 79 txt = t+1;
75 } 80 }
76 t++; 81 t++;
77 } 82 }
78 if (t!=txt) 83 if (t!=txt)
79 html(txt); 84 html(txt);
80} 85}
81 86
82void html_ntxt(int len, char *txt) 87void html_ntxt(int len, char *txt)
83{ 88{
84 char *t = txt; 89 char *t = txt;
85 while(t && *t && len--){ 90 while(t && *t && len--){
86 int c = *t; 91 int c = *t;
87 if (c=='<' || c=='>' || c=='&') { 92 if (c=='<' || c=='>' || c=='&') {
88 write(htmlfd, txt, t - txt); 93 write(htmlfd, txt, t - txt);
89 if (c=='>') 94 if (c=='>')
90 html("&gt;"); 95 html("&gt;");
91 else if (c=='<') 96 else if (c=='<')
92 html("&lt;"); 97 html("&lt;");
93 else if (c=='&') 98 else if (c=='&')
94 html("&amp;"); 99 html("&amp;");
95 txt = t+1; 100 txt = t+1;
96 } 101 }
97 t++; 102 t++;
98 } 103 }
99 if (t!=txt) 104 if (t!=txt)
100 write(htmlfd, txt, t - txt); 105 write(htmlfd, txt, t - txt);
101 if (len<0) 106 if (len<0)
102 html("..."); 107 html("...");
103} 108}
104 109
105void html_attr(char *txt) 110void html_attr(char *txt)
106{ 111{
107 char *t = txt; 112 char *t = txt;
108 while(t && *t){ 113 while(t && *t){
109 int c = *t; 114 int c = *t;
110 if (c=='<' || c=='>' || c=='\'') { 115 if (c=='<' || c=='>' || c=='\'') {
111 write(htmlfd, txt, t - txt); 116 write(htmlfd, txt, t - txt);
112 if (c=='>') 117 if (c=='>')
113 html("&gt;"); 118 html("&gt;");
114 else if (c=='<') 119 else if (c=='<')
115 html("&lt;"); 120 html("&lt;");
116 else if (c=='\'') 121 else if (c=='\'')
117 html("&quote;"); 122 html("&quote;");
118 txt = t+1; 123 txt = t+1;
119 } 124 }
120 t++; 125 t++;
121 } 126 }
122 if (t!=txt) 127 if (t!=txt)
123 html(txt); 128 html(txt);
124} 129}
125 130
126void html_hidden(char *name, char *value) 131void html_hidden(char *name, char *value)
127{ 132{
128 html("<input type='hidden' name='"); 133 html("<input type='hidden' name='");
129 html_attr(name); 134 html_attr(name);
130 html("' value='"); 135 html("' value='");
131 html_attr(value); 136 html_attr(value);
132 html("'/>"); 137 html("'/>");
133} 138}
diff --git a/html.h b/html.h
index 2bde28d..49462a2 100644
--- a/html.h
+++ b/html.h
@@ -1,21 +1,22 @@
1#ifndef HTML_H 1#ifndef HTML_H
2#define HTML_H 2#define HTML_H
3 3
4extern int htmlfd; 4extern int htmlfd;
5 5
6extern void html_raw(const char *txt, size_t size);
6extern void html(const char *txt); 7extern void html(const char *txt);
7extern void htmlf(const char *format,...); 8extern void htmlf(const char *format,...);
8extern void html_status(int code, int more_headers); 9extern void html_status(int code, int more_headers);
9extern void html_txt(char *txt); 10extern void html_txt(char *txt);
10extern void html_ntxt(int len, char *txt); 11extern void html_ntxt(int len, char *txt);
11extern void html_attr(char *txt); 12extern void html_attr(char *txt);
12extern void html_hidden(char *name, char *value); 13extern void html_hidden(char *name, char *value);
13extern void html_option(char *value, char *text, char *selected_value); 14extern void html_option(char *value, char *text, char *selected_value);
14extern void html_link_open(char *url, char *title, char *class); 15extern void html_link_open(char *url, char *title, char *class);
15extern void html_link_close(void); 16extern void html_link_close(void);
16extern void html_fileperm(unsigned short mode); 17extern void html_fileperm(unsigned short mode);
17extern int html_include(const char *filename); 18extern int html_include(const char *filename);
18 19
19extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value)); 20extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value));
20 21
21#endif /* HTML_H */ 22#endif /* HTML_H */
diff --git a/ui-plain.c b/ui-plain.c
new file mode 100644
index 0000000..28deae5
--- a/dev/null
+++ b/ui-plain.c
@@ -0,0 +1,82 @@
1/* ui-plain.c: functions for output of plain blobs by path
2 *
3 * Copyright (C) 2008 Lars Hjemli
4 *
5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text)
7 */
8
9#include "cgit.h"
10#include "html.h"
11#include "ui-shared.h"
12
13char *curr_rev;
14char *match_path;
15int match;
16
17static void print_object(const unsigned char *sha1, const char *path)
18{
19 enum object_type type;
20 char *buf;
21 size_t size;
22
23 type = sha1_object_info(sha1, &size);
24 if (type == OBJ_BAD) {
25 html_status(404, 0);
26 return;
27 }
28
29 buf = read_sha1_file(sha1, &type, &size);
30 if (!buf) {
31 html_status(404, 0);
32 return;
33 }
34 ctx.page.mimetype = "text/plain";
35 ctx.page.filename = fmt("%s", path);
36 ctx.page.size = size;
37 cgit_print_http_headers(&ctx);
38 html_raw(buf, size);
39 match = 1;
40}
41
42static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
43 const char *pathname, unsigned mode, int stage,
44 void *cbdata)
45{
46 fprintf(stderr, "[cgit] walk_tree.pathname=%s", pathname);
47
48 if (!pathname || strcmp(match_path, pathname))
49 return READ_TREE_RECURSIVE;
50
51 if (S_ISREG(mode))
52 print_object(sha1, pathname);
53
54 return 0;
55}
56
57void cgit_print_plain(struct cgit_context *ctx)
58{
59 const char *rev = ctx->qry.sha1;
60 unsigned char sha1[20];
61 struct commit *commit;
62 const char *paths[] = {ctx->qry.path, NULL};
63
64 if (!rev)
65 rev = ctx->qry.head;
66
67 curr_rev = xstrdup(rev);
68 if (get_sha1(rev, sha1)) {
69 html_status(404, 0);
70 return;
71 }
72 commit = lookup_commit_reference(sha1);
73 if (!commit || parse_commit(commit)) {
74 html_status(404, 0);
75 return;
76 }
77 match_path = ctx->qry.path;
78 fprintf(stderr, "[cgit] match_path=%s", match_path);
79 read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL);
80 if (!match)
81 html_status(404, 0);
82}
diff --git a/ui-plain.h b/ui-plain.h
new file mode 100644
index 0000000..4373118
--- a/dev/null
+++ b/ui-plain.h
@@ -0,0 +1,6 @@
1#ifndef UI_PLAIN_H
2#define UI_PLAIN_H
3
4extern void cgit_print_plain(struct cgit_context *ctx);
5
6#endif /* UI_PLAIN_H */
diff --git a/ui-shared.c b/ui-shared.c
index 197ee37..4408969 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -325,192 +325,194 @@ void cgit_patch_link(char *name, char *title, char *class, char *head,
325{ 325{
326 reporevlink("patch", name, title, class, head, rev, NULL); 326 reporevlink("patch", name, title, class, head, rev, NULL);
327} 327}
328 328
329void cgit_object_link(struct object *obj) 329void cgit_object_link(struct object *obj)
330{ 330{
331 char *page, *arg, *url; 331 char *page, *arg, *url;
332 332
333 if (obj->type == OBJ_COMMIT) { 333 if (obj->type == OBJ_COMMIT) {
334 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 334 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
335 ctx.qry.head, sha1_to_hex(obj->sha1)); 335 ctx.qry.head, sha1_to_hex(obj->sha1));
336 return; 336 return;
337 } else if (obj->type == OBJ_TREE) { 337 } else if (obj->type == OBJ_TREE) {
338 page = "tree"; 338 page = "tree";
339 arg = "id"; 339 arg = "id";
340 } else if (obj->type == OBJ_TAG) { 340 } else if (obj->type == OBJ_TAG) {
341 page = "tag"; 341 page = "tag";
342 arg = "id"; 342 arg = "id";
343 } else { 343 } else {
344 page = "blob"; 344 page = "blob";
345 arg = "id"; 345 arg = "id";
346 } 346 }
347 347
348 url = cgit_pageurl(ctx.qry.repo, page, 348 url = cgit_pageurl(ctx.qry.repo, page,
349 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 349 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
350 html_link_open(url, NULL, NULL); 350 html_link_open(url, NULL, NULL);
351 htmlf("%s %s", typename(obj->type), 351 htmlf("%s %s", typename(obj->type),
352 sha1_to_hex(obj->sha1)); 352 sha1_to_hex(obj->sha1));
353 html_link_close(); 353 html_link_close();
354} 354}
355 355
356void cgit_print_date(time_t secs, char *format, int local_time) 356void cgit_print_date(time_t secs, char *format, int local_time)
357{ 357{
358 char buf[64]; 358 char buf[64];
359 struct tm *time; 359 struct tm *time;
360 360
361 if (!secs) 361 if (!secs)
362 return; 362 return;
363 if(local_time) 363 if(local_time)
364 time = localtime(&secs); 364 time = localtime(&secs);
365 else 365 else
366 time = gmtime(&secs); 366 time = gmtime(&secs);
367 strftime(buf, sizeof(buf)-1, format, time); 367 strftime(buf, sizeof(buf)-1, format, time);
368 html_txt(buf); 368 html_txt(buf);
369} 369}
370 370
371void cgit_print_age(time_t t, time_t max_relative, char *format) 371void cgit_print_age(time_t t, time_t max_relative, char *format)
372{ 372{
373 time_t now, secs; 373 time_t now, secs;
374 374
375 if (!t) 375 if (!t)
376 return; 376 return;
377 time(&now); 377 time(&now);
378 secs = now - t; 378 secs = now - t;
379 379
380 if (secs > max_relative && max_relative >= 0) { 380 if (secs > max_relative && max_relative >= 0) {
381 cgit_print_date(t, format, ctx.cfg.local_time); 381 cgit_print_date(t, format, ctx.cfg.local_time);
382 return; 382 return;
383 } 383 }
384 384
385 if (secs < TM_HOUR * 2) { 385 if (secs < TM_HOUR * 2) {
386 htmlf("<span class='age-mins'>%.0f min.</span>", 386 htmlf("<span class='age-mins'>%.0f min.</span>",
387 secs * 1.0 / TM_MIN); 387 secs * 1.0 / TM_MIN);
388 return; 388 return;
389 } 389 }
390 if (secs < TM_DAY * 2) { 390 if (secs < TM_DAY * 2) {
391 htmlf("<span class='age-hours'>%.0f hours</span>", 391 htmlf("<span class='age-hours'>%.0f hours</span>",
392 secs * 1.0 / TM_HOUR); 392 secs * 1.0 / TM_HOUR);
393 return; 393 return;
394 } 394 }
395 if (secs < TM_WEEK * 2) { 395 if (secs < TM_WEEK * 2) {
396 htmlf("<span class='age-days'>%.0f days</span>", 396 htmlf("<span class='age-days'>%.0f days</span>",
397 secs * 1.0 / TM_DAY); 397 secs * 1.0 / TM_DAY);
398 return; 398 return;
399 } 399 }
400 if (secs < TM_MONTH * 2) { 400 if (secs < TM_MONTH * 2) {
401 htmlf("<span class='age-weeks'>%.0f weeks</span>", 401 htmlf("<span class='age-weeks'>%.0f weeks</span>",
402 secs * 1.0 / TM_WEEK); 402 secs * 1.0 / TM_WEEK);
403 return; 403 return;
404 } 404 }
405 if (secs < TM_YEAR * 2) { 405 if (secs < TM_YEAR * 2) {
406 htmlf("<span class='age-months'>%.0f months</span>", 406 htmlf("<span class='age-months'>%.0f months</span>",
407 secs * 1.0 / TM_MONTH); 407 secs * 1.0 / TM_MONTH);
408 return; 408 return;
409 } 409 }
410 htmlf("<span class='age-years'>%.0f years</span>", 410 htmlf("<span class='age-years'>%.0f years</span>",
411 secs * 1.0 / TM_YEAR); 411 secs * 1.0 / TM_YEAR);
412} 412}
413 413
414void cgit_print_http_headers(struct cgit_context *ctx) 414void cgit_print_http_headers(struct cgit_context *ctx)
415{ 415{
416 if (ctx->page.mimetype && ctx->page.charset) 416 if (ctx->page.mimetype && ctx->page.charset)
417 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 417 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
418 ctx->page.charset); 418 ctx->page.charset);
419 else if (ctx->page.mimetype) 419 else if (ctx->page.mimetype)
420 htmlf("Content-Type: %s\n", ctx->page.mimetype); 420 htmlf("Content-Type: %s\n", ctx->page.mimetype);
421 if (ctx->page.size)
422 htmlf("Content-Length: %ld\n", ctx->page.size);
421 if (ctx->page.filename) 423 if (ctx->page.filename)
422 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 424 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
423 ctx->page.filename); 425 ctx->page.filename);
424 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 426 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
425 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 427 htmlf("Expires: %s\n", http_date(ctx->page.expires));
426 html("\n"); 428 html("\n");
427} 429}
428 430
429void cgit_print_docstart(struct cgit_context *ctx) 431void cgit_print_docstart(struct cgit_context *ctx)
430{ 432{
431 html(cgit_doctype); 433 html(cgit_doctype);
432 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 434 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
433 html("<head>\n"); 435 html("<head>\n");
434 html("<title>"); 436 html("<title>");
435 html_txt(ctx->page.title); 437 html_txt(ctx->page.title);
436 html("</title>\n"); 438 html("</title>\n");
437 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 439 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
438 if (ctx->cfg.robots && *ctx->cfg.robots) 440 if (ctx->cfg.robots && *ctx->cfg.robots)
439 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 441 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
440 html("<link rel='stylesheet' type='text/css' href='"); 442 html("<link rel='stylesheet' type='text/css' href='");
441 html_attr(ctx->cfg.css); 443 html_attr(ctx->cfg.css);
442 html("'/>\n"); 444 html("'/>\n");
443 if (ctx->cfg.favicon) { 445 if (ctx->cfg.favicon) {
444 html("<link rel='shortcut icon' href='"); 446 html("<link rel='shortcut icon' href='");
445 html_attr(ctx->cfg.favicon); 447 html_attr(ctx->cfg.favicon);
446 html("'/>\n"); 448 html("'/>\n");
447 } 449 }
448 html("</head>\n"); 450 html("</head>\n");
449 html("<body>\n"); 451 html("<body>\n");
450} 452}
451 453
452void cgit_print_docend() 454void cgit_print_docend()
453{ 455{
454 html("</div>"); 456 html("</div>");
455 if (ctx.cfg.footer) 457 if (ctx.cfg.footer)
456 html_include(ctx.cfg.footer); 458 html_include(ctx.cfg.footer);
457 else { 459 else {
458 html("<div class='footer'>generated "); 460 html("<div class='footer'>generated ");
459 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 461 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
460 htmlf(" by cgit %s", cgit_version); 462 htmlf(" by cgit %s", cgit_version);
461 html("</div>\n"); 463 html("</div>\n");
462 } 464 }
463 html("</body>\n</html>\n"); 465 html("</body>\n</html>\n");
464} 466}
465 467
466int print_branch_option(const char *refname, const unsigned char *sha1, 468int print_branch_option(const char *refname, const unsigned char *sha1,
467 int flags, void *cb_data) 469 int flags, void *cb_data)
468{ 470{
469 char *name = (char *)refname; 471 char *name = (char *)refname;
470 html_option(name, name, ctx.qry.head); 472 html_option(name, name, ctx.qry.head);
471 return 0; 473 return 0;
472} 474}
473 475
474int print_archive_ref(const char *refname, const unsigned char *sha1, 476int print_archive_ref(const char *refname, const unsigned char *sha1,
475 int flags, void *cb_data) 477 int flags, void *cb_data)
476{ 478{
477 struct tag *tag; 479 struct tag *tag;
478 struct taginfo *info; 480 struct taginfo *info;
479 struct object *obj; 481 struct object *obj;
480 char buf[256], *url; 482 char buf[256], *url;
481 unsigned char fileid[20]; 483 unsigned char fileid[20];
482 int *header = (int *)cb_data; 484 int *header = (int *)cb_data;
483 485
484 if (prefixcmp(refname, "refs/archives")) 486 if (prefixcmp(refname, "refs/archives"))
485 return 0; 487 return 0;
486 strncpy(buf, refname+14, sizeof(buf)); 488 strncpy(buf, refname+14, sizeof(buf));
487 obj = parse_object(sha1); 489 obj = parse_object(sha1);
488 if (!obj) 490 if (!obj)
489 return 1; 491 return 1;
490 if (obj->type == OBJ_TAG) { 492 if (obj->type == OBJ_TAG) {
491 tag = lookup_tag(sha1); 493 tag = lookup_tag(sha1);
492 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 494 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
493 return 0; 495 return 0;
494 hashcpy(fileid, tag->tagged->sha1); 496 hashcpy(fileid, tag->tagged->sha1);
495 } else if (obj->type != OBJ_BLOB) { 497 } else if (obj->type != OBJ_BLOB) {
496 return 0; 498 return 0;
497 } else { 499 } else {
498 hashcpy(fileid, sha1); 500 hashcpy(fileid, sha1);
499 } 501 }
500 if (!*header) { 502 if (!*header) {
501 html("<h1>download</h1>\n"); 503 html("<h1>download</h1>\n");
502 *header = 1; 504 *header = 1;
503 } 505 }
504 url = cgit_pageurl(ctx.qry.repo, "blob", 506 url = cgit_pageurl(ctx.qry.repo, "blob",
505 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 507 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
506 buf)); 508 buf));
507 html_link_open(url, NULL, "menu"); 509 html_link_open(url, NULL, "menu");
508 html_txt(strlpart(buf, 20)); 510 html_txt(strlpart(buf, 20));
509 html_link_close(); 511 html_link_close();
510 return 0; 512 return 0;
511} 513}
512 514
513void add_hidden_formfields(int incl_head, int incl_search, char *page) 515void add_hidden_formfields(int incl_head, int incl_search, char *page)
514{ 516{
515 char *url; 517 char *url;
516 518