summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--cgit.c4
-rw-r--r--cgit.h20
-rw-r--r--cgitrc.5.txt6
-rw-r--r--shared.c2
-rw-r--r--ui-commit.c16
-rw-r--r--ui-tree.c8
6 files changed, 35 insertions, 21 deletions
diff --git a/cgit.c b/cgit.c
index 2cda554..fd341b8 100644
--- a/cgit.c
+++ b/cgit.c
@@ -53,192 +53,196 @@ void config_cb(const char *name, const char *value)
53 else if (!strcmp(name, "logo")) 53 else if (!strcmp(name, "logo"))
54 ctx.cfg.logo = xstrdup(value); 54 ctx.cfg.logo = xstrdup(value);
55 else if (!strcmp(name, "index-header")) 55 else if (!strcmp(name, "index-header"))
56 ctx.cfg.index_header = xstrdup(value); 56 ctx.cfg.index_header = xstrdup(value);
57 else if (!strcmp(name, "index-info")) 57 else if (!strcmp(name, "index-info"))
58 ctx.cfg.index_info = xstrdup(value); 58 ctx.cfg.index_info = xstrdup(value);
59 else if (!strcmp(name, "logo-link")) 59 else if (!strcmp(name, "logo-link"))
60 ctx.cfg.logo_link = xstrdup(value); 60 ctx.cfg.logo_link = xstrdup(value);
61 else if (!strcmp(name, "module-link")) 61 else if (!strcmp(name, "module-link"))
62 ctx.cfg.module_link = xstrdup(value); 62 ctx.cfg.module_link = xstrdup(value);
63 else if (!strcmp(name, "virtual-root")) { 63 else if (!strcmp(name, "virtual-root")) {
64 ctx.cfg.virtual_root = trim_end(value, '/'); 64 ctx.cfg.virtual_root = trim_end(value, '/');
65 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 65 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
66 ctx.cfg.virtual_root = ""; 66 ctx.cfg.virtual_root = "";
67 } else if (!strcmp(name, "nocache")) 67 } else if (!strcmp(name, "nocache"))
68 ctx.cfg.nocache = atoi(value); 68 ctx.cfg.nocache = atoi(value);
69 else if (!strcmp(name, "noheader")) 69 else if (!strcmp(name, "noheader"))
70 ctx.cfg.noheader = atoi(value); 70 ctx.cfg.noheader = atoi(value);
71 else if (!strcmp(name, "snapshots")) 71 else if (!strcmp(name, "snapshots"))
72 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 72 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
73 else if (!strcmp(name, "enable-index-links")) 73 else if (!strcmp(name, "enable-index-links"))
74 ctx.cfg.enable_index_links = atoi(value); 74 ctx.cfg.enable_index_links = atoi(value);
75 else if (!strcmp(name, "enable-log-filecount")) 75 else if (!strcmp(name, "enable-log-filecount"))
76 ctx.cfg.enable_log_filecount = atoi(value); 76 ctx.cfg.enable_log_filecount = atoi(value);
77 else if (!strcmp(name, "enable-log-linecount")) 77 else if (!strcmp(name, "enable-log-linecount"))
78 ctx.cfg.enable_log_linecount = atoi(value); 78 ctx.cfg.enable_log_linecount = atoi(value);
79 else if (!strcmp(name, "max-stats")) 79 else if (!strcmp(name, "max-stats"))
80 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 80 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
81 else if (!strcmp(name, "cache-size")) 81 else if (!strcmp(name, "cache-size"))
82 ctx.cfg.cache_size = atoi(value); 82 ctx.cfg.cache_size = atoi(value);
83 else if (!strcmp(name, "cache-root")) 83 else if (!strcmp(name, "cache-root"))
84 ctx.cfg.cache_root = xstrdup(value); 84 ctx.cfg.cache_root = xstrdup(value);
85 else if (!strcmp(name, "cache-root-ttl")) 85 else if (!strcmp(name, "cache-root-ttl"))
86 ctx.cfg.cache_root_ttl = atoi(value); 86 ctx.cfg.cache_root_ttl = atoi(value);
87 else if (!strcmp(name, "cache-repo-ttl")) 87 else if (!strcmp(name, "cache-repo-ttl"))
88 ctx.cfg.cache_repo_ttl = atoi(value); 88 ctx.cfg.cache_repo_ttl = atoi(value);
89 else if (!strcmp(name, "cache-static-ttl")) 89 else if (!strcmp(name, "cache-static-ttl"))
90 ctx.cfg.cache_static_ttl = atoi(value); 90 ctx.cfg.cache_static_ttl = atoi(value);
91 else if (!strcmp(name, "cache-dynamic-ttl")) 91 else if (!strcmp(name, "cache-dynamic-ttl"))
92 ctx.cfg.cache_dynamic_ttl = atoi(value); 92 ctx.cfg.cache_dynamic_ttl = atoi(value);
93 else if (!strcmp(name, "commit-filter")) 93 else if (!strcmp(name, "commit-filter"))
94 ctx.cfg.commit_filter = new_filter(value, 0); 94 ctx.cfg.commit_filter = new_filter(value, 0);
95 else if (!strcmp(name, "embedded")) 95 else if (!strcmp(name, "embedded"))
96 ctx.cfg.embedded = atoi(value); 96 ctx.cfg.embedded = atoi(value);
97 else if (!strcmp(name, "max-message-length")) 97 else if (!strcmp(name, "max-message-length"))
98 ctx.cfg.max_msg_len = atoi(value); 98 ctx.cfg.max_msg_len = atoi(value);
99 else if (!strcmp(name, "max-repodesc-length")) 99 else if (!strcmp(name, "max-repodesc-length"))
100 ctx.cfg.max_repodesc_len = atoi(value); 100 ctx.cfg.max_repodesc_len = atoi(value);
101 else if (!strcmp(name, "max-repo-count")) 101 else if (!strcmp(name, "max-repo-count"))
102 ctx.cfg.max_repo_count = atoi(value); 102 ctx.cfg.max_repo_count = atoi(value);
103 else if (!strcmp(name, "max-commit-count")) 103 else if (!strcmp(name, "max-commit-count"))
104 ctx.cfg.max_commit_count = atoi(value); 104 ctx.cfg.max_commit_count = atoi(value);
105 else if (!strcmp(name, "source-filter")) 105 else if (!strcmp(name, "source-filter"))
106 ctx.cfg.source_filter = new_filter(value, 1); 106 ctx.cfg.source_filter = new_filter(value, 1);
107 else if (!strcmp(name, "summary-log")) 107 else if (!strcmp(name, "summary-log"))
108 ctx.cfg.summary_log = atoi(value); 108 ctx.cfg.summary_log = atoi(value);
109 else if (!strcmp(name, "summary-branches")) 109 else if (!strcmp(name, "summary-branches"))
110 ctx.cfg.summary_branches = atoi(value); 110 ctx.cfg.summary_branches = atoi(value);
111 else if (!strcmp(name, "summary-tags")) 111 else if (!strcmp(name, "summary-tags"))
112 ctx.cfg.summary_tags = atoi(value); 112 ctx.cfg.summary_tags = atoi(value);
113 else if (!strcmp(name, "agefile")) 113 else if (!strcmp(name, "agefile"))
114 ctx.cfg.agefile = xstrdup(value); 114 ctx.cfg.agefile = xstrdup(value);
115 else if (!strcmp(name, "renamelimit")) 115 else if (!strcmp(name, "renamelimit"))
116 ctx.cfg.renamelimit = atoi(value); 116 ctx.cfg.renamelimit = atoi(value);
117 else if (!strcmp(name, "robots")) 117 else if (!strcmp(name, "robots"))
118 ctx.cfg.robots = xstrdup(value); 118 ctx.cfg.robots = xstrdup(value);
119 else if (!strcmp(name, "clone-prefix")) 119 else if (!strcmp(name, "clone-prefix"))
120 ctx.cfg.clone_prefix = xstrdup(value); 120 ctx.cfg.clone_prefix = xstrdup(value);
121 else if (!strcmp(name, "local-time")) 121 else if (!strcmp(name, "local-time"))
122 ctx.cfg.local_time = atoi(value); 122 ctx.cfg.local_time = atoi(value);
123 else if (!strcmp(name, "repo.group")) 123 else if (!strcmp(name, "repo.group"))
124 ctx.cfg.repo_group = xstrdup(value); 124 ctx.cfg.repo_group = xstrdup(value);
125 else if (!strcmp(name, "repo.url")) 125 else if (!strcmp(name, "repo.url"))
126 ctx.repo = cgit_add_repo(value); 126 ctx.repo = cgit_add_repo(value);
127 else if (!strcmp(name, "repo.name")) 127 else if (!strcmp(name, "repo.name"))
128 ctx.repo->name = xstrdup(value); 128 ctx.repo->name = xstrdup(value);
129 else if (ctx.repo && !strcmp(name, "repo.path")) 129 else if (ctx.repo && !strcmp(name, "repo.path"))
130 ctx.repo->path = trim_end(value, '/'); 130 ctx.repo->path = trim_end(value, '/');
131 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 131 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
132 ctx.repo->clone_url = xstrdup(value); 132 ctx.repo->clone_url = xstrdup(value);
133 else if (ctx.repo && !strcmp(name, "repo.desc")) 133 else if (ctx.repo && !strcmp(name, "repo.desc"))
134 ctx.repo->desc = xstrdup(value); 134 ctx.repo->desc = xstrdup(value);
135 else if (ctx.repo && !strcmp(name, "repo.owner")) 135 else if (ctx.repo && !strcmp(name, "repo.owner"))
136 ctx.repo->owner = xstrdup(value); 136 ctx.repo->owner = xstrdup(value);
137 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 137 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
138 ctx.repo->defbranch = xstrdup(value); 138 ctx.repo->defbranch = xstrdup(value);
139 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 139 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
140 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 140 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
141 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 141 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
142 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 142 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
143 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 143 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
144 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 144 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
145 else if (ctx.repo && !strcmp(name, "repo.max-stats")) 145 else if (ctx.repo && !strcmp(name, "repo.max-stats"))
146 ctx.repo->max_stats = cgit_find_stats_period(value, NULL); 146 ctx.repo->max_stats = cgit_find_stats_period(value, NULL);
147 else if (ctx.repo && !strcmp(name, "repo.module-link")) 147 else if (ctx.repo && !strcmp(name, "repo.module-link"))
148 ctx.repo->module_link= xstrdup(value); 148 ctx.repo->module_link= xstrdup(value);
149 else if (ctx.repo && !strcmp(name, "repo.commit-filter"))
150 ctx.repo->commit_filter = new_filter(value, 0);
151 else if (ctx.repo && !strcmp(name, "repo.source-filter"))
152 ctx.repo->source_filter = new_filter(value, 1);
149 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 153 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
150 if (*value == '/') 154 if (*value == '/')
151 ctx.repo->readme = xstrdup(value); 155 ctx.repo->readme = xstrdup(value);
152 else 156 else
153 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 157 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
154 } else if (!strcmp(name, "include")) 158 } else if (!strcmp(name, "include"))
155 parse_configfile(value, config_cb); 159 parse_configfile(value, config_cb);
156} 160}
157 161
158static void querystring_cb(const char *name, const char *value) 162static void querystring_cb(const char *name, const char *value)
159{ 163{
160 if (!strcmp(name,"r")) { 164 if (!strcmp(name,"r")) {
161 ctx.qry.repo = xstrdup(value); 165 ctx.qry.repo = xstrdup(value);
162 ctx.repo = cgit_get_repoinfo(value); 166 ctx.repo = cgit_get_repoinfo(value);
163 } else if (!strcmp(name, "p")) { 167 } else if (!strcmp(name, "p")) {
164 ctx.qry.page = xstrdup(value); 168 ctx.qry.page = xstrdup(value);
165 } else if (!strcmp(name, "url")) { 169 } else if (!strcmp(name, "url")) {
166 ctx.qry.url = xstrdup(value); 170 ctx.qry.url = xstrdup(value);
167 cgit_parse_url(value); 171 cgit_parse_url(value);
168 } else if (!strcmp(name, "qt")) { 172 } else if (!strcmp(name, "qt")) {
169 ctx.qry.grep = xstrdup(value); 173 ctx.qry.grep = xstrdup(value);
170 } else if (!strcmp(name, "q")) { 174 } else if (!strcmp(name, "q")) {
171 ctx.qry.search = xstrdup(value); 175 ctx.qry.search = xstrdup(value);
172 } else if (!strcmp(name, "h")) { 176 } else if (!strcmp(name, "h")) {
173 ctx.qry.head = xstrdup(value); 177 ctx.qry.head = xstrdup(value);
174 ctx.qry.has_symref = 1; 178 ctx.qry.has_symref = 1;
175 } else if (!strcmp(name, "id")) { 179 } else if (!strcmp(name, "id")) {
176 ctx.qry.sha1 = xstrdup(value); 180 ctx.qry.sha1 = xstrdup(value);
177 ctx.qry.has_sha1 = 1; 181 ctx.qry.has_sha1 = 1;
178 } else if (!strcmp(name, "id2")) { 182 } else if (!strcmp(name, "id2")) {
179 ctx.qry.sha2 = xstrdup(value); 183 ctx.qry.sha2 = xstrdup(value);
180 ctx.qry.has_sha1 = 1; 184 ctx.qry.has_sha1 = 1;
181 } else if (!strcmp(name, "ofs")) { 185 } else if (!strcmp(name, "ofs")) {
182 ctx.qry.ofs = atoi(value); 186 ctx.qry.ofs = atoi(value);
183 } else if (!strcmp(name, "path")) { 187 } else if (!strcmp(name, "path")) {
184 ctx.qry.path = trim_end(value, '/'); 188 ctx.qry.path = trim_end(value, '/');
185 } else if (!strcmp(name, "name")) { 189 } else if (!strcmp(name, "name")) {
186 ctx.qry.name = xstrdup(value); 190 ctx.qry.name = xstrdup(value);
187 } else if (!strcmp(name, "mimetype")) { 191 } else if (!strcmp(name, "mimetype")) {
188 ctx.qry.mimetype = xstrdup(value); 192 ctx.qry.mimetype = xstrdup(value);
189 } else if (!strcmp(name, "s")){ 193 } else if (!strcmp(name, "s")){
190 ctx.qry.sort = xstrdup(value); 194 ctx.qry.sort = xstrdup(value);
191 } else if (!strcmp(name, "showmsg")) { 195 } else if (!strcmp(name, "showmsg")) {
192 ctx.qry.showmsg = atoi(value); 196 ctx.qry.showmsg = atoi(value);
193 } else if (!strcmp(name, "period")) { 197 } else if (!strcmp(name, "period")) {
194 ctx.qry.period = xstrdup(value); 198 ctx.qry.period = xstrdup(value);
195 } 199 }
196} 200}
197 201
198static void prepare_context(struct cgit_context *ctx) 202static void prepare_context(struct cgit_context *ctx)
199{ 203{
200 memset(ctx, 0, sizeof(ctx)); 204 memset(ctx, 0, sizeof(ctx));
201 ctx->cfg.agefile = "info/web/last-modified"; 205 ctx->cfg.agefile = "info/web/last-modified";
202 ctx->cfg.nocache = 0; 206 ctx->cfg.nocache = 0;
203 ctx->cfg.cache_size = 0; 207 ctx->cfg.cache_size = 0;
204 ctx->cfg.cache_dynamic_ttl = 5; 208 ctx->cfg.cache_dynamic_ttl = 5;
205 ctx->cfg.cache_max_create_time = 5; 209 ctx->cfg.cache_max_create_time = 5;
206 ctx->cfg.cache_repo_ttl = 5; 210 ctx->cfg.cache_repo_ttl = 5;
207 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 211 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
208 ctx->cfg.cache_root_ttl = 5; 212 ctx->cfg.cache_root_ttl = 5;
209 ctx->cfg.cache_static_ttl = -1; 213 ctx->cfg.cache_static_ttl = -1;
210 ctx->cfg.css = "/cgit.css"; 214 ctx->cfg.css = "/cgit.css";
211 ctx->cfg.logo = "/git-logo.png"; 215 ctx->cfg.logo = "/git-logo.png";
212 ctx->cfg.local_time = 0; 216 ctx->cfg.local_time = 0;
213 ctx->cfg.max_repo_count = 50; 217 ctx->cfg.max_repo_count = 50;
214 ctx->cfg.max_commit_count = 50; 218 ctx->cfg.max_commit_count = 50;
215 ctx->cfg.max_lock_attempts = 5; 219 ctx->cfg.max_lock_attempts = 5;
216 ctx->cfg.max_msg_len = 80; 220 ctx->cfg.max_msg_len = 80;
217 ctx->cfg.max_repodesc_len = 80; 221 ctx->cfg.max_repodesc_len = 80;
218 ctx->cfg.max_stats = 0; 222 ctx->cfg.max_stats = 0;
219 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 223 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
220 ctx->cfg.renamelimit = -1; 224 ctx->cfg.renamelimit = -1;
221 ctx->cfg.robots = "index, nofollow"; 225 ctx->cfg.robots = "index, nofollow";
222 ctx->cfg.root_title = "Git repository browser"; 226 ctx->cfg.root_title = "Git repository browser";
223 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 227 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
224 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 228 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
225 ctx->cfg.summary_branches = 10; 229 ctx->cfg.summary_branches = 10;
226 ctx->cfg.summary_log = 10; 230 ctx->cfg.summary_log = 10;
227 ctx->cfg.summary_tags = 10; 231 ctx->cfg.summary_tags = 10;
228 ctx->page.mimetype = "text/html"; 232 ctx->page.mimetype = "text/html";
229 ctx->page.charset = PAGE_ENCODING; 233 ctx->page.charset = PAGE_ENCODING;
230 ctx->page.filename = NULL; 234 ctx->page.filename = NULL;
231 ctx->page.size = 0; 235 ctx->page.size = 0;
232 ctx->page.modified = time(NULL); 236 ctx->page.modified = time(NULL);
233 ctx->page.expires = ctx->page.modified; 237 ctx->page.expires = ctx->page.modified;
234 ctx->page.etag = NULL; 238 ctx->page.etag = NULL;
235} 239}
236 240
237struct refmatch { 241struct refmatch {
238 char *req_ref; 242 char *req_ref;
239 char *first_ref; 243 char *first_ref;
240 int match; 244 int match;
241}; 245};
242 246
243int find_current_ref(const char *refname, const unsigned char *sha1, 247int find_current_ref(const char *refname, const unsigned char *sha1,
244 int flags, void *cb_data) 248 int flags, void *cb_data)
diff --git a/cgit.h b/cgit.h
index 438301d..f10ba05 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,236 +1,238 @@
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-interface.h> 18#include <xdiff-interface.h>
19#include <xdiff/xdiff.h> 19#include <xdiff/xdiff.h>
20#include <utf8.h> 20#include <utf8.h>
21 21
22 22
23/* 23/*
24 * Dateformats used on misc. pages 24 * Dateformats used on misc. pages
25 */ 25 */
26#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 26#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
27#define FMT_SHORTDATE "%Y-%m-%d" 27#define FMT_SHORTDATE "%Y-%m-%d"
28#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 28#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
29 29
30 30
31/* 31/*
32 * Limits used for relative dates 32 * Limits used for relative dates
33 */ 33 */
34#define TM_MIN 60 34#define TM_MIN 60
35#define TM_HOUR (TM_MIN * 60) 35#define TM_HOUR (TM_MIN * 60)
36#define TM_DAY (TM_HOUR * 24) 36#define TM_DAY (TM_HOUR * 24)
37#define TM_WEEK (TM_DAY * 7) 37#define TM_WEEK (TM_DAY * 7)
38#define TM_YEAR (TM_DAY * 365) 38#define TM_YEAR (TM_DAY * 365)
39#define TM_MONTH (TM_YEAR / 12.0) 39#define TM_MONTH (TM_YEAR / 12.0)
40 40
41 41
42/* 42/*
43 * Default encoding 43 * Default encoding
44 */ 44 */
45#define PAGE_ENCODING "UTF-8" 45#define PAGE_ENCODING "UTF-8"
46 46
47typedef void (*configfn)(const char *name, const char *value); 47typedef void (*configfn)(const char *name, const char *value);
48typedef void (*filepair_fn)(struct diff_filepair *pair); 48typedef void (*filepair_fn)(struct diff_filepair *pair);
49typedef void (*linediff_fn)(char *line, int len); 49typedef void (*linediff_fn)(char *line, int len);
50 50
51struct cgit_filter {
52 char *cmd;
53 char **argv;
54 int old_stdout;
55 int pipe_fh[2];
56 int pid;
57 int exitstatus;
58};
59
51struct cgit_repo { 60struct cgit_repo {
52 char *url; 61 char *url;
53 char *name; 62 char *name;
54 char *path; 63 char *path;
55 char *desc; 64 char *desc;
56 char *owner; 65 char *owner;
57 char *defbranch; 66 char *defbranch;
58 char *group; 67 char *group;
59 char *module_link; 68 char *module_link;
60 char *readme; 69 char *readme;
61 char *clone_url; 70 char *clone_url;
62 int snapshots; 71 int snapshots;
63 int enable_log_filecount; 72 int enable_log_filecount;
64 int enable_log_linecount; 73 int enable_log_linecount;
65 int max_stats; 74 int max_stats;
66 time_t mtime; 75 time_t mtime;
76 struct cgit_filter *commit_filter;
77 struct cgit_filter *source_filter;
67}; 78};
68 79
69struct cgit_repolist { 80struct cgit_repolist {
70 int length; 81 int length;
71 int count; 82 int count;
72 struct cgit_repo *repos; 83 struct cgit_repo *repos;
73}; 84};
74 85
75struct commitinfo { 86struct commitinfo {
76 struct commit *commit; 87 struct commit *commit;
77 char *author; 88 char *author;
78 char *author_email; 89 char *author_email;
79 unsigned long author_date; 90 unsigned long author_date;
80 char *committer; 91 char *committer;
81 char *committer_email; 92 char *committer_email;
82 unsigned long committer_date; 93 unsigned long committer_date;
83 char *subject; 94 char *subject;
84 char *msg; 95 char *msg;
85 char *msg_encoding; 96 char *msg_encoding;
86}; 97};
87 98
88struct taginfo { 99struct taginfo {
89 char *tagger; 100 char *tagger;
90 char *tagger_email; 101 char *tagger_email;
91 unsigned long tagger_date; 102 unsigned long tagger_date;
92 char *msg; 103 char *msg;
93}; 104};
94 105
95struct refinfo { 106struct refinfo {
96 const char *refname; 107 const char *refname;
97 struct object *object; 108 struct object *object;
98 union { 109 union {
99 struct taginfo *tag; 110 struct taginfo *tag;
100 struct commitinfo *commit; 111 struct commitinfo *commit;
101 }; 112 };
102}; 113};
103 114
104struct reflist { 115struct reflist {
105 struct refinfo **refs; 116 struct refinfo **refs;
106 int alloc; 117 int alloc;
107 int count; 118 int count;
108}; 119};
109 120
110struct cgit_query { 121struct cgit_query {
111 int has_symref; 122 int has_symref;
112 int has_sha1; 123 int has_sha1;
113 char *raw; 124 char *raw;
114 char *repo; 125 char *repo;
115 char *page; 126 char *page;
116 char *search; 127 char *search;
117 char *grep; 128 char *grep;
118 char *head; 129 char *head;
119 char *sha1; 130 char *sha1;
120 char *sha2; 131 char *sha2;
121 char *path; 132 char *path;
122 char *name; 133 char *name;
123 char *mimetype; 134 char *mimetype;
124 char *url; 135 char *url;
125 char *period; 136 char *period;
126 int ofs; 137 int ofs;
127 int nohead; 138 int nohead;
128 char *sort; 139 char *sort;
129 int showmsg; 140 int showmsg;
130}; 141};
131 142
132struct cgit_filter {
133 char *cmd;
134 char **argv;
135 int old_stdout;
136 int pipe_fh[2];
137 int pid;
138 int exitstatus;
139};
140
141struct cgit_config { 143struct cgit_config {
142 char *agefile; 144 char *agefile;
143 char *cache_root; 145 char *cache_root;
144 char *clone_prefix; 146 char *clone_prefix;
145 char *css; 147 char *css;
146 char *favicon; 148 char *favicon;
147 char *footer; 149 char *footer;
148 char *head_include; 150 char *head_include;
149 char *header; 151 char *header;
150 char *index_header; 152 char *index_header;
151 char *index_info; 153 char *index_info;
152 char *logo; 154 char *logo;
153 char *logo_link; 155 char *logo_link;
154 char *module_link; 156 char *module_link;
155 char *repo_group; 157 char *repo_group;
156 char *robots; 158 char *robots;
157 char *root_title; 159 char *root_title;
158 char *root_desc; 160 char *root_desc;
159 char *root_readme; 161 char *root_readme;
160 char *script_name; 162 char *script_name;
161 char *virtual_root; 163 char *virtual_root;
162 int cache_size; 164 int cache_size;
163 int cache_dynamic_ttl; 165 int cache_dynamic_ttl;
164 int cache_max_create_time; 166 int cache_max_create_time;
165 int cache_repo_ttl; 167 int cache_repo_ttl;
166 int cache_root_ttl; 168 int cache_root_ttl;
167 int cache_static_ttl; 169 int cache_static_ttl;
168 int embedded; 170 int embedded;
169 int enable_index_links; 171 int enable_index_links;
170 int enable_log_filecount; 172 int enable_log_filecount;
171 int enable_log_linecount; 173 int enable_log_linecount;
172 int local_time; 174 int local_time;
173 int max_repo_count; 175 int max_repo_count;
174 int max_commit_count; 176 int max_commit_count;
175 int max_lock_attempts; 177 int max_lock_attempts;
176 int max_msg_len; 178 int max_msg_len;
177 int max_repodesc_len; 179 int max_repodesc_len;
178 int max_stats; 180 int max_stats;
179 int nocache; 181 int nocache;
180 int noheader; 182 int noheader;
181 int renamelimit; 183 int renamelimit;
182 int snapshots; 184 int snapshots;
183 int summary_branches; 185 int summary_branches;
184 int summary_log; 186 int summary_log;
185 int summary_tags; 187 int summary_tags;
186 struct cgit_filter *commit_filter; 188 struct cgit_filter *commit_filter;
187 struct cgit_filter *source_filter; 189 struct cgit_filter *source_filter;
188}; 190};
189 191
190struct cgit_page { 192struct cgit_page {
191 time_t modified; 193 time_t modified;
192 time_t expires; 194 time_t expires;
193 size_t size; 195 size_t size;
194 char *mimetype; 196 char *mimetype;
195 char *charset; 197 char *charset;
196 char *filename; 198 char *filename;
197 char *etag; 199 char *etag;
198 char *title; 200 char *title;
199 int status; 201 int status;
200 char *statusmsg; 202 char *statusmsg;
201}; 203};
202 204
203struct cgit_context { 205struct cgit_context {
204 struct cgit_query qry; 206 struct cgit_query qry;
205 struct cgit_config cfg; 207 struct cgit_config cfg;
206 struct cgit_repo *repo; 208 struct cgit_repo *repo;
207 struct cgit_page page; 209 struct cgit_page page;
208}; 210};
209 211
210struct cgit_snapshot_format { 212struct cgit_snapshot_format {
211 const char *suffix; 213 const char *suffix;
212 const char *mimetype; 214 const char *mimetype;
213 write_archive_fn_t write_func; 215 write_archive_fn_t write_func;
214 int bit; 216 int bit;
215}; 217};
216 218
217extern const char *cgit_version; 219extern const char *cgit_version;
218 220
219extern struct cgit_repolist cgit_repolist; 221extern struct cgit_repolist cgit_repolist;
220extern struct cgit_context ctx; 222extern struct cgit_context ctx;
221extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 223extern const struct cgit_snapshot_format cgit_snapshot_formats[];
222 224
223extern struct cgit_repo *cgit_add_repo(const char *url); 225extern struct cgit_repo *cgit_add_repo(const char *url);
224extern struct cgit_repo *cgit_get_repoinfo(const char *url); 226extern struct cgit_repo *cgit_get_repoinfo(const char *url);
225extern void cgit_repo_config_cb(const char *name, const char *value); 227extern void cgit_repo_config_cb(const char *name, const char *value);
226 228
227extern int chk_zero(int result, char *msg); 229extern int chk_zero(int result, char *msg);
228extern int chk_positive(int result, char *msg); 230extern int chk_positive(int result, char *msg);
229extern int chk_non_negative(int result, char *msg); 231extern int chk_non_negative(int result, char *msg);
230 232
231extern char *trim_end(const char *str, char c); 233extern char *trim_end(const char *str, char c);
232extern char *strlpart(char *txt, int maxlen); 234extern char *strlpart(char *txt, int maxlen);
233extern char *strrpart(char *txt, int maxlen); 235extern char *strrpart(char *txt, int maxlen);
234 236
235extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 237extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
236extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 238extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 2efd6aa..ffb3e0f 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -145,232 +145,238 @@ max-repo-count::
145 index page. Default value: "50". 145 index page. Default value: "50".
146 146
147max-repodesc-length:: 147max-repodesc-length::
148 Specifies the maximum number of repo description characters to display 148 Specifies the maximum number of repo description characters to display
149 on the repository index page. Default value: "80". 149 on the repository index page. Default value: "80".
150 150
151max-stats:: 151max-stats::
152 Set the default maximum statistics period. Valid values are "week", 152 Set the default maximum statistics period. Valid values are "week",
153 "month", "quarter" and "year". If unspecified, statistics are 153 "month", "quarter" and "year". If unspecified, statistics are
154 disabled. Default value: none. See also: "repo.max-stats". 154 disabled. Default value: none. See also: "repo.max-stats".
155 155
156module-link:: 156module-link::
157 Text which will be used as the formatstring for a hyperlink when a 157 Text which will be used as the formatstring for a hyperlink when a
158 submodule is printed in a directory listing. The arguments for the 158 submodule is printed in a directory listing. The arguments for the
159 formatstring are the path and SHA1 of the submodule commit. Default 159 formatstring are the path and SHA1 of the submodule commit. Default
160 value: "./?repo=%s&page=commit&id=%s" 160 value: "./?repo=%s&page=commit&id=%s"
161 161
162nocache:: 162nocache::
163 If set to the value "1" caching will be disabled. This settings is 163 If set to the value "1" caching will be disabled. This settings is
164 deprecated, and will not be honored starting with cgit-1.0. Default 164 deprecated, and will not be honored starting with cgit-1.0. Default
165 value: "0". 165 value: "0".
166 166
167noheader:: 167noheader::
168 Flag which, when set to "1", will make cgit omit the standard header 168 Flag which, when set to "1", will make cgit omit the standard header
169 on all pages. Default value: none. See also: "embedded". 169 on all pages. Default value: none. See also: "embedded".
170 170
171renamelimit:: 171renamelimit::
172 Maximum number of files to consider when detecting renames. The value 172 Maximum number of files to consider when detecting renames. The value
173 "-1" uses the compiletime value in git (for further info, look at 173 "-1" uses the compiletime value in git (for further info, look at
174 `man git-diff`). Default value: "-1". 174 `man git-diff`). Default value: "-1".
175 175
176repo.group:: 176repo.group::
177 A value for the current repository group, which all repositories 177 A value for the current repository group, which all repositories
178 specified after this setting will inherit. Default value: none. 178 specified after this setting will inherit. Default value: none.
179 179
180robots:: 180robots::
181 Text used as content for the "robots" meta-tag. Default value: 181 Text used as content for the "robots" meta-tag. Default value:
182 "index, nofollow". 182 "index, nofollow".
183 183
184root-desc:: 184root-desc::
185 Text printed below the heading on the repository index page. Default 185 Text printed below the heading on the repository index page. Default
186 value: "a fast webinterface for the git dscm". 186 value: "a fast webinterface for the git dscm".
187 187
188root-readme:: 188root-readme::
189 The content of the file specified with this option will be included 189 The content of the file specified with this option will be included
190 verbatim below the "about" link on the repository index page. Default 190 verbatim below the "about" link on the repository index page. Default
191 value: none. 191 value: none.
192 192
193root-title:: 193root-title::
194 Text printed as heading on the repository index page. Default value: 194 Text printed as heading on the repository index page. Default value:
195 "Git Repository Browser". 195 "Git Repository Browser".
196 196
197snapshots:: 197snapshots::
198 Text which specifies the default (and allowed) set of snapshot formats 198 Text which specifies the default (and allowed) set of snapshot formats
199 supported by cgit. The value is a space-separated list of zero or more 199 supported by cgit. The value is a space-separated list of zero or more
200 of the following values: 200 of the following values:
201 "tar" uncompressed tar-file 201 "tar" uncompressed tar-file
202 "tar.gz"gzip-compressed tar-file 202 "tar.gz"gzip-compressed tar-file
203 "tar.bz2"bzip-compressed tar-file 203 "tar.bz2"bzip-compressed tar-file
204 "zip" zip-file 204 "zip" zip-file
205 Default value: none. 205 Default value: none.
206 206
207source-filter:: 207source-filter::
208 Specifies a command which will be invoked to format plaintext blobs 208 Specifies a command which will be invoked to format plaintext blobs
209 in the tree view. The command will get the blob content on its STDIN 209 in the tree view. The command will get the blob content on its STDIN
210 and the name of the blob as its only command line argument. The STDOUT 210 and the name of the blob as its only command line argument. The STDOUT
211 from the command will be included verbatim as the blob contents, i.e. 211 from the command will be included verbatim as the blob contents, i.e.
212 this can be used to implement e.g. syntax highlighting. Default value: 212 this can be used to implement e.g. syntax highlighting. Default value:
213 none. 213 none.
214 214
215summary-branches:: 215summary-branches::
216 Specifies the number of branches to display in the repository "summary" 216 Specifies the number of branches to display in the repository "summary"
217 view. Default value: "10". 217 view. Default value: "10".
218 218
219summary-log:: 219summary-log::
220 Specifies the number of log entries to display in the repository 220 Specifies the number of log entries to display in the repository
221 "summary" view. Default value: "10". 221 "summary" view. Default value: "10".
222 222
223summary-tags:: 223summary-tags::
224 Specifies the number of tags to display in the repository "summary" 224 Specifies the number of tags to display in the repository "summary"
225 view. Default value: "10". 225 view. Default value: "10".
226 226
227virtual-root:: 227virtual-root::
228 Url which, if specified, will be used as root for all cgit links. It 228 Url which, if specified, will be used as root for all cgit links. It
229 will also cause cgit to generate 'virtual urls', i.e. urls like 229 will also cause cgit to generate 'virtual urls', i.e. urls like
230 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 230 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
231 value: none. 231 value: none.
232 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 232 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
233 same kind of virtual urls, so this option will probably be deprecated. 233 same kind of virtual urls, so this option will probably be deprecated.
234 234
235REPOSITORY SETTINGS 235REPOSITORY SETTINGS
236------------------- 236-------------------
237repo.clone-url:: 237repo.clone-url::
238 A list of space-separated urls which can be used to clone this repo. 238 A list of space-separated urls which can be used to clone this repo.
239 Default value: none. 239 Default value: none.
240 240
241repo.commit-filter::
242 Override the default commit-filter. Default value: <commit-filter>.
243
241repo.defbranch:: 244repo.defbranch::
242 The name of the default branch for this repository. If no such branch 245 The name of the default branch for this repository. If no such branch
243 exists in the repository, the first branch name (when sorted) is used 246 exists in the repository, the first branch name (when sorted) is used
244 as default instead. Default value: "master". 247 as default instead. Default value: "master".
245 248
246repo.desc:: 249repo.desc::
247 The value to show as repository description. Default value: none. 250 The value to show as repository description. Default value: none.
248 251
249repo.enable-log-filecount:: 252repo.enable-log-filecount::
250 A flag which can be used to disable the global setting 253 A flag which can be used to disable the global setting
251 `enable-log-filecount'. Default value: none. 254 `enable-log-filecount'. Default value: none.
252 255
253repo.enable-log-linecount:: 256repo.enable-log-linecount::
254 A flag which can be used to disable the global setting 257 A flag which can be used to disable the global setting
255 `enable-log-linecount'. Default value: none. 258 `enable-log-linecount'. Default value: none.
256 259
257repo.max-stats:: 260repo.max-stats::
258 Override the default maximum statistics period. Valid values are equal 261 Override the default maximum statistics period. Valid values are equal
259 to the values specified for the global "max-stats" setting. Default 262 to the values specified for the global "max-stats" setting. Default
260 value: none. 263 value: none.
261 264
262repo.name:: 265repo.name::
263 The value to show as repository name. Default value: <repo.url>. 266 The value to show as repository name. Default value: <repo.url>.
264 267
265repo.owner:: 268repo.owner::
266 A value used to identify the owner of the repository. Default value: 269 A value used to identify the owner of the repository. Default value:
267 none. 270 none.
268 271
269repo.path:: 272repo.path::
270 An absolute path to the repository directory. For non-bare repositories 273 An absolute path to the repository directory. For non-bare repositories
271 this is the .git-directory. Default value: none. 274 this is the .git-directory. Default value: none.
272 275
273repo.readme:: 276repo.readme::
274 A path (relative to <repo.path>) which specifies a file to include 277 A path (relative to <repo.path>) which specifies a file to include
275 verbatim as the "About" page for this repo. Default value: none. 278 verbatim as the "About" page for this repo. Default value: none.
276 279
277repo.snapshots:: 280repo.snapshots::
278 A mask of allowed snapshot-formats for this repo, restricted by the 281 A mask of allowed snapshot-formats for this repo, restricted by the
279 "snapshots" global setting. Default value: <snapshots>. 282 "snapshots" global setting. Default value: <snapshots>.
280 283
284repo.source-filter::
285 Override the default source-filter. Default value: <source-filter>.
286
281repo.url:: 287repo.url::
282 The relative url used to access the repository. This must be the first 288 The relative url used to access the repository. This must be the first
283 setting specified for each repo. Default value: none. 289 setting specified for each repo. Default value: none.
284 290
285 291
286EXAMPLE CGITRC FILE 292EXAMPLE CGITRC FILE
287------------------- 293-------------------
288 294
289.... 295....
290# Enable caching of up to 1000 output entriess 296# Enable caching of up to 1000 output entriess
291cache-size=1000 297cache-size=1000
292 298
293 299
294# Specify some default clone prefixes 300# Specify some default clone prefixes
295clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git 301clone-prefix=git://foobar.com ssh://foobar.com/pub/git http://foobar.com/git
296 302
297# Specify the css url 303# Specify the css url
298css=/css/cgit.css 304css=/css/cgit.css
299 305
300 306
301# Show extra links for each repository on the index page 307# Show extra links for each repository on the index page
302enable-index-links=1 308enable-index-links=1
303 309
304 310
305# Show number of affected files per commit on the log pages 311# Show number of affected files per commit on the log pages
306enable-log-filecount=1 312enable-log-filecount=1
307 313
308 314
309# Show number of added/removed lines per commit on the log pages 315# Show number of added/removed lines per commit on the log pages
310enable-log-linecount=1 316enable-log-linecount=1
311 317
312 318
313# Add a cgit favicon 319# Add a cgit favicon
314favicon=/favicon.ico 320favicon=/favicon.ico
315 321
316 322
317# Use a custom logo 323# Use a custom logo
318logo=/img/mylogo.png 324logo=/img/mylogo.png
319 325
320 326
321# Enable statistics per week, month and quarter 327# Enable statistics per week, month and quarter
322max-stats=quarter 328max-stats=quarter
323 329
324 330
325# Set the title and heading of the repository index page 331# Set the title and heading of the repository index page
326root-title=foobar.com git repositories 332root-title=foobar.com git repositories
327 333
328 334
329# Set a subheading for the repository index page 335# Set a subheading for the repository index page
330root-desc=tracking the foobar development 336root-desc=tracking the foobar development
331 337
332 338
333# Include some more info about foobar.com on the index page 339# Include some more info about foobar.com on the index page
334root-readme=/var/www/htdocs/about.html 340root-readme=/var/www/htdocs/about.html
335 341
336 342
337# Allow download of tar.gz, tar.bz2 and zip-files 343# Allow download of tar.gz, tar.bz2 and zip-files
338snapshots=tar.gz tar.bz2 zip 344snapshots=tar.gz tar.bz2 zip
339 345
340 346
341## 347##
342## List of repositories. 348## List of repositories.
343## PS: Any repositories listed when repo.group is unset will not be 349## PS: Any repositories listed when repo.group is unset will not be
344## displayed under a group heading 350## displayed under a group heading
345## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos') 351## PPS: This list could be kept in a different file (e.g. '/etc/cgitrepos')
346## and included like this: 352## and included like this:
347## include=/etc/cgitrepos 353## include=/etc/cgitrepos
348## 354##
349 355
350 356
351repo.url=foo 357repo.url=foo
352repo.path=/pub/git/foo.git 358repo.path=/pub/git/foo.git
353repo.desc=the master foo repository 359repo.desc=the master foo repository
354repo.owner=fooman@foobar.com 360repo.owner=fooman@foobar.com
355repo.readme=info/web/about.html 361repo.readme=info/web/about.html
356 362
357 363
358repo.url=bar 364repo.url=bar
359repo.path=/pub/git/bar.git 365repo.path=/pub/git/bar.git
360repo.desc=the bars for your foo 366repo.desc=the bars for your foo
361repo.owner=barman@foobar.com 367repo.owner=barman@foobar.com
362repo.readme=info/web/about.html 368repo.readme=info/web/about.html
363 369
364 370
365# The next repositories will be displayed under the 'extras' heading 371# The next repositories will be displayed under the 'extras' heading
366repo.group=extras 372repo.group=extras
367 373
368 374
369repo.url=baz 375repo.url=baz
370repo.path=/pub/git/baz.git 376repo.path=/pub/git/baz.git
371repo.desc=a set of extensions for bar users 377repo.desc=a set of extensions for bar users
372 378
373repo.url=wiz 379repo.url=wiz
374repo.path=/pub/git/wiz.git 380repo.path=/pub/git/wiz.git
375repo.desc=the wizard of foo 381repo.desc=the wizard of foo
376 382
diff --git a/shared.c b/shared.c
index 288cfa2..783604b 100644
--- a/shared.c
+++ b/shared.c
@@ -1,160 +1,162 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback functions
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11struct cgit_repolist cgit_repolist; 11struct cgit_repolist cgit_repolist;
12struct cgit_context ctx; 12struct cgit_context ctx;
13int cgit_cmd; 13int cgit_cmd;
14 14
15int chk_zero(int result, char *msg) 15int chk_zero(int result, char *msg)
16{ 16{
17 if (result != 0) 17 if (result != 0)
18 die("%s: %s", msg, strerror(errno)); 18 die("%s: %s", msg, strerror(errno));
19 return result; 19 return result;
20} 20}
21 21
22int chk_positive(int result, char *msg) 22int chk_positive(int result, char *msg)
23{ 23{
24 if (result <= 0) 24 if (result <= 0)
25 die("%s: %s", msg, strerror(errno)); 25 die("%s: %s", msg, strerror(errno));
26 return result; 26 return result;
27} 27}
28 28
29int chk_non_negative(int result, char *msg) 29int chk_non_negative(int result, char *msg)
30{ 30{
31 if (result < 0) 31 if (result < 0)
32 die("%s: %s",msg, strerror(errno)); 32 die("%s: %s",msg, strerror(errno));
33 return result; 33 return result;
34} 34}
35 35
36struct cgit_repo *cgit_add_repo(const char *url) 36struct cgit_repo *cgit_add_repo(const char *url)
37{ 37{
38 struct cgit_repo *ret; 38 struct cgit_repo *ret;
39 39
40 if (++cgit_repolist.count > cgit_repolist.length) { 40 if (++cgit_repolist.count > cgit_repolist.length) {
41 if (cgit_repolist.length == 0) 41 if (cgit_repolist.length == 0)
42 cgit_repolist.length = 8; 42 cgit_repolist.length = 8;
43 else 43 else
44 cgit_repolist.length *= 2; 44 cgit_repolist.length *= 2;
45 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 45 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
46 cgit_repolist.length * 46 cgit_repolist.length *
47 sizeof(struct cgit_repo)); 47 sizeof(struct cgit_repo));
48 } 48 }
49 49
50 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 50 ret = &cgit_repolist.repos[cgit_repolist.count-1];
51 ret->url = trim_end(url, '/'); 51 ret->url = trim_end(url, '/');
52 ret->name = ret->url; 52 ret->name = ret->url;
53 ret->path = NULL; 53 ret->path = NULL;
54 ret->desc = "[no description]"; 54 ret->desc = "[no description]";
55 ret->owner = NULL; 55 ret->owner = NULL;
56 ret->group = ctx.cfg.repo_group; 56 ret->group = ctx.cfg.repo_group;
57 ret->defbranch = "master"; 57 ret->defbranch = "master";
58 ret->snapshots = ctx.cfg.snapshots; 58 ret->snapshots = ctx.cfg.snapshots;
59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->max_stats = ctx.cfg.max_stats; 61 ret->max_stats = ctx.cfg.max_stats;
62 ret->module_link = ctx.cfg.module_link; 62 ret->module_link = ctx.cfg.module_link;
63 ret->readme = NULL; 63 ret->readme = NULL;
64 ret->mtime = -1; 64 ret->mtime = -1;
65 ret->commit_filter = ctx.cfg.commit_filter;
66 ret->source_filter = ctx.cfg.source_filter;
65 return ret; 67 return ret;
66} 68}
67 69
68struct cgit_repo *cgit_get_repoinfo(const char *url) 70struct cgit_repo *cgit_get_repoinfo(const char *url)
69{ 71{
70 int i; 72 int i;
71 struct cgit_repo *repo; 73 struct cgit_repo *repo;
72 74
73 for (i=0; i<cgit_repolist.count; i++) { 75 for (i=0; i<cgit_repolist.count; i++) {
74 repo = &cgit_repolist.repos[i]; 76 repo = &cgit_repolist.repos[i];
75 if (!strcmp(repo->url, url)) 77 if (!strcmp(repo->url, url))
76 return repo; 78 return repo;
77 } 79 }
78 return NULL; 80 return NULL;
79} 81}
80 82
81void *cgit_free_commitinfo(struct commitinfo *info) 83void *cgit_free_commitinfo(struct commitinfo *info)
82{ 84{
83 free(info->author); 85 free(info->author);
84 free(info->author_email); 86 free(info->author_email);
85 free(info->committer); 87 free(info->committer);
86 free(info->committer_email); 88 free(info->committer_email);
87 free(info->subject); 89 free(info->subject);
88 free(info->msg); 90 free(info->msg);
89 free(info->msg_encoding); 91 free(info->msg_encoding);
90 free(info); 92 free(info);
91 return NULL; 93 return NULL;
92} 94}
93 95
94char *trim_end(const char *str, char c) 96char *trim_end(const char *str, char c)
95{ 97{
96 int len; 98 int len;
97 char *s, *t; 99 char *s, *t;
98 100
99 if (str == NULL) 101 if (str == NULL)
100 return NULL; 102 return NULL;
101 t = (char *)str; 103 t = (char *)str;
102 len = strlen(t); 104 len = strlen(t);
103 while(len > 0 && t[len - 1] == c) 105 while(len > 0 && t[len - 1] == c)
104 len--; 106 len--;
105 107
106 if (len == 0) 108 if (len == 0)
107 return NULL; 109 return NULL;
108 110
109 c = t[len]; 111 c = t[len];
110 t[len] = '\0'; 112 t[len] = '\0';
111 s = xstrdup(t); 113 s = xstrdup(t);
112 t[len] = c; 114 t[len] = c;
113 return s; 115 return s;
114} 116}
115 117
116char *strlpart(char *txt, int maxlen) 118char *strlpart(char *txt, int maxlen)
117{ 119{
118 char *result; 120 char *result;
119 121
120 if (!txt) 122 if (!txt)
121 return txt; 123 return txt;
122 124
123 if (strlen(txt) <= maxlen) 125 if (strlen(txt) <= maxlen)
124 return txt; 126 return txt;
125 result = xmalloc(maxlen + 1); 127 result = xmalloc(maxlen + 1);
126 memcpy(result, txt, maxlen - 3); 128 memcpy(result, txt, maxlen - 3);
127 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.'; 129 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.';
128 result[maxlen] = '\0'; 130 result[maxlen] = '\0';
129 return result; 131 return result;
130} 132}
131 133
132char *strrpart(char *txt, int maxlen) 134char *strrpart(char *txt, int maxlen)
133{ 135{
134 char *result; 136 char *result;
135 137
136 if (!txt) 138 if (!txt)
137 return txt; 139 return txt;
138 140
139 if (strlen(txt) <= maxlen) 141 if (strlen(txt) <= maxlen)
140 return txt; 142 return txt;
141 result = xmalloc(maxlen + 1); 143 result = xmalloc(maxlen + 1);
142 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3); 144 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
143 result[0] = result[1] = result[2] = '.'; 145 result[0] = result[1] = result[2] = '.';
144 return result; 146 return result;
145} 147}
146 148
147void cgit_add_ref(struct reflist *list, struct refinfo *ref) 149void cgit_add_ref(struct reflist *list, struct refinfo *ref)
148{ 150{
149 size_t size; 151 size_t size;
150 152
151 if (list->count >= list->alloc) { 153 if (list->count >= list->alloc) {
152 list->alloc += (list->alloc ? list->alloc : 4); 154 list->alloc += (list->alloc ? list->alloc : 4);
153 size = list->alloc * sizeof(struct refinfo *); 155 size = list->alloc * sizeof(struct refinfo *);
154 list->refs = xrealloc(list->refs, size); 156 list->refs = xrealloc(list->refs, size);
155 } 157 }
156 list->refs[list->count++] = ref; 158 list->refs[list->count++] = ref;
157} 159}
158 160
159struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 161struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
160{ 162{
diff --git a/ui-commit.c b/ui-commit.c
index ee0e139..5815b58 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,114 +1,114 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
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#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12#include "ui-diff.h" 12#include "ui-diff.h"
13#include "ui-log.h" 13#include "ui-log.h"
14 14
15void cgit_print_commit(char *hex) 15void cgit_print_commit(char *hex)
16{ 16{
17 struct commit *commit, *parent; 17 struct commit *commit, *parent;
18 struct commitinfo *info; 18 struct commitinfo *info;
19 struct commit_list *p; 19 struct commit_list *p;
20 unsigned char sha1[20]; 20 unsigned char sha1[20];
21 char *tmp; 21 char *tmp;
22 int parents = 0; 22 int parents = 0;
23 23
24 if (!hex) 24 if (!hex)
25 hex = ctx.qry.head; 25 hex = ctx.qry.head;
26 26
27 if (get_sha1(hex, sha1)) { 27 if (get_sha1(hex, sha1)) {
28 cgit_print_error(fmt("Bad object id: %s", hex)); 28 cgit_print_error(fmt("Bad object id: %s", hex));
29 return; 29 return;
30 } 30 }
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 html(" ");
44 html_txt(info->author_email); 44 html_txt(info->author_email);
45 html("</td><td class='right'>"); 45 html("</td><td class='right'>");
46 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time); 46 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time);
47 html("</td></tr>\n"); 47 html("</td></tr>\n");
48 html("<tr><th>committer</th><td>"); 48 html("<tr><th>committer</th><td>");
49 html_txt(info->committer); 49 html_txt(info->committer);
50 html(" "); 50 html(" ");
51 html_txt(info->committer_email); 51 html_txt(info->committer_email);
52 html("</td><td class='right'>"); 52 html("</td><td class='right'>");
53 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time); 53 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time);
54 html("</td></tr>\n"); 54 html("</td></tr>\n");
55 html("<tr><th>commit</th><td colspan='2' class='sha1'>"); 55 html("<tr><th>commit</th><td colspan='2' class='sha1'>");
56 tmp = sha1_to_hex(commit->object.sha1); 56 tmp = sha1_to_hex(commit->object.sha1);
57 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp); 57 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp);
58 html(" ("); 58 html(" (");
59 cgit_patch_link("patch", NULL, NULL, NULL, tmp); 59 cgit_patch_link("patch", NULL, NULL, NULL, tmp);
60 html(")</td></tr>\n"); 60 html(")</td></tr>\n");
61 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 61 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
62 tmp = xstrdup(hex); 62 tmp = xstrdup(hex);
63 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, 63 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
64 ctx.qry.head, tmp, NULL); 64 ctx.qry.head, tmp, NULL);
65 html("</td></tr>\n"); 65 html("</td></tr>\n");
66 for (p = commit->parents; p ; p = p->next) { 66 for (p = commit->parents; p ; p = p->next) {
67 parent = lookup_commit_reference(p->item->object.sha1); 67 parent = lookup_commit_reference(p->item->object.sha1);
68 if (!parent) { 68 if (!parent) {
69 html("<tr><td colspan='3'>"); 69 html("<tr><td colspan='3'>");
70 cgit_print_error("Error reading parent commit"); 70 cgit_print_error("Error reading parent commit");
71 html("</td></tr>"); 71 html("</td></tr>");
72 continue; 72 continue;
73 } 73 }
74 html("<tr><th>parent</th>" 74 html("<tr><th>parent</th>"
75 "<td colspan='2' class='sha1'>"); 75 "<td colspan='2' class='sha1'>");
76 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 76 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
77 ctx.qry.head, sha1_to_hex(p->item->object.sha1)); 77 ctx.qry.head, sha1_to_hex(p->item->object.sha1));
78 html(" ("); 78 html(" (");
79 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, 79 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
80 sha1_to_hex(p->item->object.sha1), NULL); 80 sha1_to_hex(p->item->object.sha1), NULL);
81 html(")</td></tr>"); 81 html(")</td></tr>");
82 parents++; 82 parents++;
83 } 83 }
84 if (ctx.repo->snapshots) { 84 if (ctx.repo->snapshots) {
85 html("<tr><th>download</th><td colspan='2' class='sha1'>"); 85 html("<tr><th>download</th><td colspan='2' class='sha1'>");
86 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, 86 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
87 hex, ctx.repo->snapshots); 87 hex, ctx.repo->snapshots);
88 html("</td></tr>"); 88 html("</td></tr>");
89 } 89 }
90 html("</table>\n"); 90 html("</table>\n");
91 html("<div class='commit-subject'>"); 91 html("<div class='commit-subject'>");
92 if (ctx.cfg.commit_filter) 92 if (ctx.repo->commit_filter)
93 cgit_open_filter(ctx.cfg.commit_filter); 93 cgit_open_filter(ctx.repo->commit_filter);
94 html_txt(info->subject); 94 html_txt(info->subject);
95 if (ctx.cfg.commit_filter) 95 if (ctx.repo->commit_filter)
96 cgit_close_filter(ctx.cfg.commit_filter); 96 cgit_close_filter(ctx.repo->commit_filter);
97 show_commit_decorations(commit); 97 show_commit_decorations(commit);
98 html("</div>"); 98 html("</div>");
99 html("<div class='commit-msg'>"); 99 html("<div class='commit-msg'>");
100 if (ctx.cfg.commit_filter) 100 if (ctx.repo->commit_filter)
101 cgit_open_filter(ctx.cfg.commit_filter); 101 cgit_open_filter(ctx.repo->commit_filter);
102 html_txt(info->msg); 102 html_txt(info->msg);
103 if (ctx.cfg.commit_filter) 103 if (ctx.repo->commit_filter)
104 cgit_close_filter(ctx.cfg.commit_filter); 104 cgit_close_filter(ctx.repo->commit_filter);
105 html("</div>"); 105 html("</div>");
106 if (parents < 3) { 106 if (parents < 3) {
107 if (parents) 107 if (parents)
108 tmp = sha1_to_hex(commit->parents->item->object.sha1); 108 tmp = sha1_to_hex(commit->parents->item->object.sha1);
109 else 109 else
110 tmp = NULL; 110 tmp = NULL;
111 cgit_print_diff(ctx.qry.sha1, tmp, NULL); 111 cgit_print_diff(ctx.qry.sha1, tmp, NULL);
112 } 112 }
113 cgit_free_commitinfo(info); 113 cgit_free_commitinfo(info);
114} 114}
diff --git a/ui-tree.c b/ui-tree.c
index 816e121..caf6a9e 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -1,126 +1,126 @@
1/* ui-tree.c: functions for tree output 1/* ui-tree.c: functions for tree 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 <ctype.h> 9#include <ctype.h>
10#include "cgit.h" 10#include "cgit.h"
11#include "html.h" 11#include "html.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13 13
14char *curr_rev; 14char *curr_rev;
15char *match_path; 15char *match_path;
16int header = 0; 16int header = 0;
17 17
18static void print_text_buffer(const char *name, char *buf, unsigned long size) 18static void print_text_buffer(const char *name, char *buf, unsigned long size)
19{ 19{
20 unsigned long lineno, idx; 20 unsigned long lineno, idx;
21 const char *numberfmt = 21 const char *numberfmt =
22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; 22 "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n";
23 23
24 html("<table summary='blob content' class='blob'>\n"); 24 html("<table summary='blob content' class='blob'>\n");
25 if (ctx.cfg.source_filter) { 25 if (ctx.repo->source_filter) {
26 html("<tr><td class='lines'><pre><code>"); 26 html("<tr><td class='lines'><pre><code>");
27 ctx.cfg.source_filter->argv[1] = xstrdup(name); 27 ctx.repo->source_filter->argv[1] = xstrdup(name);
28 cgit_open_filter(ctx.cfg.source_filter); 28 cgit_open_filter(ctx.repo->source_filter);
29 write(STDOUT_FILENO, buf, size); 29 write(STDOUT_FILENO, buf, size);
30 cgit_close_filter(ctx.cfg.source_filter); 30 cgit_close_filter(ctx.repo->source_filter);
31 html("</code></pre></td></tr></table>\n"); 31 html("</code></pre></td></tr></table>\n");
32 return; 32 return;
33 } 33 }
34 34
35 html("<tr><td class='linenumbers'><pre>"); 35 html("<tr><td class='linenumbers'><pre>");
36 idx = 0; 36 idx = 0;
37 lineno = 0; 37 lineno = 0;
38 38
39 if (size) { 39 if (size) {
40 htmlf(numberfmt, ++lineno); 40 htmlf(numberfmt, ++lineno);
41 while(idx < size - 1) { // skip absolute last newline 41 while(idx < size - 1) { // skip absolute last newline
42 if (buf[idx] == '\n') 42 if (buf[idx] == '\n')
43 htmlf(numberfmt, ++lineno); 43 htmlf(numberfmt, ++lineno);
44 idx++; 44 idx++;
45 } 45 }
46 } 46 }
47 html("</pre></td>\n"); 47 html("</pre></td>\n");
48 html("<td class='lines'><pre><code>"); 48 html("<td class='lines'><pre><code>");
49 html_txt(buf); 49 html_txt(buf);
50 html("</code></pre></td></tr></table>\n"); 50 html("</code></pre></td></tr></table>\n");
51} 51}
52 52
53#define ROWLEN 32 53#define ROWLEN 32
54 54
55static void print_binary_buffer(char *buf, unsigned long size) 55static void print_binary_buffer(char *buf, unsigned long size)
56{ 56{
57 unsigned long ofs, idx; 57 unsigned long ofs, idx;
58 static char ascii[ROWLEN + 1]; 58 static char ascii[ROWLEN + 1];
59 59
60 html("<table summary='blob content' class='bin-blob'>\n"); 60 html("<table summary='blob content' class='bin-blob'>\n");
61 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); 61 html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>");
62 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { 62 for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) {
63 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); 63 htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs);
64 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 64 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
65 htmlf("%*s%02x", 65 htmlf("%*s%02x",
66 idx == 16 ? 4 : 1, "", 66 idx == 16 ? 4 : 1, "",
67 buf[idx] & 0xff); 67 buf[idx] & 0xff);
68 html(" </td><td class='hex'>"); 68 html(" </td><td class='hex'>");
69 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) 69 for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++)
70 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; 70 ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.';
71 ascii[idx] = '\0'; 71 ascii[idx] = '\0';
72 html_txt(ascii); 72 html_txt(ascii);
73 html("</td></tr>\n"); 73 html("</td></tr>\n");
74 } 74 }
75 html("</table>\n"); 75 html("</table>\n");
76} 76}
77 77
78static void print_object(const unsigned char *sha1, char *path, const char *basename) 78static void print_object(const unsigned char *sha1, char *path, const char *basename)
79{ 79{
80 enum object_type type; 80 enum object_type type;
81 char *buf; 81 char *buf;
82 unsigned long size; 82 unsigned long size;
83 83
84 type = sha1_object_info(sha1, &size); 84 type = sha1_object_info(sha1, &size);
85 if (type == OBJ_BAD) { 85 if (type == OBJ_BAD) {
86 cgit_print_error(fmt("Bad object name: %s", 86 cgit_print_error(fmt("Bad object name: %s",
87 sha1_to_hex(sha1))); 87 sha1_to_hex(sha1)));
88 return; 88 return;
89 } 89 }
90 90
91 buf = read_sha1_file(sha1, &type, &size); 91 buf = read_sha1_file(sha1, &type, &size);
92 if (!buf) { 92 if (!buf) {
93 cgit_print_error(fmt("Error reading object %s", 93 cgit_print_error(fmt("Error reading object %s",
94 sha1_to_hex(sha1))); 94 sha1_to_hex(sha1)));
95 return; 95 return;
96 } 96 }
97 97
98 html(" ("); 98 html(" (");
99 cgit_plain_link("plain", NULL, NULL, ctx.qry.head, 99 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
100 curr_rev, path); 100 curr_rev, path);
101 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); 101 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1));
102 102
103 if (buffer_is_binary(buf, size)) 103 if (buffer_is_binary(buf, size))
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 enum object_type type; 116 enum object_type type;
117 unsigned long size = 0; 117 unsigned long size = 0;
118 118
119 name = xstrdup(pathname); 119 name = xstrdup(pathname);
120 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 120 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
121 ctx.qry.path ? "/" : "", name); 121 ctx.qry.path ? "/" : "", name);
122 122
123 if (!S_ISGITLINK(mode)) { 123 if (!S_ISGITLINK(mode)) {
124 type = sha1_object_info(sha1, &size); 124 type = sha1_object_info(sha1, &size);
125 if (type == OBJ_BAD) { 125 if (type == OBJ_BAD) {
126 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 126 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",