summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.h3
-rw-r--r--shared.c5
-rw-r--r--ui-diff.c4
-rw-r--r--ui-log.c2
-rw-r--r--ui-patch.c2
6 files changed, 11 insertions, 7 deletions
diff --git a/cgit.c b/cgit.c
index 2c3ad73..e9bafb5 100644
--- a/cgit.c
+++ b/cgit.c
@@ -125,256 +125,258 @@ void config_cb(const char *name, const char *value)
125 ctx.cfg.virtual_root = ""; 125 ctx.cfg.virtual_root = "";
126 } else if (!strcmp(name, "nocache")) 126 } else if (!strcmp(name, "nocache"))
127 ctx.cfg.nocache = atoi(value); 127 ctx.cfg.nocache = atoi(value);
128 else if (!strcmp(name, "noplainemail")) 128 else if (!strcmp(name, "noplainemail"))
129 ctx.cfg.noplainemail = atoi(value); 129 ctx.cfg.noplainemail = atoi(value);
130 else if (!strcmp(name, "noheader")) 130 else if (!strcmp(name, "noheader"))
131 ctx.cfg.noheader = atoi(value); 131 ctx.cfg.noheader = atoi(value);
132 else if (!strcmp(name, "snapshots")) 132 else if (!strcmp(name, "snapshots"))
133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
134 else if (!strcmp(name, "enable-filter-overrides")) 134 else if (!strcmp(name, "enable-filter-overrides"))
135 ctx.cfg.enable_filter_overrides = atoi(value); 135 ctx.cfg.enable_filter_overrides = atoi(value);
136 else if (!strcmp(name, "enable-index-links")) 136 else if (!strcmp(name, "enable-index-links"))
137 ctx.cfg.enable_index_links = atoi(value); 137 ctx.cfg.enable_index_links = atoi(value);
138 else if (!strcmp(name, "enable-log-filecount")) 138 else if (!strcmp(name, "enable-log-filecount"))
139 ctx.cfg.enable_log_filecount = atoi(value); 139 ctx.cfg.enable_log_filecount = atoi(value);
140 else if (!strcmp(name, "enable-log-linecount")) 140 else if (!strcmp(name, "enable-log-linecount"))
141 ctx.cfg.enable_log_linecount = atoi(value); 141 ctx.cfg.enable_log_linecount = atoi(value);
142 else if (!strcmp(name, "enable-remote-branches")) 142 else if (!strcmp(name, "enable-remote-branches"))
143 ctx.cfg.enable_remote_branches = atoi(value); 143 ctx.cfg.enable_remote_branches = atoi(value);
144 else if (!strcmp(name, "enable-tree-linenumbers")) 144 else if (!strcmp(name, "enable-tree-linenumbers"))
145 ctx.cfg.enable_tree_linenumbers = atoi(value); 145 ctx.cfg.enable_tree_linenumbers = atoi(value);
146 else if (!strcmp(name, "max-stats")) 146 else if (!strcmp(name, "max-stats"))
147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
148 else if (!strcmp(name, "cache-size")) 148 else if (!strcmp(name, "cache-size"))
149 ctx.cfg.cache_size = atoi(value); 149 ctx.cfg.cache_size = atoi(value);
150 else if (!strcmp(name, "cache-root")) 150 else if (!strcmp(name, "cache-root"))
151 ctx.cfg.cache_root = xstrdup(value); 151 ctx.cfg.cache_root = xstrdup(value);
152 else if (!strcmp(name, "cache-root-ttl")) 152 else if (!strcmp(name, "cache-root-ttl"))
153 ctx.cfg.cache_root_ttl = atoi(value); 153 ctx.cfg.cache_root_ttl = atoi(value);
154 else if (!strcmp(name, "cache-repo-ttl")) 154 else if (!strcmp(name, "cache-repo-ttl"))
155 ctx.cfg.cache_repo_ttl = atoi(value); 155 ctx.cfg.cache_repo_ttl = atoi(value);
156 else if (!strcmp(name, "cache-scanrc-ttl")) 156 else if (!strcmp(name, "cache-scanrc-ttl"))
157 ctx.cfg.cache_scanrc_ttl = atoi(value); 157 ctx.cfg.cache_scanrc_ttl = atoi(value);
158 else if (!strcmp(name, "cache-static-ttl")) 158 else if (!strcmp(name, "cache-static-ttl"))
159 ctx.cfg.cache_static_ttl = atoi(value); 159 ctx.cfg.cache_static_ttl = atoi(value);
160 else if (!strcmp(name, "cache-dynamic-ttl")) 160 else if (!strcmp(name, "cache-dynamic-ttl"))
161 ctx.cfg.cache_dynamic_ttl = atoi(value); 161 ctx.cfg.cache_dynamic_ttl = atoi(value);
162 else if (!strcmp(name, "about-filter")) 162 else if (!strcmp(name, "about-filter"))
163 ctx.cfg.about_filter = new_filter(value, 0); 163 ctx.cfg.about_filter = new_filter(value, 0);
164 else if (!strcmp(name, "commit-filter")) 164 else if (!strcmp(name, "commit-filter"))
165 ctx.cfg.commit_filter = new_filter(value, 0); 165 ctx.cfg.commit_filter = new_filter(value, 0);
166 else if (!strcmp(name, "embedded")) 166 else if (!strcmp(name, "embedded"))
167 ctx.cfg.embedded = atoi(value); 167 ctx.cfg.embedded = atoi(value);
168 else if (!strcmp(name, "max-message-length")) 168 else if (!strcmp(name, "max-message-length"))
169 ctx.cfg.max_msg_len = atoi(value); 169 ctx.cfg.max_msg_len = atoi(value);
170 else if (!strcmp(name, "max-repodesc-length")) 170 else if (!strcmp(name, "max-repodesc-length"))
171 ctx.cfg.max_repodesc_len = atoi(value); 171 ctx.cfg.max_repodesc_len = atoi(value);
172 else if (!strcmp(name, "max-blob-size")) 172 else if (!strcmp(name, "max-blob-size"))
173 ctx.cfg.max_blob_size = atoi(value); 173 ctx.cfg.max_blob_size = atoi(value);
174 else if (!strcmp(name, "max-repo-count")) 174 else if (!strcmp(name, "max-repo-count"))
175 ctx.cfg.max_repo_count = atoi(value); 175 ctx.cfg.max_repo_count = atoi(value);
176 else if (!strcmp(name, "max-commit-count")) 176 else if (!strcmp(name, "max-commit-count"))
177 ctx.cfg.max_commit_count = atoi(value); 177 ctx.cfg.max_commit_count = atoi(value);
178 else if (!strcmp(name, "scan-path")) 178 else if (!strcmp(name, "scan-path"))
179 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 179 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
180 process_cached_repolist(value); 180 process_cached_repolist(value);
181 else 181 else
182 scan_tree(value, repo_config); 182 scan_tree(value, repo_config);
183 else if (!strcmp(name, "source-filter")) 183 else if (!strcmp(name, "source-filter"))
184 ctx.cfg.source_filter = new_filter(value, 1); 184 ctx.cfg.source_filter = new_filter(value, 1);
185 else if (!strcmp(name, "summary-log")) 185 else if (!strcmp(name, "summary-log"))
186 ctx.cfg.summary_log = atoi(value); 186 ctx.cfg.summary_log = atoi(value);
187 else if (!strcmp(name, "summary-branches")) 187 else if (!strcmp(name, "summary-branches"))
188 ctx.cfg.summary_branches = atoi(value); 188 ctx.cfg.summary_branches = atoi(value);
189 else if (!strcmp(name, "summary-tags")) 189 else if (!strcmp(name, "summary-tags"))
190 ctx.cfg.summary_tags = atoi(value); 190 ctx.cfg.summary_tags = atoi(value);
191 else if (!strcmp(name, "side-by-side-diffs")) 191 else if (!strcmp(name, "side-by-side-diffs"))
192 ctx.cfg.ssdiff = atoi(value); 192 ctx.cfg.ssdiff = atoi(value);
193 else if (!strcmp(name, "agefile")) 193 else if (!strcmp(name, "agefile"))
194 ctx.cfg.agefile = xstrdup(value); 194 ctx.cfg.agefile = xstrdup(value);
195 else if (!strcmp(name, "renamelimit")) 195 else if (!strcmp(name, "renamelimit"))
196 ctx.cfg.renamelimit = atoi(value); 196 ctx.cfg.renamelimit = atoi(value);
197 else if (!strcmp(name, "robots")) 197 else if (!strcmp(name, "robots"))
198 ctx.cfg.robots = xstrdup(value); 198 ctx.cfg.robots = xstrdup(value);
199 else if (!strcmp(name, "clone-prefix")) 199 else if (!strcmp(name, "clone-prefix"))
200 ctx.cfg.clone_prefix = xstrdup(value); 200 ctx.cfg.clone_prefix = xstrdup(value);
201 else if (!strcmp(name, "local-time")) 201 else if (!strcmp(name, "local-time"))
202 ctx.cfg.local_time = atoi(value); 202 ctx.cfg.local_time = atoi(value);
203 else if (!prefixcmp(name, "mimetype.")) 203 else if (!prefixcmp(name, "mimetype."))
204 add_mimetype(name + 9, value); 204 add_mimetype(name + 9, value);
205 else if (!strcmp(name, "include")) 205 else if (!strcmp(name, "include"))
206 parse_configfile(value, config_cb); 206 parse_configfile(value, config_cb);
207} 207}
208 208
209static void querystring_cb(const char *name, const char *value) 209static void querystring_cb(const char *name, const char *value)
210{ 210{
211 if (!value) 211 if (!value)
212 value = ""; 212 value = "";
213 213
214 if (!strcmp(name,"r")) { 214 if (!strcmp(name,"r")) {
215 ctx.qry.repo = xstrdup(value); 215 ctx.qry.repo = xstrdup(value);
216 ctx.repo = cgit_get_repoinfo(value); 216 ctx.repo = cgit_get_repoinfo(value);
217 } else if (!strcmp(name, "p")) { 217 } else if (!strcmp(name, "p")) {
218 ctx.qry.page = xstrdup(value); 218 ctx.qry.page = xstrdup(value);
219 } else if (!strcmp(name, "url")) { 219 } else if (!strcmp(name, "url")) {
220 if (*value == '/') 220 if (*value == '/')
221 value++; 221 value++;
222 ctx.qry.url = xstrdup(value); 222 ctx.qry.url = xstrdup(value);
223 cgit_parse_url(value); 223 cgit_parse_url(value);
224 } else if (!strcmp(name, "qt")) { 224 } else if (!strcmp(name, "qt")) {
225 ctx.qry.grep = xstrdup(value); 225 ctx.qry.grep = xstrdup(value);
226 } else if (!strcmp(name, "q")) { 226 } else if (!strcmp(name, "q")) {
227 ctx.qry.search = xstrdup(value); 227 ctx.qry.search = xstrdup(value);
228 } else if (!strcmp(name, "h")) { 228 } else if (!strcmp(name, "h")) {
229 ctx.qry.head = xstrdup(value); 229 ctx.qry.head = xstrdup(value);
230 ctx.qry.has_symref = 1; 230 ctx.qry.has_symref = 1;
231 } else if (!strcmp(name, "id")) { 231 } else if (!strcmp(name, "id")) {
232 ctx.qry.sha1 = xstrdup(value); 232 ctx.qry.sha1 = xstrdup(value);
233 ctx.qry.has_sha1 = 1; 233 ctx.qry.has_sha1 = 1;
234 } else if (!strcmp(name, "id2")) { 234 } else if (!strcmp(name, "id2")) {
235 ctx.qry.sha2 = xstrdup(value); 235 ctx.qry.sha2 = xstrdup(value);
236 ctx.qry.has_sha1 = 1; 236 ctx.qry.has_sha1 = 1;
237 } else if (!strcmp(name, "ofs")) { 237 } else if (!strcmp(name, "ofs")) {
238 ctx.qry.ofs = atoi(value); 238 ctx.qry.ofs = atoi(value);
239 } else if (!strcmp(name, "path")) { 239 } else if (!strcmp(name, "path")) {
240 ctx.qry.path = trim_end(value, '/'); 240 ctx.qry.path = trim_end(value, '/');
241 } else if (!strcmp(name, "name")) { 241 } else if (!strcmp(name, "name")) {
242 ctx.qry.name = xstrdup(value); 242 ctx.qry.name = xstrdup(value);
243 } else if (!strcmp(name, "mimetype")) { 243 } else if (!strcmp(name, "mimetype")) {
244 ctx.qry.mimetype = xstrdup(value); 244 ctx.qry.mimetype = xstrdup(value);
245 } else if (!strcmp(name, "s")){ 245 } else if (!strcmp(name, "s")){
246 ctx.qry.sort = xstrdup(value); 246 ctx.qry.sort = xstrdup(value);
247 } else if (!strcmp(name, "showmsg")) { 247 } else if (!strcmp(name, "showmsg")) {
248 ctx.qry.showmsg = atoi(value); 248 ctx.qry.showmsg = atoi(value);
249 } else if (!strcmp(name, "period")) { 249 } else if (!strcmp(name, "period")) {
250 ctx.qry.period = xstrdup(value); 250 ctx.qry.period = xstrdup(value);
251 } else if (!strcmp(name, "ss")) { 251 } else if (!strcmp(name, "ss")) {
252 ctx.qry.ssdiff = atoi(value); 252 ctx.qry.ssdiff = atoi(value);
253 } else if (!strcmp(name, "context")) {
254 ctx.qry.context = atoi(value);
253 } 255 }
254} 256}
255 257
256char *xstrdupn(const char *str) 258char *xstrdupn(const char *str)
257{ 259{
258 return (str ? xstrdup(str) : NULL); 260 return (str ? xstrdup(str) : NULL);
259} 261}
260 262
261static void prepare_context(struct cgit_context *ctx) 263static void prepare_context(struct cgit_context *ctx)
262{ 264{
263 memset(ctx, 0, sizeof(*ctx)); 265 memset(ctx, 0, sizeof(*ctx));
264 ctx->cfg.agefile = "info/web/last-modified"; 266 ctx->cfg.agefile = "info/web/last-modified";
265 ctx->cfg.nocache = 0; 267 ctx->cfg.nocache = 0;
266 ctx->cfg.cache_size = 0; 268 ctx->cfg.cache_size = 0;
267 ctx->cfg.cache_dynamic_ttl = 5; 269 ctx->cfg.cache_dynamic_ttl = 5;
268 ctx->cfg.cache_max_create_time = 5; 270 ctx->cfg.cache_max_create_time = 5;
269 ctx->cfg.cache_repo_ttl = 5; 271 ctx->cfg.cache_repo_ttl = 5;
270 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 272 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
271 ctx->cfg.cache_root_ttl = 5; 273 ctx->cfg.cache_root_ttl = 5;
272 ctx->cfg.cache_scanrc_ttl = 15; 274 ctx->cfg.cache_scanrc_ttl = 15;
273 ctx->cfg.cache_static_ttl = -1; 275 ctx->cfg.cache_static_ttl = -1;
274 ctx->cfg.css = "/cgit.css"; 276 ctx->cfg.css = "/cgit.css";
275 ctx->cfg.logo = "/cgit.png"; 277 ctx->cfg.logo = "/cgit.png";
276 ctx->cfg.local_time = 0; 278 ctx->cfg.local_time = 0;
277 ctx->cfg.enable_tree_linenumbers = 1; 279 ctx->cfg.enable_tree_linenumbers = 1;
278 ctx->cfg.max_repo_count = 50; 280 ctx->cfg.max_repo_count = 50;
279 ctx->cfg.max_commit_count = 50; 281 ctx->cfg.max_commit_count = 50;
280 ctx->cfg.max_lock_attempts = 5; 282 ctx->cfg.max_lock_attempts = 5;
281 ctx->cfg.max_msg_len = 80; 283 ctx->cfg.max_msg_len = 80;
282 ctx->cfg.max_repodesc_len = 80; 284 ctx->cfg.max_repodesc_len = 80;
283 ctx->cfg.max_blob_size = 0; 285 ctx->cfg.max_blob_size = 0;
284 ctx->cfg.max_stats = 0; 286 ctx->cfg.max_stats = 0;
285 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 287 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
286 ctx->cfg.renamelimit = -1; 288 ctx->cfg.renamelimit = -1;
287 ctx->cfg.robots = "index, nofollow"; 289 ctx->cfg.robots = "index, nofollow";
288 ctx->cfg.root_title = "Git repository browser"; 290 ctx->cfg.root_title = "Git repository browser";
289 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 291 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
290 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 292 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
291 ctx->cfg.section = ""; 293 ctx->cfg.section = "";
292 ctx->cfg.summary_branches = 10; 294 ctx->cfg.summary_branches = 10;
293 ctx->cfg.summary_log = 10; 295 ctx->cfg.summary_log = 10;
294 ctx->cfg.summary_tags = 10; 296 ctx->cfg.summary_tags = 10;
295 ctx->cfg.ssdiff = 0; 297 ctx->cfg.ssdiff = 0;
296 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 298 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
297 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 299 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
298 ctx->env.https = xstrdupn(getenv("HTTPS")); 300 ctx->env.https = xstrdupn(getenv("HTTPS"));
299 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 301 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
300 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 302 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
301 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 303 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
302 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 304 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
303 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 305 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
304 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 306 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
305 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 307 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
306 ctx->page.mimetype = "text/html"; 308 ctx->page.mimetype = "text/html";
307 ctx->page.charset = PAGE_ENCODING; 309 ctx->page.charset = PAGE_ENCODING;
308 ctx->page.filename = NULL; 310 ctx->page.filename = NULL;
309 ctx->page.size = 0; 311 ctx->page.size = 0;
310 ctx->page.modified = time(NULL); 312 ctx->page.modified = time(NULL);
311 ctx->page.expires = ctx->page.modified; 313 ctx->page.expires = ctx->page.modified;
312 ctx->page.etag = NULL; 314 ctx->page.etag = NULL;
313 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); 315 memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list));
314 if (ctx->env.script_name) 316 if (ctx->env.script_name)
315 ctx->cfg.script_name = ctx->env.script_name; 317 ctx->cfg.script_name = ctx->env.script_name;
316 if (ctx->env.query_string) 318 if (ctx->env.query_string)
317 ctx->qry.raw = ctx->env.query_string; 319 ctx->qry.raw = ctx->env.query_string;
318 if (!ctx->env.cgit_config) 320 if (!ctx->env.cgit_config)
319 ctx->env.cgit_config = CGIT_CONFIG; 321 ctx->env.cgit_config = CGIT_CONFIG;
320} 322}
321 323
322struct refmatch { 324struct refmatch {
323 char *req_ref; 325 char *req_ref;
324 char *first_ref; 326 char *first_ref;
325 int match; 327 int match;
326}; 328};
327 329
328int find_current_ref(const char *refname, const unsigned char *sha1, 330int find_current_ref(const char *refname, const unsigned char *sha1,
329 int flags, void *cb_data) 331 int flags, void *cb_data)
330{ 332{
331 struct refmatch *info; 333 struct refmatch *info;
332 334
333 info = (struct refmatch *)cb_data; 335 info = (struct refmatch *)cb_data;
334 if (!strcmp(refname, info->req_ref)) 336 if (!strcmp(refname, info->req_ref))
335 info->match = 1; 337 info->match = 1;
336 if (!info->first_ref) 338 if (!info->first_ref)
337 info->first_ref = xstrdup(refname); 339 info->first_ref = xstrdup(refname);
338 return info->match; 340 return info->match;
339} 341}
340 342
341char *find_default_branch(struct cgit_repo *repo) 343char *find_default_branch(struct cgit_repo *repo)
342{ 344{
343 struct refmatch info; 345 struct refmatch info;
344 char *ref; 346 char *ref;
345 347
346 info.req_ref = repo->defbranch; 348 info.req_ref = repo->defbranch;
347 info.first_ref = NULL; 349 info.first_ref = NULL;
348 info.match = 0; 350 info.match = 0;
349 for_each_branch_ref(find_current_ref, &info); 351 for_each_branch_ref(find_current_ref, &info);
350 if (info.match) 352 if (info.match)
351 ref = info.req_ref; 353 ref = info.req_ref;
352 else 354 else
353 ref = info.first_ref; 355 ref = info.first_ref;
354 if (ref) 356 if (ref)
355 ref = xstrdup(ref); 357 ref = xstrdup(ref);
356 return ref; 358 return ref;
357} 359}
358 360
359static int prepare_repo_cmd(struct cgit_context *ctx) 361static int prepare_repo_cmd(struct cgit_context *ctx)
360{ 362{
361 char *tmp; 363 char *tmp;
362 unsigned char sha1[20]; 364 unsigned char sha1[20];
363 int nongit = 0; 365 int nongit = 0;
364 366
365 setenv("GIT_DIR", ctx->repo->path, 1); 367 setenv("GIT_DIR", ctx->repo->path, 1);
366 setup_git_directory_gently(&nongit); 368 setup_git_directory_gently(&nongit);
367 if (nongit) { 369 if (nongit) {
368 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 370 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
369 "config error"); 371 "config error");
370 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 372 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
371 ctx->repo = NULL; 373 ctx->repo = NULL;
372 cgit_print_http_headers(ctx); 374 cgit_print_http_headers(ctx);
373 cgit_print_docstart(ctx); 375 cgit_print_docstart(ctx);
374 cgit_print_pageheader(ctx); 376 cgit_print_pageheader(ctx);
375 cgit_print_error(tmp); 377 cgit_print_error(tmp);
376 cgit_print_docend(); 378 cgit_print_docend();
377 return 1; 379 return 1;
378 } 380 }
379 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 381 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
380 382
diff --git a/cgit.h b/cgit.h
index f990b15..bb8f598 100644
--- a/cgit.h
+++ b/cgit.h
@@ -20,281 +20,282 @@
20#include <xdiff/xdiff.h> 20#include <xdiff/xdiff.h>
21#include <utf8.h> 21#include <utf8.h>
22 22
23 23
24/* 24/*
25 * Dateformats used on misc. pages 25 * Dateformats used on misc. pages
26 */ 26 */
27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 27#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
28#define FMT_SHORTDATE "%Y-%m-%d" 28#define FMT_SHORTDATE "%Y-%m-%d"
29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
30 30
31 31
32/* 32/*
33 * Limits used for relative dates 33 * Limits used for relative dates
34 */ 34 */
35#define TM_MIN 60 35#define TM_MIN 60
36#define TM_HOUR (TM_MIN * 60) 36#define TM_HOUR (TM_MIN * 60)
37#define TM_DAY (TM_HOUR * 24) 37#define TM_DAY (TM_HOUR * 24)
38#define TM_WEEK (TM_DAY * 7) 38#define TM_WEEK (TM_DAY * 7)
39#define TM_YEAR (TM_DAY * 365) 39#define TM_YEAR (TM_DAY * 365)
40#define TM_MONTH (TM_YEAR / 12.0) 40#define TM_MONTH (TM_YEAR / 12.0)
41 41
42 42
43/* 43/*
44 * Default encoding 44 * Default encoding
45 */ 45 */
46#define PAGE_ENCODING "UTF-8" 46#define PAGE_ENCODING "UTF-8"
47 47
48typedef void (*configfn)(const char *name, const char *value); 48typedef void (*configfn)(const char *name, const char *value);
49typedef void (*filepair_fn)(struct diff_filepair *pair); 49typedef void (*filepair_fn)(struct diff_filepair *pair);
50typedef void (*linediff_fn)(char *line, int len); 50typedef void (*linediff_fn)(char *line, int len);
51 51
52struct cgit_filter { 52struct cgit_filter {
53 char *cmd; 53 char *cmd;
54 char **argv; 54 char **argv;
55 int old_stdout; 55 int old_stdout;
56 int pipe_fh[2]; 56 int pipe_fh[2];
57 int pid; 57 int pid;
58 int exitstatus; 58 int exitstatus;
59}; 59};
60 60
61struct cgit_repo { 61struct cgit_repo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *module_link; 68 char *module_link;
69 char *readme; 69 char *readme;
70 char *section; 70 char *section;
71 char *clone_url; 71 char *clone_url;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75 int enable_remote_branches; 75 int enable_remote_branches;
76 int max_stats; 76 int max_stats;
77 time_t mtime; 77 time_t mtime;
78 struct cgit_filter *about_filter; 78 struct cgit_filter *about_filter;
79 struct cgit_filter *commit_filter; 79 struct cgit_filter *commit_filter;
80 struct cgit_filter *source_filter; 80 struct cgit_filter *source_filter;
81}; 81};
82 82
83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
84 const char *value); 84 const char *value);
85 85
86struct cgit_repolist { 86struct cgit_repolist {
87 int length; 87 int length;
88 int count; 88 int count;
89 struct cgit_repo *repos; 89 struct cgit_repo *repos;
90}; 90};
91 91
92struct commitinfo { 92struct commitinfo {
93 struct commit *commit; 93 struct commit *commit;
94 char *author; 94 char *author;
95 char *author_email; 95 char *author_email;
96 unsigned long author_date; 96 unsigned long author_date;
97 char *committer; 97 char *committer;
98 char *committer_email; 98 char *committer_email;
99 unsigned long committer_date; 99 unsigned long committer_date;
100 char *subject; 100 char *subject;
101 char *msg; 101 char *msg;
102 char *msg_encoding; 102 char *msg_encoding;
103}; 103};
104 104
105struct taginfo { 105struct taginfo {
106 char *tagger; 106 char *tagger;
107 char *tagger_email; 107 char *tagger_email;
108 unsigned long tagger_date; 108 unsigned long tagger_date;
109 char *msg; 109 char *msg;
110}; 110};
111 111
112struct refinfo { 112struct refinfo {
113 const char *refname; 113 const char *refname;
114 struct object *object; 114 struct object *object;
115 union { 115 union {
116 struct taginfo *tag; 116 struct taginfo *tag;
117 struct commitinfo *commit; 117 struct commitinfo *commit;
118 }; 118 };
119}; 119};
120 120
121struct reflist { 121struct reflist {
122 struct refinfo **refs; 122 struct refinfo **refs;
123 int alloc; 123 int alloc;
124 int count; 124 int count;
125}; 125};
126 126
127struct cgit_query { 127struct cgit_query {
128 int has_symref; 128 int has_symref;
129 int has_sha1; 129 int has_sha1;
130 char *raw; 130 char *raw;
131 char *repo; 131 char *repo;
132 char *page; 132 char *page;
133 char *search; 133 char *search;
134 char *grep; 134 char *grep;
135 char *head; 135 char *head;
136 char *sha1; 136 char *sha1;
137 char *sha2; 137 char *sha2;
138 char *path; 138 char *path;
139 char *name; 139 char *name;
140 char *mimetype; 140 char *mimetype;
141 char *url; 141 char *url;
142 char *period; 142 char *period;
143 int ofs; 143 int ofs;
144 int nohead; 144 int nohead;
145 char *sort; 145 char *sort;
146 int showmsg; 146 int showmsg;
147 int ssdiff; 147 int ssdiff;
148 int context;
148 char *vpath; 149 char *vpath;
149}; 150};
150 151
151struct cgit_config { 152struct cgit_config {
152 char *agefile; 153 char *agefile;
153 char *cache_root; 154 char *cache_root;
154 char *clone_prefix; 155 char *clone_prefix;
155 char *css; 156 char *css;
156 char *favicon; 157 char *favicon;
157 char *footer; 158 char *footer;
158 char *head_include; 159 char *head_include;
159 char *header; 160 char *header;
160 char *index_header; 161 char *index_header;
161 char *index_info; 162 char *index_info;
162 char *logo; 163 char *logo;
163 char *logo_link; 164 char *logo_link;
164 char *module_link; 165 char *module_link;
165 char *robots; 166 char *robots;
166 char *root_title; 167 char *root_title;
167 char *root_desc; 168 char *root_desc;
168 char *root_readme; 169 char *root_readme;
169 char *script_name; 170 char *script_name;
170 char *section; 171 char *section;
171 char *virtual_root; 172 char *virtual_root;
172 int cache_size; 173 int cache_size;
173 int cache_dynamic_ttl; 174 int cache_dynamic_ttl;
174 int cache_max_create_time; 175 int cache_max_create_time;
175 int cache_repo_ttl; 176 int cache_repo_ttl;
176 int cache_root_ttl; 177 int cache_root_ttl;
177 int cache_scanrc_ttl; 178 int cache_scanrc_ttl;
178 int cache_static_ttl; 179 int cache_static_ttl;
179 int embedded; 180 int embedded;
180 int enable_filter_overrides; 181 int enable_filter_overrides;
181 int enable_index_links; 182 int enable_index_links;
182 int enable_log_filecount; 183 int enable_log_filecount;
183 int enable_log_linecount; 184 int enable_log_linecount;
184 int enable_remote_branches; 185 int enable_remote_branches;
185 int enable_tree_linenumbers; 186 int enable_tree_linenumbers;
186 int local_time; 187 int local_time;
187 int max_repo_count; 188 int max_repo_count;
188 int max_commit_count; 189 int max_commit_count;
189 int max_lock_attempts; 190 int max_lock_attempts;
190 int max_msg_len; 191 int max_msg_len;
191 int max_repodesc_len; 192 int max_repodesc_len;
192 int max_blob_size; 193 int max_blob_size;
193 int max_stats; 194 int max_stats;
194 int nocache; 195 int nocache;
195 int noplainemail; 196 int noplainemail;
196 int noheader; 197 int noheader;
197 int renamelimit; 198 int renamelimit;
198 int snapshots; 199 int snapshots;
199 int summary_branches; 200 int summary_branches;
200 int summary_log; 201 int summary_log;
201 int summary_tags; 202 int summary_tags;
202 int ssdiff; 203 int ssdiff;
203 struct string_list mimetypes; 204 struct string_list mimetypes;
204 struct cgit_filter *about_filter; 205 struct cgit_filter *about_filter;
205 struct cgit_filter *commit_filter; 206 struct cgit_filter *commit_filter;
206 struct cgit_filter *source_filter; 207 struct cgit_filter *source_filter;
207}; 208};
208 209
209struct cgit_page { 210struct cgit_page {
210 time_t modified; 211 time_t modified;
211 time_t expires; 212 time_t expires;
212 size_t size; 213 size_t size;
213 char *mimetype; 214 char *mimetype;
214 char *charset; 215 char *charset;
215 char *filename; 216 char *filename;
216 char *etag; 217 char *etag;
217 char *title; 218 char *title;
218 int status; 219 int status;
219 char *statusmsg; 220 char *statusmsg;
220}; 221};
221 222
222struct cgit_environment { 223struct cgit_environment {
223 char *cgit_config; 224 char *cgit_config;
224 char *http_host; 225 char *http_host;
225 char *https; 226 char *https;
226 char *no_http; 227 char *no_http;
227 char *path_info; 228 char *path_info;
228 char *query_string; 229 char *query_string;
229 char *request_method; 230 char *request_method;
230 char *script_name; 231 char *script_name;
231 char *server_name; 232 char *server_name;
232 char *server_port; 233 char *server_port;
233}; 234};
234 235
235struct cgit_context { 236struct cgit_context {
236 struct cgit_environment env; 237 struct cgit_environment env;
237 struct cgit_query qry; 238 struct cgit_query qry;
238 struct cgit_config cfg; 239 struct cgit_config cfg;
239 struct cgit_repo *repo; 240 struct cgit_repo *repo;
240 struct cgit_page page; 241 struct cgit_page page;
241}; 242};
242 243
243struct cgit_snapshot_format { 244struct cgit_snapshot_format {
244 const char *suffix; 245 const char *suffix;
245 const char *mimetype; 246 const char *mimetype;
246 write_archive_fn_t write_func; 247 write_archive_fn_t write_func;
247 int bit; 248 int bit;
248}; 249};
249 250
250extern const char *cgit_version; 251extern const char *cgit_version;
251 252
252extern struct cgit_repolist cgit_repolist; 253extern struct cgit_repolist cgit_repolist;
253extern struct cgit_context ctx; 254extern struct cgit_context ctx;
254extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 255extern const struct cgit_snapshot_format cgit_snapshot_formats[];
255 256
256extern struct cgit_repo *cgit_add_repo(const char *url); 257extern struct cgit_repo *cgit_add_repo(const char *url);
257extern struct cgit_repo *cgit_get_repoinfo(const char *url); 258extern struct cgit_repo *cgit_get_repoinfo(const char *url);
258extern void cgit_repo_config_cb(const char *name, const char *value); 259extern void cgit_repo_config_cb(const char *name, const char *value);
259 260
260extern int chk_zero(int result, char *msg); 261extern int chk_zero(int result, char *msg);
261extern int chk_positive(int result, char *msg); 262extern int chk_positive(int result, char *msg);
262extern int chk_non_negative(int result, char *msg); 263extern int chk_non_negative(int result, char *msg);
263 264
264extern char *trim_end(const char *str, char c); 265extern char *trim_end(const char *str, char c);
265extern char *strlpart(char *txt, int maxlen); 266extern char *strlpart(char *txt, int maxlen);
266extern char *strrpart(char *txt, int maxlen); 267extern char *strrpart(char *txt, int maxlen);
267 268
268extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 269extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
269extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 270extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
270 int flags, void *cb_data); 271 int flags, void *cb_data);
271 272
272extern void *cgit_free_commitinfo(struct commitinfo *info); 273extern void *cgit_free_commitinfo(struct commitinfo *info);
273 274
274extern int cgit_diff_files(const unsigned char *old_sha1, 275extern int cgit_diff_files(const unsigned char *old_sha1,
275 const unsigned char *new_sha1, 276 const unsigned char *new_sha1,
276 unsigned long *old_size, unsigned long *new_size, 277 unsigned long *old_size, unsigned long *new_size,
277 int *binary, linediff_fn fn); 278 int *binary, int context, linediff_fn fn);
278 279
279extern void cgit_diff_tree(const unsigned char *old_sha1, 280extern void cgit_diff_tree(const unsigned char *old_sha1,
280 const unsigned char *new_sha1, 281 const unsigned char *new_sha1,
281 filepair_fn fn, const char *prefix); 282 filepair_fn fn, const char *prefix);
282 283
283extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 284extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
284 285
285extern char *fmt(const char *format,...); 286extern char *fmt(const char *format,...);
286 287
287extern struct commitinfo *cgit_parse_commit(struct commit *commit); 288extern struct commitinfo *cgit_parse_commit(struct commit *commit);
288extern struct taginfo *cgit_parse_tag(struct tag *tag); 289extern struct taginfo *cgit_parse_tag(struct tag *tag);
289extern void cgit_parse_url(const char *url); 290extern void cgit_parse_url(const char *url);
290 291
291extern const char *cgit_repobasename(const char *reponame); 292extern const char *cgit_repobasename(const char *reponame);
292 293
293extern int cgit_parse_snapshots_mask(const char *str); 294extern int cgit_parse_snapshots_mask(const char *str);
294 295
295extern int cgit_open_filter(struct cgit_filter *filter); 296extern int cgit_open_filter(struct cgit_filter *filter);
296extern int cgit_close_filter(struct cgit_filter *filter); 297extern int cgit_close_filter(struct cgit_filter *filter);
297 298
298extern int readfile(const char *path, char **buf, size_t *size); 299extern int readfile(const char *path, char **buf, size_t *size);
299 300
300#endif /* CGIT_H */ 301#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index 6e8f0ce..7cf1e59 100644
--- a/shared.c
+++ b/shared.c
@@ -137,280 +137,281 @@ char *strrpart(char *txt, int maxlen)
137{ 137{
138 char *result; 138 char *result;
139 139
140 if (!txt) 140 if (!txt)
141 return txt; 141 return txt;
142 142
143 if (strlen(txt) <= maxlen) 143 if (strlen(txt) <= maxlen)
144 return txt; 144 return txt;
145 result = xmalloc(maxlen + 1); 145 result = xmalloc(maxlen + 1);
146 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3); 146 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
147 result[0] = result[1] = result[2] = '.'; 147 result[0] = result[1] = result[2] = '.';
148 return result; 148 return result;
149} 149}
150 150
151void cgit_add_ref(struct reflist *list, struct refinfo *ref) 151void cgit_add_ref(struct reflist *list, struct refinfo *ref)
152{ 152{
153 size_t size; 153 size_t size;
154 154
155 if (list->count >= list->alloc) { 155 if (list->count >= list->alloc) {
156 list->alloc += (list->alloc ? list->alloc : 4); 156 list->alloc += (list->alloc ? list->alloc : 4);
157 size = list->alloc * sizeof(struct refinfo *); 157 size = list->alloc * sizeof(struct refinfo *);
158 list->refs = xrealloc(list->refs, size); 158 list->refs = xrealloc(list->refs, size);
159 } 159 }
160 list->refs[list->count++] = ref; 160 list->refs[list->count++] = ref;
161} 161}
162 162
163struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 163struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
164{ 164{
165 struct refinfo *ref; 165 struct refinfo *ref;
166 166
167 ref = xmalloc(sizeof (struct refinfo)); 167 ref = xmalloc(sizeof (struct refinfo));
168 ref->refname = xstrdup(refname); 168 ref->refname = xstrdup(refname);
169 ref->object = parse_object(sha1); 169 ref->object = parse_object(sha1);
170 switch (ref->object->type) { 170 switch (ref->object->type) {
171 case OBJ_TAG: 171 case OBJ_TAG:
172 ref->tag = cgit_parse_tag((struct tag *)ref->object); 172 ref->tag = cgit_parse_tag((struct tag *)ref->object);
173 break; 173 break;
174 case OBJ_COMMIT: 174 case OBJ_COMMIT:
175 ref->commit = cgit_parse_commit((struct commit *)ref->object); 175 ref->commit = cgit_parse_commit((struct commit *)ref->object);
176 break; 176 break;
177 } 177 }
178 return ref; 178 return ref;
179} 179}
180 180
181int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, 181int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
182 void *cb_data) 182 void *cb_data)
183{ 183{
184 struct reflist *list = (struct reflist *)cb_data; 184 struct reflist *list = (struct reflist *)cb_data;
185 struct refinfo *info = cgit_mk_refinfo(refname, sha1); 185 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
186 186
187 if (info) 187 if (info)
188 cgit_add_ref(list, info); 188 cgit_add_ref(list, info);
189 return 0; 189 return 0;
190} 190}
191 191
192void cgit_diff_tree_cb(struct diff_queue_struct *q, 192void cgit_diff_tree_cb(struct diff_queue_struct *q,
193 struct diff_options *options, void *data) 193 struct diff_options *options, void *data)
194{ 194{
195 int i; 195 int i;
196 196
197 for (i = 0; i < q->nr; i++) { 197 for (i = 0; i < q->nr; i++) {
198 if (q->queue[i]->status == 'U') 198 if (q->queue[i]->status == 'U')
199 continue; 199 continue;
200 ((filepair_fn)data)(q->queue[i]); 200 ((filepair_fn)data)(q->queue[i]);
201 } 201 }
202} 202}
203 203
204static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 204static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
205{ 205{
206 enum object_type type; 206 enum object_type type;
207 207
208 if (is_null_sha1(sha1)) { 208 if (is_null_sha1(sha1)) {
209 file->ptr = (char *)""; 209 file->ptr = (char *)"";
210 file->size = 0; 210 file->size = 0;
211 } else { 211 } else {
212 file->ptr = read_sha1_file(sha1, &type, 212 file->ptr = read_sha1_file(sha1, &type,
213 (unsigned long *)&file->size); 213 (unsigned long *)&file->size);
214 } 214 }
215 return 1; 215 return 1;
216} 216}
217 217
218/* 218/*
219 * Receive diff-buffers from xdiff and concatenate them as 219 * Receive diff-buffers from xdiff and concatenate them as
220 * needed across multiple callbacks. 220 * needed across multiple callbacks.
221 * 221 *
222 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 222 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
223 * ripped from git and modified to use globals instead of 223 * ripped from git and modified to use globals instead of
224 * a special callback-struct. 224 * a special callback-struct.
225 */ 225 */
226char *diffbuf = NULL; 226char *diffbuf = NULL;
227int buflen = 0; 227int buflen = 0;
228 228
229int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 229int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
230{ 230{
231 int i; 231 int i;
232 232
233 for (i = 0; i < nbuf; i++) { 233 for (i = 0; i < nbuf; i++) {
234 if (mb[i].ptr[mb[i].size-1] != '\n') { 234 if (mb[i].ptr[mb[i].size-1] != '\n') {
235 /* Incomplete line */ 235 /* Incomplete line */
236 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 236 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
237 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 237 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
238 buflen += mb[i].size; 238 buflen += mb[i].size;
239 continue; 239 continue;
240 } 240 }
241 241
242 /* we have a complete line */ 242 /* we have a complete line */
243 if (!diffbuf) { 243 if (!diffbuf) {
244 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 244 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
245 continue; 245 continue;
246 } 246 }
247 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 247 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
248 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 248 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
249 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 249 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
250 free(diffbuf); 250 free(diffbuf);
251 diffbuf = NULL; 251 diffbuf = NULL;
252 buflen = 0; 252 buflen = 0;
253 } 253 }
254 if (diffbuf) { 254 if (diffbuf) {
255 ((linediff_fn)priv)(diffbuf, buflen); 255 ((linediff_fn)priv)(diffbuf, buflen);
256 free(diffbuf); 256 free(diffbuf);
257 diffbuf = NULL; 257 diffbuf = NULL;
258 buflen = 0; 258 buflen = 0;
259 } 259 }
260 return 0; 260 return 0;
261} 261}
262 262
263int cgit_diff_files(const unsigned char *old_sha1, 263int cgit_diff_files(const unsigned char *old_sha1,
264 const unsigned char *new_sha1, unsigned long *old_size, 264 const unsigned char *new_sha1, unsigned long *old_size,
265 unsigned long *new_size, int *binary, linediff_fn fn) 265 unsigned long *new_size, int *binary, int context,
266 linediff_fn fn)
266{ 267{
267 mmfile_t file1, file2; 268 mmfile_t file1, file2;
268 xpparam_t diff_params; 269 xpparam_t diff_params;
269 xdemitconf_t emit_params; 270 xdemitconf_t emit_params;
270 xdemitcb_t emit_cb; 271 xdemitcb_t emit_cb;
271 272
272 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 273 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
273 return 1; 274 return 1;
274 275
275 *old_size = file1.size; 276 *old_size = file1.size;
276 *new_size = file2.size; 277 *new_size = file2.size;
277 278
278 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || 279 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
279 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { 280 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
280 *binary = 1; 281 *binary = 1;
281 return 0; 282 return 0;
282 } 283 }
283 284
284 memset(&diff_params, 0, sizeof(diff_params)); 285 memset(&diff_params, 0, sizeof(diff_params));
285 memset(&emit_params, 0, sizeof(emit_params)); 286 memset(&emit_params, 0, sizeof(emit_params));
286 memset(&emit_cb, 0, sizeof(emit_cb)); 287 memset(&emit_cb, 0, sizeof(emit_cb));
287 diff_params.flags = XDF_NEED_MINIMAL; 288 diff_params.flags = XDF_NEED_MINIMAL;
288 emit_params.ctxlen = 3; 289 emit_params.ctxlen = context > 0 ? context : 3;
289 emit_params.flags = XDL_EMIT_FUNCNAMES; 290 emit_params.flags = XDL_EMIT_FUNCNAMES;
290 emit_cb.outf = filediff_cb; 291 emit_cb.outf = filediff_cb;
291 emit_cb.priv = fn; 292 emit_cb.priv = fn;
292 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 293 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
293 return 0; 294 return 0;
294} 295}
295 296
296void cgit_diff_tree(const unsigned char *old_sha1, 297void cgit_diff_tree(const unsigned char *old_sha1,
297 const unsigned char *new_sha1, 298 const unsigned char *new_sha1,
298 filepair_fn fn, const char *prefix) 299 filepair_fn fn, const char *prefix)
299{ 300{
300 struct diff_options opt; 301 struct diff_options opt;
301 int ret; 302 int ret;
302 int prefixlen; 303 int prefixlen;
303 304
304 diff_setup(&opt); 305 diff_setup(&opt);
305 opt.output_format = DIFF_FORMAT_CALLBACK; 306 opt.output_format = DIFF_FORMAT_CALLBACK;
306 opt.detect_rename = 1; 307 opt.detect_rename = 1;
307 opt.rename_limit = ctx.cfg.renamelimit; 308 opt.rename_limit = ctx.cfg.renamelimit;
308 DIFF_OPT_SET(&opt, RECURSIVE); 309 DIFF_OPT_SET(&opt, RECURSIVE);
309 opt.format_callback = cgit_diff_tree_cb; 310 opt.format_callback = cgit_diff_tree_cb;
310 opt.format_callback_data = fn; 311 opt.format_callback_data = fn;
311 if (prefix) { 312 if (prefix) {
312 opt.nr_paths = 1; 313 opt.nr_paths = 1;
313 opt.paths = &prefix; 314 opt.paths = &prefix;
314 prefixlen = strlen(prefix); 315 prefixlen = strlen(prefix);
315 opt.pathlens = &prefixlen; 316 opt.pathlens = &prefixlen;
316 } 317 }
317 diff_setup_done(&opt); 318 diff_setup_done(&opt);
318 319
319 if (old_sha1 && !is_null_sha1(old_sha1)) 320 if (old_sha1 && !is_null_sha1(old_sha1))
320 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 321 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
321 else 322 else
322 ret = diff_root_tree_sha1(new_sha1, "", &opt); 323 ret = diff_root_tree_sha1(new_sha1, "", &opt);
323 diffcore_std(&opt); 324 diffcore_std(&opt);
324 diff_flush(&opt); 325 diff_flush(&opt);
325} 326}
326 327
327void cgit_diff_commit(struct commit *commit, filepair_fn fn) 328void cgit_diff_commit(struct commit *commit, filepair_fn fn)
328{ 329{
329 unsigned char *old_sha1 = NULL; 330 unsigned char *old_sha1 = NULL;
330 331
331 if (commit->parents) 332 if (commit->parents)
332 old_sha1 = commit->parents->item->object.sha1; 333 old_sha1 = commit->parents->item->object.sha1;
333 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); 334 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
334} 335}
335 336
336int cgit_parse_snapshots_mask(const char *str) 337int cgit_parse_snapshots_mask(const char *str)
337{ 338{
338 const struct cgit_snapshot_format *f; 339 const struct cgit_snapshot_format *f;
339 static const char *delim = " \t,:/|;"; 340 static const char *delim = " \t,:/|;";
340 int tl, sl, rv = 0; 341 int tl, sl, rv = 0;
341 342
342 /* favor legacy setting */ 343 /* favor legacy setting */
343 if(atoi(str)) 344 if(atoi(str))
344 return 1; 345 return 1;
345 for(;;) { 346 for(;;) {
346 str += strspn(str,delim); 347 str += strspn(str,delim);
347 tl = strcspn(str,delim); 348 tl = strcspn(str,delim);
348 if (!tl) 349 if (!tl)
349 break; 350 break;
350 for (f = cgit_snapshot_formats; f->suffix; f++) { 351 for (f = cgit_snapshot_formats; f->suffix; f++) {
351 sl = strlen(f->suffix); 352 sl = strlen(f->suffix);
352 if((tl == sl && !strncmp(f->suffix, str, tl)) || 353 if((tl == sl && !strncmp(f->suffix, str, tl)) ||
353 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { 354 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) {
354 rv |= f->bit; 355 rv |= f->bit;
355 break; 356 break;
356 } 357 }
357 } 358 }
358 str += tl; 359 str += tl;
359 } 360 }
360 return rv; 361 return rv;
361} 362}
362 363
363int cgit_open_filter(struct cgit_filter *filter) 364int cgit_open_filter(struct cgit_filter *filter)
364{ 365{
365 366
366 filter->old_stdout = chk_positive(dup(STDOUT_FILENO), 367 filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
367 "Unable to duplicate STDOUT"); 368 "Unable to duplicate STDOUT");
368 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess"); 369 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
369 filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); 370 filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
370 if (filter->pid == 0) { 371 if (filter->pid == 0) {
371 close(filter->pipe_fh[1]); 372 close(filter->pipe_fh[1]);
372 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO), 373 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
373 "Unable to use pipe as STDIN"); 374 "Unable to use pipe as STDIN");
374 execvp(filter->cmd, filter->argv); 375 execvp(filter->cmd, filter->argv);
375 die("Unable to exec subprocess %s: %s (%d)", filter->cmd, 376 die("Unable to exec subprocess %s: %s (%d)", filter->cmd,
376 strerror(errno), errno); 377 strerror(errno), errno);
377 } 378 }
378 close(filter->pipe_fh[0]); 379 close(filter->pipe_fh[0]);
379 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO), 380 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
380 "Unable to use pipe as STDOUT"); 381 "Unable to use pipe as STDOUT");
381 close(filter->pipe_fh[1]); 382 close(filter->pipe_fh[1]);
382 return 0; 383 return 0;
383} 384}
384 385
385int cgit_close_filter(struct cgit_filter *filter) 386int cgit_close_filter(struct cgit_filter *filter)
386{ 387{
387 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), 388 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
388 "Unable to restore STDOUT"); 389 "Unable to restore STDOUT");
389 close(filter->old_stdout); 390 close(filter->old_stdout);
390 if (filter->pid < 0) 391 if (filter->pid < 0)
391 return 0; 392 return 0;
392 waitpid(filter->pid, &filter->exitstatus, 0); 393 waitpid(filter->pid, &filter->exitstatus, 0);
393 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus)) 394 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus))
394 return 0; 395 return 0;
395 die("Subprocess %s exited abnormally", filter->cmd); 396 die("Subprocess %s exited abnormally", filter->cmd);
396} 397}
397 398
398/* Read the content of the specified file into a newly allocated buffer, 399/* Read the content of the specified file into a newly allocated buffer,
399 * zeroterminate the buffer and return 0 on success, errno otherwise. 400 * zeroterminate the buffer and return 0 on success, errno otherwise.
400 */ 401 */
401int readfile(const char *path, char **buf, size_t *size) 402int readfile(const char *path, char **buf, size_t *size)
402{ 403{
403 int fd, e; 404 int fd, e;
404 struct stat st; 405 struct stat st;
405 406
406 fd = open(path, O_RDONLY); 407 fd = open(path, O_RDONLY);
407 if (fd == -1) 408 if (fd == -1)
408 return errno; 409 return errno;
409 if (fstat(fd, &st)) { 410 if (fstat(fd, &st)) {
410 e = errno; 411 e = errno;
411 close(fd); 412 close(fd);
412 return e; 413 return e;
413 } 414 }
414 if (!S_ISREG(st.st_mode)) { 415 if (!S_ISREG(st.st_mode)) {
415 close(fd); 416 close(fd);
416 return EISDIR; 417 return EISDIR;
diff --git a/ui-diff.c b/ui-diff.c
index fb836df..d3a3b2c 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -2,354 +2,354 @@
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-ssdiff.h" 12#include "ui-ssdiff.h"
13 13
14unsigned char old_rev_sha1[20]; 14unsigned char old_rev_sha1[20];
15unsigned char new_rev_sha1[20]; 15unsigned char new_rev_sha1[20];
16 16
17static int files, slots; 17static int files, slots;
18static int total_adds, total_rems, max_changes; 18static int total_adds, total_rems, max_changes;
19static int lines_added, lines_removed; 19static int lines_added, lines_removed;
20 20
21static struct fileinfo { 21static struct fileinfo {
22 char status; 22 char status;
23 unsigned char old_sha1[20]; 23 unsigned char old_sha1[20];
24 unsigned char new_sha1[20]; 24 unsigned char new_sha1[20];
25 unsigned short old_mode; 25 unsigned short old_mode;
26 unsigned short new_mode; 26 unsigned short new_mode;
27 char *old_path; 27 char *old_path;
28 char *new_path; 28 char *new_path;
29 unsigned int added; 29 unsigned int added;
30 unsigned int removed; 30 unsigned int removed;
31 unsigned long old_size; 31 unsigned long old_size;
32 unsigned long new_size; 32 unsigned long new_size;
33 int binary:1; 33 int binary:1;
34} *items; 34} *items;
35 35
36static int use_ssdiff = 0; 36static int use_ssdiff = 0;
37 37
38static void print_fileinfo(struct fileinfo *info) 38static void print_fileinfo(struct fileinfo *info)
39{ 39{
40 char *class; 40 char *class;
41 41
42 switch (info->status) { 42 switch (info->status) {
43 case DIFF_STATUS_ADDED: 43 case DIFF_STATUS_ADDED:
44 class = "add"; 44 class = "add";
45 break; 45 break;
46 case DIFF_STATUS_COPIED: 46 case DIFF_STATUS_COPIED:
47 class = "cpy"; 47 class = "cpy";
48 break; 48 break;
49 case DIFF_STATUS_DELETED: 49 case DIFF_STATUS_DELETED:
50 class = "del"; 50 class = "del";
51 break; 51 break;
52 case DIFF_STATUS_MODIFIED: 52 case DIFF_STATUS_MODIFIED:
53 class = "upd"; 53 class = "upd";
54 break; 54 break;
55 case DIFF_STATUS_RENAMED: 55 case DIFF_STATUS_RENAMED:
56 class = "mov"; 56 class = "mov";
57 break; 57 break;
58 case DIFF_STATUS_TYPE_CHANGED: 58 case DIFF_STATUS_TYPE_CHANGED:
59 class = "typ"; 59 class = "typ";
60 break; 60 break;
61 case DIFF_STATUS_UNKNOWN: 61 case DIFF_STATUS_UNKNOWN:
62 class = "unk"; 62 class = "unk";
63 break; 63 break;
64 case DIFF_STATUS_UNMERGED: 64 case DIFF_STATUS_UNMERGED:
65 class = "stg"; 65 class = "stg";
66 break; 66 break;
67 default: 67 default:
68 die("bug: unhandled diff status %c", info->status); 68 die("bug: unhandled diff status %c", info->status);
69 } 69 }
70 70
71 html("<tr>"); 71 html("<tr>");
72 htmlf("<td class='mode'>"); 72 htmlf("<td class='mode'>");
73 if (is_null_sha1(info->new_sha1)) { 73 if (is_null_sha1(info->new_sha1)) {
74 cgit_print_filemode(info->old_mode); 74 cgit_print_filemode(info->old_mode);
75 } else { 75 } else {
76 cgit_print_filemode(info->new_mode); 76 cgit_print_filemode(info->new_mode);
77 } 77 }
78 78
79 if (info->old_mode != info->new_mode && 79 if (info->old_mode != info->new_mode &&
80 !is_null_sha1(info->old_sha1) && 80 !is_null_sha1(info->old_sha1) &&
81 !is_null_sha1(info->new_sha1)) { 81 !is_null_sha1(info->new_sha1)) {
82 html("<span class='modechange'>["); 82 html("<span class='modechange'>[");
83 cgit_print_filemode(info->old_mode); 83 cgit_print_filemode(info->old_mode);
84 html("]</span>"); 84 html("]</span>");
85 } 85 }
86 htmlf("</td><td class='%s'>", class); 86 htmlf("</td><td class='%s'>", class);
87 cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1, 87 cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
88 ctx.qry.sha2, info->new_path, 0); 88 ctx.qry.sha2, info->new_path, 0);
89 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) 89 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
90 htmlf(" (%s from %s)", 90 htmlf(" (%s from %s)",
91 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", 91 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
92 info->old_path); 92 info->old_path);
93 html("</td><td class='right'>"); 93 html("</td><td class='right'>");
94 if (info->binary) { 94 if (info->binary) {
95 htmlf("bin</td><td class='graph'>%d -> %d bytes", 95 htmlf("bin</td><td class='graph'>%d -> %d bytes",
96 info->old_size, info->new_size); 96 info->old_size, info->new_size);
97 return; 97 return;
98 } 98 }
99 htmlf("%d", info->added + info->removed); 99 htmlf("%d", info->added + info->removed);
100 html("</td><td class='graph'>"); 100 html("</td><td class='graph'>");
101 htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); 101 htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
102 htmlf("<td class='add' style='width: %.1f%%;'/>", 102 htmlf("<td class='add' style='width: %.1f%%;'/>",
103 info->added * 100.0 / max_changes); 103 info->added * 100.0 / max_changes);
104 htmlf("<td class='rem' style='width: %.1f%%;'/>", 104 htmlf("<td class='rem' style='width: %.1f%%;'/>",
105 info->removed * 100.0 / max_changes); 105 info->removed * 100.0 / max_changes);
106 htmlf("<td class='none' style='width: %.1f%%;'/>", 106 htmlf("<td class='none' style='width: %.1f%%;'/>",
107 (max_changes - info->removed - info->added) * 100.0 / max_changes); 107 (max_changes - info->removed - info->added) * 100.0 / max_changes);
108 html("</tr></table></td></tr>\n"); 108 html("</tr></table></td></tr>\n");
109} 109}
110 110
111static void count_diff_lines(char *line, int len) 111static void count_diff_lines(char *line, int len)
112{ 112{
113 if (line && (len > 0)) { 113 if (line && (len > 0)) {
114 if (line[0] == '+') 114 if (line[0] == '+')
115 lines_added++; 115 lines_added++;
116 else if (line[0] == '-') 116 else if (line[0] == '-')
117 lines_removed++; 117 lines_removed++;
118 } 118 }
119} 119}
120 120
121static void inspect_filepair(struct diff_filepair *pair) 121static void inspect_filepair(struct diff_filepair *pair)
122{ 122{
123 int binary = 0; 123 int binary = 0;
124 unsigned long old_size = 0; 124 unsigned long old_size = 0;
125 unsigned long new_size = 0; 125 unsigned long new_size = 0;
126 files++; 126 files++;
127 lines_added = 0; 127 lines_added = 0;
128 lines_removed = 0; 128 lines_removed = 0;
129 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size, 129 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size,
130 &binary, count_diff_lines); 130 &binary, 0, count_diff_lines);
131 if (files >= slots) { 131 if (files >= slots) {
132 if (slots == 0) 132 if (slots == 0)
133 slots = 4; 133 slots = 4;
134 else 134 else
135 slots = slots * 2; 135 slots = slots * 2;
136 items = xrealloc(items, slots * sizeof(struct fileinfo)); 136 items = xrealloc(items, slots * sizeof(struct fileinfo));
137 } 137 }
138 items[files-1].status = pair->status; 138 items[files-1].status = pair->status;
139 hashcpy(items[files-1].old_sha1, pair->one->sha1); 139 hashcpy(items[files-1].old_sha1, pair->one->sha1);
140 hashcpy(items[files-1].new_sha1, pair->two->sha1); 140 hashcpy(items[files-1].new_sha1, pair->two->sha1);
141 items[files-1].old_mode = pair->one->mode; 141 items[files-1].old_mode = pair->one->mode;
142 items[files-1].new_mode = pair->two->mode; 142 items[files-1].new_mode = pair->two->mode;
143 items[files-1].old_path = xstrdup(pair->one->path); 143 items[files-1].old_path = xstrdup(pair->one->path);
144 items[files-1].new_path = xstrdup(pair->two->path); 144 items[files-1].new_path = xstrdup(pair->two->path);
145 items[files-1].added = lines_added; 145 items[files-1].added = lines_added;
146 items[files-1].removed = lines_removed; 146 items[files-1].removed = lines_removed;
147 items[files-1].old_size = old_size; 147 items[files-1].old_size = old_size;
148 items[files-1].new_size = new_size; 148 items[files-1].new_size = new_size;
149 items[files-1].binary = binary; 149 items[files-1].binary = binary;
150 if (lines_added + lines_removed > max_changes) 150 if (lines_added + lines_removed > max_changes)
151 max_changes = lines_added + lines_removed; 151 max_changes = lines_added + lines_removed;
152 total_adds += lines_added; 152 total_adds += lines_added;
153 total_rems += lines_removed; 153 total_rems += lines_removed;
154} 154}
155 155
156void cgit_print_diffstat(const unsigned char *old_sha1, 156void cgit_print_diffstat(const unsigned char *old_sha1,
157 const unsigned char *new_sha1, const char *prefix) 157 const unsigned char *new_sha1, const char *prefix)
158{ 158{
159 int i; 159 int i;
160 160
161 html("<div class='diffstat-header'>"); 161 html("<div class='diffstat-header'>");
162 cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1, 162 cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
163 ctx.qry.sha2, NULL, 0); 163 ctx.qry.sha2, NULL, 0);
164 if (prefix) 164 if (prefix)
165 htmlf(" (limited to '%s')", prefix); 165 htmlf(" (limited to '%s')", prefix);
166 html("</div>"); 166 html("</div>");
167 html("<table summary='diffstat' class='diffstat'>"); 167 html("<table summary='diffstat' class='diffstat'>");
168 max_changes = 0; 168 max_changes = 0;
169 cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, prefix); 169 cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, prefix);
170 for(i = 0; i<files; i++) 170 for(i = 0; i<files; i++)
171 print_fileinfo(&items[i]); 171 print_fileinfo(&items[i]);
172 html("</table>"); 172 html("</table>");
173 html("<div class='diffstat-summary'>"); 173 html("<div class='diffstat-summary'>");
174 htmlf("%d files changed, %d insertions, %d deletions", 174 htmlf("%d files changed, %d insertions, %d deletions",
175 files, total_adds, total_rems); 175 files, total_adds, total_rems);
176 html("</div>"); 176 html("</div>");
177} 177}
178 178
179 179
180/* 180/*
181 * print a single line returned from xdiff 181 * print a single line returned from xdiff
182 */ 182 */
183static void print_line(char *line, int len) 183static void print_line(char *line, int len)
184{ 184{
185 char *class = "ctx"; 185 char *class = "ctx";
186 char c = line[len-1]; 186 char c = line[len-1];
187 187
188 if (line[0] == '+') 188 if (line[0] == '+')
189 class = "add"; 189 class = "add";
190 else if (line[0] == '-') 190 else if (line[0] == '-')
191 class = "del"; 191 class = "del";
192 else if (line[0] == '@') 192 else if (line[0] == '@')
193 class = "hunk"; 193 class = "hunk";
194 194
195 htmlf("<div class='%s'>", class); 195 htmlf("<div class='%s'>", class);
196 line[len-1] = '\0'; 196 line[len-1] = '\0';
197 html_txt(line); 197 html_txt(line);
198 html("</div>"); 198 html("</div>");
199 line[len-1] = c; 199 line[len-1] = c;
200} 200}
201 201
202static void header(unsigned char *sha1, char *path1, int mode1, 202static void header(unsigned char *sha1, char *path1, int mode1,
203 unsigned char *sha2, char *path2, int mode2) 203 unsigned char *sha2, char *path2, int mode2)
204{ 204{
205 char *abbrev1, *abbrev2; 205 char *abbrev1, *abbrev2;
206 int subproject; 206 int subproject;
207 207
208 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); 208 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
209 html("<div class='head'>"); 209 html("<div class='head'>");
210 html("diff --git a/"); 210 html("diff --git a/");
211 html_txt(path1); 211 html_txt(path1);
212 html(" b/"); 212 html(" b/");
213 html_txt(path2); 213 html_txt(path2);
214 214
215 if (is_null_sha1(sha1)) 215 if (is_null_sha1(sha1))
216 path1 = "dev/null"; 216 path1 = "dev/null";
217 if (is_null_sha1(sha2)) 217 if (is_null_sha1(sha2))
218 path2 = "dev/null"; 218 path2 = "dev/null";
219 219
220 if (mode1 == 0) 220 if (mode1 == 0)
221 htmlf("<br/>new file mode %.6o", mode2); 221 htmlf("<br/>new file mode %.6o", mode2);
222 222
223 if (mode2 == 0) 223 if (mode2 == 0)
224 htmlf("<br/>deleted file mode %.6o", mode1); 224 htmlf("<br/>deleted file mode %.6o", mode1);
225 225
226 if (!subproject) { 226 if (!subproject) {
227 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); 227 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
228 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); 228 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
229 htmlf("<br/>index %s..%s", abbrev1, abbrev2); 229 htmlf("<br/>index %s..%s", abbrev1, abbrev2);
230 free(abbrev1); 230 free(abbrev1);
231 free(abbrev2); 231 free(abbrev2);
232 if (mode1 != 0 && mode2 != 0) { 232 if (mode1 != 0 && mode2 != 0) {
233 htmlf(" %.6o", mode1); 233 htmlf(" %.6o", mode1);
234 if (mode2 != mode1) 234 if (mode2 != mode1)
235 htmlf("..%.6o", mode2); 235 htmlf("..%.6o", mode2);
236 } 236 }
237 html("<br/>--- a/"); 237 html("<br/>--- a/");
238 if (mode1 != 0) 238 if (mode1 != 0)
239 cgit_tree_link(path1, NULL, NULL, ctx.qry.head, 239 cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
240 sha1_to_hex(old_rev_sha1), path1); 240 sha1_to_hex(old_rev_sha1), path1);
241 else 241 else
242 html_txt(path1); 242 html_txt(path1);
243 html("<br/>+++ b/"); 243 html("<br/>+++ b/");
244 if (mode2 != 0) 244 if (mode2 != 0)
245 cgit_tree_link(path2, NULL, NULL, ctx.qry.head, 245 cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
246 sha1_to_hex(new_rev_sha1), path2); 246 sha1_to_hex(new_rev_sha1), path2);
247 else 247 else
248 html_txt(path2); 248 html_txt(path2);
249 } 249 }
250 html("</div>"); 250 html("</div>");
251} 251}
252 252
253static void print_ssdiff_link() 253static void print_ssdiff_link()
254{ 254{
255 if (!strcmp(ctx.qry.page, "diff")) { 255 if (!strcmp(ctx.qry.page, "diff")) {
256 if (use_ssdiff) 256 if (use_ssdiff)
257 cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head, 257 cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head,
258 ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1); 258 ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1);
259 else 259 else
260 cgit_diff_link("Side-by-side diff", NULL, NULL, 260 cgit_diff_link("Side-by-side diff", NULL, NULL,
261 ctx.qry.head, ctx.qry.sha1, 261 ctx.qry.head, ctx.qry.sha1,
262 ctx.qry.sha2, ctx.qry.path, 1); 262 ctx.qry.sha2, ctx.qry.path, 1);
263 } 263 }
264} 264}
265 265
266static void filepair_cb(struct diff_filepair *pair) 266static void filepair_cb(struct diff_filepair *pair)
267{ 267{
268 unsigned long old_size = 0; 268 unsigned long old_size = 0;
269 unsigned long new_size = 0; 269 unsigned long new_size = 0;
270 int binary = 0; 270 int binary = 0;
271 linediff_fn print_line_fn = print_line; 271 linediff_fn print_line_fn = print_line;
272 272
273 if (use_ssdiff) { 273 if (use_ssdiff) {
274 cgit_ssdiff_header_begin(); 274 cgit_ssdiff_header_begin();
275 print_line_fn = cgit_ssdiff_line_cb; 275 print_line_fn = cgit_ssdiff_line_cb;
276 } 276 }
277 header(pair->one->sha1, pair->one->path, pair->one->mode, 277 header(pair->one->sha1, pair->one->path, pair->one->mode,
278 pair->two->sha1, pair->two->path, pair->two->mode); 278 pair->two->sha1, pair->two->path, pair->two->mode);
279 if (use_ssdiff) 279 if (use_ssdiff)
280 cgit_ssdiff_header_end(); 280 cgit_ssdiff_header_end();
281 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 281 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
282 if (S_ISGITLINK(pair->one->mode)) 282 if (S_ISGITLINK(pair->one->mode))
283 print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 283 print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
284 if (S_ISGITLINK(pair->two->mode)) 284 if (S_ISGITLINK(pair->two->mode))
285 print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 285 print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
286 if (use_ssdiff) 286 if (use_ssdiff)
287 cgit_ssdiff_footer(); 287 cgit_ssdiff_footer();
288 return; 288 return;
289 } 289 }
290 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 290 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
291 &new_size, &binary, print_line_fn)) 291 &new_size, &binary, ctx.qry.context, print_line_fn))
292 cgit_print_error("Error running diff"); 292 cgit_print_error("Error running diff");
293 if (binary) { 293 if (binary) {
294 if (use_ssdiff) 294 if (use_ssdiff)
295 html("<tr><td colspan='4'>Binary files differ</td></tr>"); 295 html("<tr><td colspan='4'>Binary files differ</td></tr>");
296 else 296 else
297 html("Binary files differ"); 297 html("Binary files differ");
298 } 298 }
299 if (use_ssdiff) 299 if (use_ssdiff)
300 cgit_ssdiff_footer(); 300 cgit_ssdiff_footer();
301} 301}
302 302
303void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) 303void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix)
304{ 304{
305 enum object_type type; 305 enum object_type type;
306 unsigned long size; 306 unsigned long size;
307 struct commit *commit, *commit2; 307 struct commit *commit, *commit2;
308 308
309 if (!new_rev) 309 if (!new_rev)
310 new_rev = ctx.qry.head; 310 new_rev = ctx.qry.head;
311 get_sha1(new_rev, new_rev_sha1); 311 get_sha1(new_rev, new_rev_sha1);
312 type = sha1_object_info(new_rev_sha1, &size); 312 type = sha1_object_info(new_rev_sha1, &size);
313 if (type == OBJ_BAD) { 313 if (type == OBJ_BAD) {
314 cgit_print_error(fmt("Bad object name: %s", new_rev)); 314 cgit_print_error(fmt("Bad object name: %s", new_rev));
315 return; 315 return;
316 } 316 }
317 commit = lookup_commit_reference(new_rev_sha1); 317 commit = lookup_commit_reference(new_rev_sha1);
318 if (!commit || parse_commit(commit)) 318 if (!commit || parse_commit(commit))
319 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1))); 319 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1)));
320 320
321 if (old_rev) 321 if (old_rev)
322 get_sha1(old_rev, old_rev_sha1); 322 get_sha1(old_rev, old_rev_sha1);
323 else if (commit->parents && commit->parents->item) 323 else if (commit->parents && commit->parents->item)
324 hashcpy(old_rev_sha1, commit->parents->item->object.sha1); 324 hashcpy(old_rev_sha1, commit->parents->item->object.sha1);
325 else 325 else
326 hashclr(old_rev_sha1); 326 hashclr(old_rev_sha1);
327 327
328 if (!is_null_sha1(old_rev_sha1)) { 328 if (!is_null_sha1(old_rev_sha1)) {
329 type = sha1_object_info(old_rev_sha1, &size); 329 type = sha1_object_info(old_rev_sha1, &size);
330 if (type == OBJ_BAD) { 330 if (type == OBJ_BAD) {
331 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1))); 331 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1)));
332 return; 332 return;
333 } 333 }
334 commit2 = lookup_commit_reference(old_rev_sha1); 334 commit2 = lookup_commit_reference(old_rev_sha1);
335 if (!commit2 || parse_commit(commit2)) 335 if (!commit2 || parse_commit(commit2))
336 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1))); 336 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(old_rev_sha1)));
337 } 337 }
338 338
339 if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) 339 if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff))
340 use_ssdiff = 1; 340 use_ssdiff = 1;
341 341
342 print_ssdiff_link(); 342 print_ssdiff_link();
343 cgit_print_diffstat(old_rev_sha1, new_rev_sha1, prefix); 343 cgit_print_diffstat(old_rev_sha1, new_rev_sha1, prefix);
344 344
345 if (use_ssdiff) { 345 if (use_ssdiff) {
346 html("<table summary='ssdiff' class='ssdiff'>"); 346 html("<table summary='ssdiff' class='ssdiff'>");
347 } else { 347 } else {
348 html("<table summary='diff' class='diff'>"); 348 html("<table summary='diff' class='diff'>");
349 html("<tr><td>"); 349 html("<tr><td>");
350 } 350 }
351 cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); 351 cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix);
352 if (!use_ssdiff) 352 if (!use_ssdiff)
353 html("</td></tr>"); 353 html("</td></tr>");
354 html("</table>"); 354 html("</table>");
355} 355}
diff --git a/ui-log.c b/ui-log.c
index bfbe436..5eb5c81 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,164 +1,164 @@
1/* ui-log.c: functions for log output 1/* ui-log.c: functions for log output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13int files, add_lines, rem_lines; 13int files, add_lines, rem_lines;
14 14
15void count_lines(char *line, int size) 15void count_lines(char *line, int size)
16{ 16{
17 if (size <= 0) 17 if (size <= 0)
18 return; 18 return;
19 19
20 if (line[0] == '+') 20 if (line[0] == '+')
21 add_lines++; 21 add_lines++;
22 22
23 else if (line[0] == '-') 23 else if (line[0] == '-')
24 rem_lines++; 24 rem_lines++;
25} 25}
26 26
27void inspect_files(struct diff_filepair *pair) 27void inspect_files(struct diff_filepair *pair)
28{ 28{
29 unsigned long old_size = 0; 29 unsigned long old_size = 0;
30 unsigned long new_size = 0; 30 unsigned long new_size = 0;
31 int binary = 0; 31 int binary = 0;
32 32
33 files++; 33 files++;
34 if (ctx.repo->enable_log_linecount) 34 if (ctx.repo->enable_log_linecount)
35 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 35 cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
36 &new_size, &binary, count_lines); 36 &new_size, &binary, 0, count_lines);
37} 37}
38 38
39void show_commit_decorations(struct commit *commit) 39void show_commit_decorations(struct commit *commit)
40{ 40{
41 struct name_decoration *deco; 41 struct name_decoration *deco;
42 static char buf[1024]; 42 static char buf[1024];
43 43
44 buf[sizeof(buf) - 1] = 0; 44 buf[sizeof(buf) - 1] = 0;
45 deco = lookup_decoration(&name_decoration, &commit->object); 45 deco = lookup_decoration(&name_decoration, &commit->object);
46 while (deco) { 46 while (deco) {
47 if (!prefixcmp(deco->name, "refs/heads/")) { 47 if (!prefixcmp(deco->name, "refs/heads/")) {
48 strncpy(buf, deco->name + 11, sizeof(buf) - 1); 48 strncpy(buf, deco->name + 11, sizeof(buf) - 1);
49 cgit_log_link(buf, NULL, "branch-deco", buf, NULL, 49 cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
50 ctx.qry.vpath, 0, NULL, NULL, 50 ctx.qry.vpath, 0, NULL, NULL,
51 ctx.qry.showmsg); 51 ctx.qry.showmsg);
52 } 52 }
53 else if (!prefixcmp(deco->name, "tag: refs/tags/")) { 53 else if (!prefixcmp(deco->name, "tag: refs/tags/")) {
54 strncpy(buf, deco->name + 15, sizeof(buf) - 1); 54 strncpy(buf, deco->name + 15, sizeof(buf) - 1);
55 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf); 55 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
56 } 56 }
57 else if (!prefixcmp(deco->name, "refs/tags/")) { 57 else if (!prefixcmp(deco->name, "refs/tags/")) {
58 strncpy(buf, deco->name + 10, sizeof(buf) - 1); 58 strncpy(buf, deco->name + 10, sizeof(buf) - 1);
59 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf); 59 cgit_tag_link(buf, NULL, "tag-deco", ctx.qry.head, buf);
60 } 60 }
61 else if (!prefixcmp(deco->name, "refs/remotes/")) { 61 else if (!prefixcmp(deco->name, "refs/remotes/")) {
62 strncpy(buf, deco->name + 13, sizeof(buf) - 1); 62 strncpy(buf, deco->name + 13, sizeof(buf) - 1);
63 cgit_log_link(buf, NULL, "remote-deco", NULL, 63 cgit_log_link(buf, NULL, "remote-deco", NULL,
64 sha1_to_hex(commit->object.sha1), 64 sha1_to_hex(commit->object.sha1),
65 ctx.qry.vpath, 0, NULL, NULL, 65 ctx.qry.vpath, 0, NULL, NULL,
66 ctx.qry.showmsg); 66 ctx.qry.showmsg);
67 } 67 }
68 else { 68 else {
69 strncpy(buf, deco->name, sizeof(buf) - 1); 69 strncpy(buf, deco->name, sizeof(buf) - 1);
70 cgit_commit_link(buf, NULL, "deco", ctx.qry.head, 70 cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
71 sha1_to_hex(commit->object.sha1), 71 sha1_to_hex(commit->object.sha1),
72 ctx.qry.vpath, 0); 72 ctx.qry.vpath, 0);
73 } 73 }
74 deco = deco->next; 74 deco = deco->next;
75 } 75 }
76} 76}
77 77
78void print_commit(struct commit *commit) 78void print_commit(struct commit *commit)
79{ 79{
80 struct commitinfo *info; 80 struct commitinfo *info;
81 char *tmp; 81 char *tmp;
82 int cols = 2; 82 int cols = 2;
83 83
84 info = cgit_parse_commit(commit); 84 info = cgit_parse_commit(commit);
85 htmlf("<tr%s><td>", 85 htmlf("<tr%s><td>",
86 ctx.qry.showmsg ? " class='logheader'" : ""); 86 ctx.qry.showmsg ? " class='logheader'" : "");
87 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); 87 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
88 tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp); 88 tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp);
89 html_link_open(tmp, NULL, NULL); 89 html_link_open(tmp, NULL, NULL);
90 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 90 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
91 html_link_close(); 91 html_link_close();
92 htmlf("</td><td%s>", 92 htmlf("</td><td%s>",
93 ctx.qry.showmsg ? " class='logsubject'" : ""); 93 ctx.qry.showmsg ? " class='logsubject'" : "");
94 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, 94 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
95 sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0); 95 sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0);
96 show_commit_decorations(commit); 96 show_commit_decorations(commit);
97 html("</td><td>"); 97 html("</td><td>");
98 html_txt(info->author); 98 html_txt(info->author);
99 if (ctx.repo->enable_log_filecount) { 99 if (ctx.repo->enable_log_filecount) {
100 files = 0; 100 files = 0;
101 add_lines = 0; 101 add_lines = 0;
102 rem_lines = 0; 102 rem_lines = 0;
103 cgit_diff_commit(commit, inspect_files); 103 cgit_diff_commit(commit, inspect_files);
104 html("</td><td>"); 104 html("</td><td>");
105 htmlf("%d", files); 105 htmlf("%d", files);
106 if (ctx.repo->enable_log_linecount) { 106 if (ctx.repo->enable_log_linecount) {
107 html("</td><td>"); 107 html("</td><td>");
108 htmlf("-%d/+%d", rem_lines, add_lines); 108 htmlf("-%d/+%d", rem_lines, add_lines);
109 } 109 }
110 } 110 }
111 html("</td></tr>\n"); 111 html("</td></tr>\n");
112 if (ctx.qry.showmsg) { 112 if (ctx.qry.showmsg) {
113 if (ctx.repo->enable_log_filecount) { 113 if (ctx.repo->enable_log_filecount) {
114 cols++; 114 cols++;
115 if (ctx.repo->enable_log_linecount) 115 if (ctx.repo->enable_log_linecount)
116 cols++; 116 cols++;
117 } 117 }
118 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>", 118 htmlf("<tr class='nohover'><td/><td colspan='%d' class='logmsg'>",
119 cols); 119 cols);
120 html_txt(info->msg); 120 html_txt(info->msg);
121 html("</td></tr>\n"); 121 html("</td></tr>\n");
122 } 122 }
123 cgit_free_commitinfo(info); 123 cgit_free_commitinfo(info);
124} 124}
125 125
126static const char *disambiguate_ref(const char *ref) 126static const char *disambiguate_ref(const char *ref)
127{ 127{
128 unsigned char sha1[20]; 128 unsigned char sha1[20];
129 const char *longref; 129 const char *longref;
130 130
131 longref = fmt("refs/heads/%s", ref); 131 longref = fmt("refs/heads/%s", ref);
132 if (get_sha1(longref, sha1) == 0) 132 if (get_sha1(longref, sha1) == 0)
133 return longref; 133 return longref;
134 134
135 return ref; 135 return ref;
136} 136}
137 137
138void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, 138void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
139 char *path, int pager) 139 char *path, int pager)
140{ 140{
141 struct rev_info rev; 141 struct rev_info rev;
142 struct commit *commit; 142 struct commit *commit;
143 const char *argv[] = {NULL, NULL, NULL, NULL, NULL}; 143 const char *argv[] = {NULL, NULL, NULL, NULL, NULL};
144 int argc = 2; 144 int argc = 2;
145 int i, columns = 3; 145 int i, columns = 3;
146 146
147 if (!tip) 147 if (!tip)
148 tip = ctx.qry.head; 148 tip = ctx.qry.head;
149 149
150 argv[1] = disambiguate_ref(tip); 150 argv[1] = disambiguate_ref(tip);
151 151
152 if (grep && pattern && (!strcmp(grep, "grep") || 152 if (grep && pattern && (!strcmp(grep, "grep") ||
153 !strcmp(grep, "author") || 153 !strcmp(grep, "author") ||
154 !strcmp(grep, "committer"))) 154 !strcmp(grep, "committer")))
155 argv[argc++] = fmt("--%s=%s", grep, pattern); 155 argv[argc++] = fmt("--%s=%s", grep, pattern);
156 156
157 if (path) { 157 if (path) {
158 argv[argc++] = "--"; 158 argv[argc++] = "--";
159 argv[argc++] = path; 159 argv[argc++] = path;
160 } 160 }
161 init_revisions(&rev, NULL); 161 init_revisions(&rev, NULL);
162 rev.abbrev = DEFAULT_ABBREV; 162 rev.abbrev = DEFAULT_ABBREV;
163 rev.commit_format = CMIT_FMT_DEFAULT; 163 rev.commit_format = CMIT_FMT_DEFAULT;
164 rev.verbose_header = 1; 164 rev.verbose_header = 1;
diff --git a/ui-patch.c b/ui-patch.c
index 25dc9fe..d13104c 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -1,131 +1,131 @@
1/* ui-patch.c: generate patch view 1/* ui-patch.c: generate patch view
2 * 2 *
3 * Copyright (C) 2007 Lars Hjemli 3 * Copyright (C) 2007 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 12
13static void print_line(char *line, int len) 13static void print_line(char *line, int len)
14{ 14{
15 char c = line[len-1]; 15 char c = line[len-1];
16 16
17 line[len-1] = '\0'; 17 line[len-1] = '\0';
18 htmlf("%s\n", line); 18 htmlf("%s\n", line);
19 line[len-1] = c; 19 line[len-1] = c;
20} 20}
21 21
22static void header(unsigned char *sha1, char *path1, int mode1, 22static void header(unsigned char *sha1, char *path1, int mode1,
23 unsigned char *sha2, char *path2, int mode2) 23 unsigned char *sha2, char *path2, int mode2)
24{ 24{
25 char *abbrev1, *abbrev2; 25 char *abbrev1, *abbrev2;
26 int subproject; 26 int subproject;
27 27
28 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); 28 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
29 htmlf("diff --git a/%s b/%s\n", path1, path2); 29 htmlf("diff --git a/%s b/%s\n", path1, path2);
30 30
31 if (is_null_sha1(sha1)) 31 if (is_null_sha1(sha1))
32 path1 = "dev/null"; 32 path1 = "dev/null";
33 if (is_null_sha1(sha2)) 33 if (is_null_sha1(sha2))
34 path2 = "dev/null"; 34 path2 = "dev/null";
35 35
36 if (mode1 == 0) 36 if (mode1 == 0)
37 htmlf("new file mode %.6o\n", mode2); 37 htmlf("new file mode %.6o\n", mode2);
38 38
39 if (mode2 == 0) 39 if (mode2 == 0)
40 htmlf("deleted file mode %.6o\n", mode1); 40 htmlf("deleted file mode %.6o\n", mode1);
41 41
42 if (!subproject) { 42 if (!subproject) {
43 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); 43 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
44 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); 44 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
45 htmlf("index %s..%s", abbrev1, abbrev2); 45 htmlf("index %s..%s", abbrev1, abbrev2);
46 free(abbrev1); 46 free(abbrev1);
47 free(abbrev2); 47 free(abbrev2);
48 if (mode1 != 0 && mode2 != 0) { 48 if (mode1 != 0 && mode2 != 0) {
49 htmlf(" %.6o", mode1); 49 htmlf(" %.6o", mode1);
50 if (mode2 != mode1) 50 if (mode2 != mode1)
51 htmlf("..%.6o", mode2); 51 htmlf("..%.6o", mode2);
52 } 52 }
53 htmlf("\n--- a/%s\n", path1); 53 htmlf("\n--- a/%s\n", path1);
54 htmlf("+++ b/%s\n", path2); 54 htmlf("+++ b/%s\n", path2);
55 } 55 }
56} 56}
57 57
58static void filepair_cb(struct diff_filepair *pair) 58static void filepair_cb(struct diff_filepair *pair)
59{ 59{
60 unsigned long old_size = 0; 60 unsigned long old_size = 0;
61 unsigned long new_size = 0; 61 unsigned long new_size = 0;
62 int binary = 0; 62 int binary = 0;
63 63
64 header(pair->one->sha1, pair->one->path, pair->one->mode, 64 header(pair->one->sha1, pair->one->path, pair->one->mode,
65 pair->two->sha1, pair->two->path, pair->two->mode); 65 pair->two->sha1, pair->two->path, pair->two->mode);
66 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 66 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
67 if (S_ISGITLINK(pair->one->mode)) 67 if (S_ISGITLINK(pair->one->mode))
68 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 68 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
69 if (S_ISGITLINK(pair->two->mode)) 69 if (S_ISGITLINK(pair->two->mode))
70 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 70 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
71 return; 71 return;
72 } 72 }
73 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 73 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
74 &new_size, &binary, print_line)) 74 &new_size, &binary, 0, print_line))
75 html("Error running diff"); 75 html("Error running diff");
76 if (binary) 76 if (binary)
77 html("Binary files differ\n"); 77 html("Binary files differ\n");
78} 78}
79 79
80void cgit_print_patch(char *hex, const char *prefix) 80void cgit_print_patch(char *hex, const char *prefix)
81{ 81{
82 struct commit *commit; 82 struct commit *commit;
83 struct commitinfo *info; 83 struct commitinfo *info;
84 unsigned char sha1[20], old_sha1[20]; 84 unsigned char sha1[20], old_sha1[20];
85 char *patchname; 85 char *patchname;
86 86
87 if (!hex) 87 if (!hex)
88 hex = ctx.qry.head; 88 hex = ctx.qry.head;
89 89
90 if (get_sha1(hex, sha1)) { 90 if (get_sha1(hex, sha1)) {
91 cgit_print_error(fmt("Bad object id: %s", hex)); 91 cgit_print_error(fmt("Bad object id: %s", hex));
92 return; 92 return;
93 } 93 }
94 commit = lookup_commit_reference(sha1); 94 commit = lookup_commit_reference(sha1);
95 if (!commit) { 95 if (!commit) {
96 cgit_print_error(fmt("Bad commit reference: %s", hex)); 96 cgit_print_error(fmt("Bad commit reference: %s", hex));
97 return; 97 return;
98 } 98 }
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", info->author); 111 htmlf("From: %s", info->author);
112 if (!ctx.cfg.noplainemail) { 112 if (!ctx.cfg.noplainemail) {
113 htmlf(" %s", info->author_email); 113 htmlf(" %s", info->author_email);
114 } 114 }
115 html("\n"); 115 html("\n");
116 html("Date: "); 116 html("Date: ");
117 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);
118 htmlf("Subject: %s\n\n", info->subject); 118 htmlf("Subject: %s\n\n", info->subject);
119 if (info->msg && *info->msg) { 119 if (info->msg && *info->msg) {
120 htmlf("%s", info->msg); 120 htmlf("%s", info->msg);
121 if (info->msg[strlen(info->msg) - 1] != '\n') 121 if (info->msg[strlen(info->msg) - 1] != '\n')
122 html("\n"); 122 html("\n");
123 } 123 }
124 html("---\n"); 124 html("---\n");
125 if (prefix) 125 if (prefix)
126 htmlf("(limited to '%s')\n\n", prefix); 126 htmlf("(limited to '%s')\n\n", prefix);
127 cgit_diff_tree(old_sha1, sha1, filepair_cb, prefix); 127 cgit_diff_tree(old_sha1, sha1, filepair_cb, prefix);
128 html("--\n"); 128 html("--\n");
129 htmlf("cgit %s\n", CGIT_VERSION); 129 htmlf("cgit %s\n", CGIT_VERSION);
130 cgit_free_commitinfo(info); 130 cgit_free_commitinfo(info);
131} 131}