summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c13
-rw-r--r--cgit.h3
-rw-r--r--cgitrc.5.txt21
-rw-r--r--ui-atom.c2
-rw-r--r--ui-commit.c12
-rw-r--r--ui-patch.c6
-rw-r--r--ui-plain.c20
-rw-r--r--ui-tag.c2
-rw-r--r--ui-tree.c8
9 files changed, 74 insertions, 13 deletions
diff --git a/cgit.c b/cgit.c
index cb1149d..7b228af 100644
--- a/cgit.c
+++ b/cgit.c
@@ -8,24 +8,32 @@
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cache.h" 10#include "cache.h"
11#include "cmd.h" 11#include "cmd.h"
12#include "configfile.h" 12#include "configfile.h"
13#include "html.h" 13#include "html.h"
14#include "ui-shared.h" 14#include "ui-shared.h"
15#include "ui-stats.h" 15#include "ui-stats.h"
16#include "scan-tree.h" 16#include "scan-tree.h"
17 17
18const char *cgit_version = CGIT_VERSION; 18const char *cgit_version = CGIT_VERSION;
19 19
20void add_mimetype(const char *name, const char *value)
21{
22 struct string_list_item *item;
23
24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes);
25 item->util = xstrdup(value);
26}
27
20struct cgit_filter *new_filter(const char *cmd, int extra_args) 28struct cgit_filter *new_filter(const char *cmd, int extra_args)
21{ 29{
22 struct cgit_filter *f; 30 struct cgit_filter *f;
23 31
24 if (!cmd || !cmd[0]) 32 if (!cmd || !cmd[0])
25 return NULL; 33 return NULL;
26 34
27 f = xmalloc(sizeof(struct cgit_filter)); 35 f = xmalloc(sizeof(struct cgit_filter));
28 f->cmd = xstrdup(cmd); 36 f->cmd = xstrdup(cmd);
29 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 37 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
30 f->argv[0] = f->cmd; 38 f->argv[0] = f->cmd;
31 f->argv[1] = NULL; 39 f->argv[1] = NULL;
@@ -57,24 +65,26 @@ void config_cb(const char *name, const char *value)
57 else if (!strcmp(name, "index-info")) 65 else if (!strcmp(name, "index-info"))
58 ctx.cfg.index_info = xstrdup(value); 66 ctx.cfg.index_info = xstrdup(value);
59 else if (!strcmp(name, "logo-link")) 67 else if (!strcmp(name, "logo-link"))
60 ctx.cfg.logo_link = xstrdup(value); 68 ctx.cfg.logo_link = xstrdup(value);
61 else if (!strcmp(name, "module-link")) 69 else if (!strcmp(name, "module-link"))
62 ctx.cfg.module_link = xstrdup(value); 70 ctx.cfg.module_link = xstrdup(value);
63 else if (!strcmp(name, "virtual-root")) { 71 else if (!strcmp(name, "virtual-root")) {
64 ctx.cfg.virtual_root = trim_end(value, '/'); 72 ctx.cfg.virtual_root = trim_end(value, '/');
65 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 73 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
66 ctx.cfg.virtual_root = ""; 74 ctx.cfg.virtual_root = "";
67 } else if (!strcmp(name, "nocache")) 75 } else if (!strcmp(name, "nocache"))
68 ctx.cfg.nocache = atoi(value); 76 ctx.cfg.nocache = atoi(value);
77 else if (!strcmp(name, "noplainemail"))
78 ctx.cfg.noplainemail = atoi(value);
69 else if (!strcmp(name, "noheader")) 79 else if (!strcmp(name, "noheader"))
70 ctx.cfg.noheader = atoi(value); 80 ctx.cfg.noheader = atoi(value);
71 else if (!strcmp(name, "snapshots")) 81 else if (!strcmp(name, "snapshots"))
72 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 82 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
73 else if (!strcmp(name, "enable-index-links")) 83 else if (!strcmp(name, "enable-index-links"))
74 ctx.cfg.enable_index_links = atoi(value); 84 ctx.cfg.enable_index_links = atoi(value);
75 else if (!strcmp(name, "enable-log-filecount")) 85 else if (!strcmp(name, "enable-log-filecount"))
76 ctx.cfg.enable_log_filecount = atoi(value); 86 ctx.cfg.enable_log_filecount = atoi(value);
77 else if (!strcmp(name, "enable-log-linecount")) 87 else if (!strcmp(name, "enable-log-linecount"))
78 ctx.cfg.enable_log_linecount = atoi(value); 88 ctx.cfg.enable_log_linecount = atoi(value);
79 else if (!strcmp(name, "max-stats")) 89 else if (!strcmp(name, "max-stats"))
80 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 90 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
@@ -113,24 +123,26 @@ void config_cb(const char *name, const char *value)
113 else if (!strcmp(name, "summary-tags")) 123 else if (!strcmp(name, "summary-tags"))
114 ctx.cfg.summary_tags = atoi(value); 124 ctx.cfg.summary_tags = atoi(value);
115 else if (!strcmp(name, "agefile")) 125 else if (!strcmp(name, "agefile"))
116 ctx.cfg.agefile = xstrdup(value); 126 ctx.cfg.agefile = xstrdup(value);
117 else if (!strcmp(name, "renamelimit")) 127 else if (!strcmp(name, "renamelimit"))
118 ctx.cfg.renamelimit = atoi(value); 128 ctx.cfg.renamelimit = atoi(value);
119 else if (!strcmp(name, "robots")) 129 else if (!strcmp(name, "robots"))
120 ctx.cfg.robots = xstrdup(value); 130 ctx.cfg.robots = xstrdup(value);
121 else if (!strcmp(name, "clone-prefix")) 131 else if (!strcmp(name, "clone-prefix"))
122 ctx.cfg.clone_prefix = xstrdup(value); 132 ctx.cfg.clone_prefix = xstrdup(value);
123 else if (!strcmp(name, "local-time")) 133 else if (!strcmp(name, "local-time"))
124 ctx.cfg.local_time = atoi(value); 134 ctx.cfg.local_time = atoi(value);
135 else if (!prefixcmp(name, "mimetype."))
136 add_mimetype(name + 9, value);
125 else if (!strcmp(name, "repo.group")) 137 else if (!strcmp(name, "repo.group"))
126 ctx.cfg.repo_group = xstrdup(value); 138 ctx.cfg.repo_group = xstrdup(value);
127 else if (!strcmp(name, "repo.url")) 139 else if (!strcmp(name, "repo.url"))
128 ctx.repo = cgit_add_repo(value); 140 ctx.repo = cgit_add_repo(value);
129 else if (!strcmp(name, "repo.name")) 141 else if (!strcmp(name, "repo.name"))
130 ctx.repo->name = xstrdup(value); 142 ctx.repo->name = xstrdup(value);
131 else if (ctx.repo && !strcmp(name, "repo.path")) 143 else if (ctx.repo && !strcmp(name, "repo.path"))
132 ctx.repo->path = trim_end(value, '/'); 144 ctx.repo->path = trim_end(value, '/');
133 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 145 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
134 ctx.repo->clone_url = xstrdup(value); 146 ctx.repo->clone_url = xstrdup(value);
135 else if (ctx.repo && !strcmp(name, "repo.desc")) 147 else if (ctx.repo && !strcmp(name, "repo.desc"))
136 ctx.repo->desc = xstrdup(value); 148 ctx.repo->desc = xstrdup(value);
@@ -231,24 +243,25 @@ static void prepare_context(struct cgit_context *ctx)
231 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 243 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
232 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 244 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
233 ctx->cfg.summary_branches = 10; 245 ctx->cfg.summary_branches = 10;
234 ctx->cfg.summary_log = 10; 246 ctx->cfg.summary_log = 10;
235 ctx->cfg.summary_tags = 10; 247 ctx->cfg.summary_tags = 10;
236 ctx->page.mimetype = "text/html"; 248 ctx->page.mimetype = "text/html";
237 ctx->page.charset = PAGE_ENCODING; 249 ctx->page.charset = PAGE_ENCODING;
238 ctx->page.filename = NULL; 250 ctx->page.filename = NULL;
239 ctx->page.size = 0; 251 ctx->page.size = 0;
240 ctx->page.modified = time(NULL); 252 ctx->page.modified = time(NULL);
241 ctx->page.expires = ctx->page.modified; 253 ctx->page.expires = ctx->page.modified;
242 ctx->page.etag = NULL; 254 ctx->page.etag = NULL;
255 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
243} 256}
244 257
245struct refmatch { 258struct refmatch {
246 char *req_ref; 259 char *req_ref;
247 char *first_ref; 260 char *first_ref;
248 int match; 261 int match;
249}; 262};
250 263
251int find_current_ref(const char *refname, const unsigned char *sha1, 264int find_current_ref(const char *refname, const unsigned char *sha1,
252 int flags, void *cb_data) 265 int flags, void *cb_data)
253{ 266{
254 struct refmatch *info; 267 struct refmatch *info;
diff --git a/cgit.h b/cgit.h
index b8f4850..4c854ea 100644
--- a/cgit.h
+++ b/cgit.h
@@ -6,24 +6,25 @@
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 <string-list.h>
18#include <xdiff-interface.h> 19#include <xdiff-interface.h>
19#include <xdiff/xdiff.h> 20#include <xdiff/xdiff.h>
20#include <utf8.h> 21#include <utf8.h>
21 22
22 23
23/* 24/*
24 * Dateformats used on misc. pages 25 * Dateformats used on misc. pages
25 */ 26 */
26#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
27#define FMT_SHORTDATE "%Y-%m-%d" 28#define FMT_SHORTDATE "%Y-%m-%d"
28#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
29 30
@@ -171,30 +172,32 @@ struct cgit_config {
171 int embedded; 172 int embedded;
172 int enable_index_links; 173 int enable_index_links;
173 int enable_log_filecount; 174 int enable_log_filecount;
174 int enable_log_linecount; 175 int enable_log_linecount;
175 int local_time; 176 int local_time;
176 int max_repo_count; 177 int max_repo_count;
177 int max_commit_count; 178 int max_commit_count;
178 int max_lock_attempts; 179 int max_lock_attempts;
179 int max_msg_len; 180 int max_msg_len;
180 int max_repodesc_len; 181 int max_repodesc_len;
181 int max_stats; 182 int max_stats;
182 int nocache; 183 int nocache;
184 int noplainemail;
183 int noheader; 185 int noheader;
184 int renamelimit; 186 int renamelimit;
185 int snapshots; 187 int snapshots;
186 int summary_branches; 188 int summary_branches;
187 int summary_log; 189 int summary_log;
188 int summary_tags; 190 int summary_tags;
191 struct string_list mimetypes;
189 struct cgit_filter *about_filter; 192 struct cgit_filter *about_filter;
190 struct cgit_filter *commit_filter; 193 struct cgit_filter *commit_filter;
191 struct cgit_filter *source_filter; 194 struct cgit_filter *source_filter;
192}; 195};
193 196
194struct cgit_page { 197struct cgit_page {
195 time_t modified; 198 time_t modified;
196 time_t expires; 199 time_t expires;
197 size_t size; 200 size_t size;
198 char *mimetype; 201 char *mimetype;
199 char *charset; 202 char *charset;
200 char *filename; 203 char *filename;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index d8e4b97..4d656fe 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -151,35 +151,43 @@ max-repo-count::
151 Specifies the number of entries to list per page on therepository 151 Specifies the number of entries to list per page on therepository
152 index page. Default value: "50". 152 index page. Default value: "50".
153 153
154max-repodesc-length:: 154max-repodesc-length::
155 Specifies the maximum number of repo description characters to display 155 Specifies the maximum number of repo description characters to display
156 on the repository index page. Default value: "80". 156 on the repository index page. Default value: "80".
157 157
158max-stats:: 158max-stats::
159 Set the default maximum statistics period. Valid values are "week", 159 Set the default maximum statistics period. Valid values are "week",
160 "month", "quarter" and "year". If unspecified, statistics are 160 "month", "quarter" and "year". If unspecified, statistics are
161 disabled. Default value: none. See also: "repo.max-stats". 161 disabled. Default value: none. See also: "repo.max-stats".
162 162
163mimetype.<ext>::
164 Set the mimetype for the specified filename extension. This is used
165 by the `plain` command when returning blob content.
166
163module-link:: 167module-link::
164 Text which will be used as the formatstring for a hyperlink when a 168 Text which will be used as the formatstring for a hyperlink when a
165 submodule is printed in a directory listing. The arguments for the 169 submodule is printed in a directory listing. The arguments for the
166 formatstring are the path and SHA1 of the submodule commit. Default 170 formatstring are the path and SHA1 of the submodule commit. Default
167 value: "./?repo=%s&page=commit&id=%s" 171 value: "./?repo=%s&page=commit&id=%s"
168 172
169nocache:: 173nocache::
170 If set to the value "1" caching will be disabled. This settings is 174 If set to the value "1" caching will be disabled. This settings is
171 deprecated, and will not be honored starting with cgit-1.0. Default 175 deprecated, and will not be honored starting with cgit-1.0. Default
172 value: "0". 176 value: "0".
173 177
178noplainemail::
179 If set to "1" showing full author email adresses will be disabled.
180 Default value: "0".
181
174noheader:: 182noheader::
175 Flag which, when set to "1", will make cgit omit the standard header 183 Flag which, when set to "1", will make cgit omit the standard header
176 on all pages. Default value: none. See also: "embedded". 184 on all pages. Default value: none. See also: "embedded".
177 185
178renamelimit:: 186renamelimit::
179 Maximum number of files to consider when detecting renames. The value 187 Maximum number of files to consider when detecting renames. The value
180 "-1" uses the compiletime value in git (for further info, look at 188 "-1" uses the compiletime value in git (for further info, look at
181 `man git-diff`). Default value: "-1". 189 `man git-diff`). Default value: "-1".
182 190
183repo.group:: 191repo.group::
184 A value for the current repository group, which all repositories 192 A value for the current repository group, which all repositories
185 specified after this setting will inherit. Default value: none. 193 specified after this setting will inherit. Default value: none.
@@ -346,24 +354,37 @@ root-title=foobar.com git repositories
346root-desc=tracking the foobar development 354root-desc=tracking the foobar development
347 355
348 356
349# Include some more info about foobar.com on the index page 357# Include some more info about foobar.com on the index page
350root-readme=/var/www/htdocs/about.html 358root-readme=/var/www/htdocs/about.html
351 359
352 360
353# Allow download of tar.gz, tar.bz2 and zip-files 361# Allow download of tar.gz, tar.bz2 and zip-files
354snapshots=tar.gz tar.bz2 zip 362snapshots=tar.gz tar.bz2 zip
355 363
356 364
357## 365##
366## List of common mimetypes
367##
368
369mimetype.git=image/git
370mimetype.html=text/html
371mimetype.jpg=image/jpeg
372mimetype.jpeg=image/jpeg
373mimetype.pdf=application/pdf
374mimetype.png=image/png
375mimetype.svg=image/svg+xml
376
377
378##
358## List of repositories. 379## List of repositories.
359## PS: Any repositories listed when repo.group is unset will not be 380## PS: Any repositories listed when repo.group is unset will not be
360## displayed under a group heading 381## displayed under a group heading
361## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') 382## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
362## and included like this: 383## and included like this:
363## include=/etc/cgitrepos 384## include=/etc/cgitrepos
364## 385##
365 386
366 387
367repo.url=foo 388repo.url=foo
368repo.path=/pub/git/foo.git 389repo.path=/pub/git/foo.git
369repo.desc=the master foo repository 390repo.desc=the master foo repository
diff --git a/ui-atom.c b/ui-atom.c
index e5c31d9..808b2d0 100644
--- a/ui-atom.c
+++ b/ui-atom.c
@@ -23,25 +23,25 @@ void add_entry(struct commit *commit, char *host)
23 html("<title>"); 23 html("<title>");
24 html_txt(info->subject); 24 html_txt(info->subject);
25 html("</title>\n"); 25 html("</title>\n");
26 html("<updated>"); 26 html("<updated>");
27 cgit_print_date(info->author_date, FMT_ATOMDATE, ctx.cfg.local_time); 27 cgit_print_date(info->author_date, FMT_ATOMDATE, ctx.cfg.local_time);
28 html("</updated>\n"); 28 html("</updated>\n");
29 html("<author>\n"); 29 html("<author>\n");
30 if (info->author) { 30 if (info->author) {
31 html("<name>"); 31 html("<name>");
32 html_txt(info->author); 32 html_txt(info->author);
33 html("</name>\n"); 33 html("</name>\n");
34 } 34 }
35 if (info->author_email) { 35 if (info->author_email && !ctx.cfg.noplainemail) {
36 mail = xstrdup(info->author_email); 36 mail = xstrdup(info->author_email);
37 t = strchr(mail, '<'); 37 t = strchr(mail, '<');
38 if (t) 38 if (t)
39 t++; 39 t++;
40 else 40 else
41 t = mail; 41 t = mail;
42 t2 = strchr(t, '>'); 42 t2 = strchr(t, '>');
43 if (t2) 43 if (t2)
44 *t2 = '\0'; 44 *t2 = '\0';
45 html("<email>"); 45 html("<email>");
46 html_txt(t); 46 html_txt(t);
47 html("</email>\n"); 47 html("</email>\n");
diff --git a/ui-commit.c b/ui-commit.c
index 5815b58..d6b73ee 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -31,33 +31,37 @@ void cgit_print_commit(char *hex)
31 commit = lookup_commit_reference(sha1); 31 commit = lookup_commit_reference(sha1);
32 if (!commit) { 32 if (!commit) {
33 cgit_print_error(fmt("Bad commit reference: %s", hex)); 33 cgit_print_error(fmt("Bad commit reference: %s", hex));
34 return; 34 return;
35 } 35 }
36 info = cgit_parse_commit(commit); 36 info = cgit_parse_commit(commit);
37 37
38 load_ref_decorations(); 38 load_ref_decorations();
39 39
40 html("<table summary='commit info' class='commit-info'>\n"); 40 html("<table summary='commit info' class='commit-info'>\n");
41 html("<tr><th>author</th><td>"); 41 html("<tr><th>author</th><td>");
42 html_txt(info->author); 42 html_txt(info->author);
43 html(" "); 43 if (!ctx.cfg.noplainemail) {
44 html_txt(info->author_email); 44 html(" ");
45 html_txt(info->author_email);
46 }
45 html("</td><td class='right'>"); 47 html("</td><td class='right'>");
46 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time); 48 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time);
47 html("</td></tr>\n"); 49 html("</td></tr>\n");
48 html("<tr><th>committer</th><td>"); 50 html("<tr><th>committer</th><td>");
49 html_txt(info->committer); 51 html_txt(info->committer);
50 html(" "); 52 if (!ctx.cfg.noplainemail) {
51 html_txt(info->committer_email); 53 html(" ");
54 html_txt(info->committer_email);
55 }
52 html("</td><td class='right'>"); 56 html("</td><td class='right'>");
53 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time); 57 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time);
54 html("</td></tr>\n"); 58 html("</td></tr>\n");
55 html("<tr><th>commit</th><td colspan='2' class='sha1'>"); 59 html("<tr><th>commit</th><td colspan='2' class='sha1'>");
56 tmp = sha1_to_hex(commit->object.sha1); 60 tmp = sha1_to_hex(commit->object.sha1);
57 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp); 61 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp);
58 html(" ("); 62 html(" (");
59 cgit_patch_link("patch", NULL, NULL, NULL, tmp); 63 cgit_patch_link("patch", NULL, NULL, NULL, tmp);
60 html(")</td></tr>\n"); 64 html(")</td></tr>\n");
61 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 65 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
62 tmp = xstrdup(hex); 66 tmp = xstrdup(hex);
63 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, 67 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
diff --git a/ui-patch.c b/ui-patch.c
index 5d665d3..2a8f7a5 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -99,25 +99,29 @@ void cgit_print_patch(char *hex)
99 info = cgit_parse_commit(commit); 99 info = cgit_parse_commit(commit);
100 100
101 if (commit->parents && commit->parents->item) 101 if (commit->parents && commit->parents->item)
102 hashcpy(old_sha1, commit->parents->item->object.sha1); 102 hashcpy(old_sha1, commit->parents->item->object.sha1);
103 else 103 else
104 hashclr(old_sha1); 104 hashclr(old_sha1);
105 105
106 patchname = fmt("%s.patch", sha1_to_hex(sha1)); 106 patchname = fmt("%s.patch", sha1_to_hex(sha1));
107 ctx.page.mimetype = "text/plain"; 107 ctx.page.mimetype = "text/plain";
108 ctx.page.filename = patchname; 108 ctx.page.filename = patchname;
109 cgit_print_http_headers(&ctx); 109 cgit_print_http_headers(&ctx);
110 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1)); 110 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
111 htmlf("From: %s %s\n", info->author, info->author_email); 111 htmlf("From: %s", info->author);
112 if (!ctx.cfg.noplainemail) {
113 htmlf(" %s", info->author_email);
114 }
115 html("\n");
112 html("Date: "); 116 html("Date: ");
113 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n", ctx.cfg.local_time); 117 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n", ctx.cfg.local_time);
114 htmlf("Subject: %s\n\n", info->subject); 118 htmlf("Subject: %s\n\n", info->subject);
115 if (info->msg && *info->msg) { 119 if (info->msg && *info->msg) {
116 htmlf("%s", info->msg); 120 htmlf("%s", info->msg);
117 if (info->msg[strlen(info->msg) - 1] != '\n') 121 if (info->msg[strlen(info->msg) - 1] != '\n')
118 html("\n"); 122 html("\n");
119 } 123 }
120 html("---\n"); 124 html("---\n");
121 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL); 125 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL);
122 html("--\n"); 126 html("--\n");
123 htmlf("cgit %s\n", CGIT_VERSION); 127 htmlf("cgit %s\n", CGIT_VERSION);
diff --git a/ui-plain.c b/ui-plain.c
index 93a3a05..27c6dae 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -8,42 +8,52 @@
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13char *curr_rev; 13char *curr_rev;
14char *match_path; 14char *match_path;
15int match; 15int match;
16 16
17static void print_object(const unsigned char *sha1, const char *path) 17static void print_object(const unsigned char *sha1, const char *path)
18{ 18{
19 enum object_type type; 19 enum object_type type;
20 char *buf; 20 char *buf, *ext;
21 unsigned long size; 21 unsigned long size;
22 struct string_list_item *mime;
22 23
23 type = sha1_object_info(sha1, &size); 24 type = sha1_object_info(sha1, &size);
24 if (type == OBJ_BAD) { 25 if (type == OBJ_BAD) {
25 html_status(404, "Not found", 0); 26 html_status(404, "Not found", 0);
26 return; 27 return;
27 } 28 }
28 29
29 buf = read_sha1_file(sha1, &type, &size); 30 buf = read_sha1_file(sha1, &type, &size);
30 if (!buf) { 31 if (!buf) {
31 html_status(404, "Not found", 0); 32 html_status(404, "Not found", 0);
32 return; 33 return;
33 } 34 }
34 if (buffer_is_binary(buf, size)) 35 ctx.page.mimetype = NULL;
35 ctx.page.mimetype = "application/octet-stream"; 36 ext = strrchr(path, '.');
36 else 37 if (ext && *(++ext)) {
37 ctx.page.mimetype = "text/plain"; 38 mime = string_list_lookup(ext, &ctx.cfg.mimetypes);
39 if (mime)
40 ctx.page.mimetype = (char *)mime->util;
41 }
42 if (!ctx.page.mimetype) {
43 if (buffer_is_binary(buf, size))
44 ctx.page.mimetype = "application/octet-stream";
45 else
46 ctx.page.mimetype = "text/plain";
47 }
38 ctx.page.filename = fmt("%s", path); 48 ctx.page.filename = fmt("%s", path);
39 ctx.page.size = size; 49 ctx.page.size = size;
40 ctx.page.etag = sha1_to_hex(sha1); 50 ctx.page.etag = sha1_to_hex(sha1);
41 cgit_print_http_headers(&ctx); 51 cgit_print_http_headers(&ctx);
42 html_raw(buf, size); 52 html_raw(buf, size);
43 match = 1; 53 match = 1;
44} 54}
45 55
46static int walk_tree(const unsigned char *sha1, const char *base, int baselen, 56static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
47 const char *pathname, unsigned mode, int stage, 57 const char *pathname, unsigned mode, int stage,
48 void *cbdata) 58 void *cbdata)
49{ 59{
diff --git a/ui-tag.c b/ui-tag.c
index 0e056e0..a9c8670 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -55,25 +55,25 @@ void cgit_print_tag(char *revname)
55 html("<table class='commit-info'>\n"); 55 html("<table class='commit-info'>\n");
56 htmlf("<tr><td>Tag name</td><td>"); 56 htmlf("<tr><td>Tag name</td><td>");
57 html_txt(revname); 57 html_txt(revname);
58 htmlf(" (%s)</td></tr>\n", sha1_to_hex(sha1)); 58 htmlf(" (%s)</td></tr>\n", sha1_to_hex(sha1));
59 if (info->tagger_date > 0) { 59 if (info->tagger_date > 0) {
60 html("<tr><td>Tag date</td><td>"); 60 html("<tr><td>Tag date</td><td>");
61 cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time); 61 cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time);
62 html("</td></tr>\n"); 62 html("</td></tr>\n");
63 } 63 }
64 if (info->tagger) { 64 if (info->tagger) {
65 html("<tr><td>Tagged by</td><td>"); 65 html("<tr><td>Tagged by</td><td>");
66 html_txt(info->tagger); 66 html_txt(info->tagger);
67 if (info->tagger_email) { 67 if (info->tagger_email && !ctx.cfg.noplainemail) {
68 html(" "); 68 html(" ");
69 html_txt(info->tagger_email); 69 html_txt(info->tagger_email);
70 } 70 }
71 html("</td></tr>\n"); 71 html("</td></tr>\n");
72 } 72 }
73 html("<tr><td>Tagged object</td><td>"); 73 html("<tr><td>Tagged object</td><td>");
74 cgit_object_link(tag->tagged); 74 cgit_object_link(tag->tagged);
75 html("</td></tr>\n"); 75 html("</td></tr>\n");
76 html("</table>\n"); 76 html("</table>\n");
77 print_tag_content(info->msg); 77 print_tag_content(info->msg);
78 } else { 78 } else {
79 html("<table class='commit-info'>\n"); 79 html("<table class='commit-info'>\n");
diff --git a/ui-tree.c b/ui-tree.c
index caf6a9e..c608754 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -104,24 +104,25 @@ static void print_object(const unsigned char *sha1, char *path, const char *base
104 print_binary_buffer(buf, size); 104 print_binary_buffer(buf, size);
105 else 105 else
106 print_text_buffer(basename, buf, size); 106 print_text_buffer(basename, buf, size);
107} 107}
108 108
109 109
110static int ls_item(const unsigned char *sha1, const char *base, int baselen, 110static int ls_item(const unsigned char *sha1, const char *base, int baselen,
111 const char *pathname, unsigned int mode, int stage, 111 const char *pathname, unsigned int mode, int stage,
112 void *cbdata) 112 void *cbdata)
113{ 113{
114 char *name; 114 char *name;
115 char *fullpath; 115 char *fullpath;
116 char *class;
116 enum object_type type; 117 enum object_type type;
117 unsigned long size = 0; 118 unsigned long size = 0;
118 119
119 name = xstrdup(pathname); 120 name = xstrdup(pathname);
120 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 121 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
121 ctx.qry.path ? "/" : "", name); 122 ctx.qry.path ? "/" : "", name);
122 123
123 if (!S_ISGITLINK(mode)) { 124 if (!S_ISGITLINK(mode)) {
124 type = sha1_object_info(sha1, &size); 125 type = sha1_object_info(sha1, &size);
125 if (type == OBJ_BAD) { 126 if (type == OBJ_BAD) {
126 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 127 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
127 name, 128 name,
@@ -136,25 +137,30 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen,
136 if (S_ISGITLINK(mode)) { 137 if (S_ISGITLINK(mode)) {
137 htmlf("<a class='ls-mod' href='"); 138 htmlf("<a class='ls-mod' href='");
138 html_attr(fmt(ctx.repo->module_link, 139 html_attr(fmt(ctx.repo->module_link,
139 name, 140 name,
140 sha1_to_hex(sha1))); 141 sha1_to_hex(sha1)));
141 html("'>"); 142 html("'>");
142 html_txt(name); 143 html_txt(name);
143 html("</a>"); 144 html("</a>");
144 } else if (S_ISDIR(mode)) { 145 } else if (S_ISDIR(mode)) {
145 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, 146 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
146 curr_rev, fullpath); 147 curr_rev, fullpath);
147 } else { 148 } else {
148 cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, 149 class = strrchr(name, '.');
150 if (class != NULL) {
151 class = fmt("ls-blob %s", class + 1);
152 } else
153 class = "ls-blob";
154 cgit_tree_link(name, NULL, class, ctx.qry.head,
149 curr_rev, fullpath); 155 curr_rev, fullpath);
150 } 156 }
151 htmlf("</td><td class='ls-size'>%li</td>", size); 157 htmlf("</td><td class='ls-size'>%li</td>", size);
152 158
153 html("<td>"); 159 html("<td>");
154 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, 160 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev,
155 fullpath, 0, NULL, NULL, ctx.qry.showmsg); 161 fullpath, 0, NULL, NULL, ctx.qry.showmsg);
156 if (ctx.repo->max_stats) 162 if (ctx.repo->max_stats)
157 cgit_stats_link("stats", NULL, "button", ctx.qry.head, 163 cgit_stats_link("stats", NULL, "button", ctx.qry.head,
158 fullpath); 164 fullpath);
159 html("</td></tr>\n"); 165 html("</td></tr>\n");
160 free(name); 166 free(name);