summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2011-02-19 13:51:00 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2011-02-19 13:51:00 (UTC)
commit979c460e7f71d153ae79da67b8b21c3412f0fe02 (patch) (unidiff)
tree6da9ffb66ed0a68205e6644cb7e2b4652d6684be
parentfb9e6d1594a24fe4e551fd57a9c91fd18b14806e (diff)
parent0141b9f889bbaa1fe474f9a98dd377138ac73054 (diff)
downloadcgit-979c460e7f71d153ae79da67b8b21c3412f0fe02.zip
cgit-979c460e7f71d153ae79da67b8b21c3412f0fe02.tar.gz
cgit-979c460e7f71d153ae79da67b8b21c3412f0fe02.tar.bz2
Merge branch 'br/misc'
* br/misc: Use transparent background for the cgit logo ssdiff: anchors for ssdiff implement repo.logo and repo.logo-link
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c8
-rw-r--r--cgit.css2
-rw-r--r--cgit.h2
-rw-r--r--cgit.pngbin1840 -> 1488 bytes
-rw-r--r--cgitrc.5.txt9
-rw-r--r--ui-diff.c12
-rw-r--r--ui-diff.h6
-rw-r--r--ui-shared.c18
-rw-r--r--ui-ssdiff.c34
9 files changed, 74 insertions, 17 deletions
diff --git a/cgit.c b/cgit.c
index 71f3fc8..916feb4 100644
--- a/cgit.c
+++ b/cgit.c
@@ -28,99 +28,103 @@ void add_mimetype(const char *name, const char *value)
28 28
29struct cgit_filter *new_filter(const char *cmd, int extra_args) 29struct cgit_filter *new_filter(const char *cmd, int extra_args)
30{ 30{
31 struct cgit_filter *f; 31 struct cgit_filter *f;
32 32
33 if (!cmd || !cmd[0]) 33 if (!cmd || !cmd[0])
34 return NULL; 34 return NULL;
35 35
36 f = xmalloc(sizeof(struct cgit_filter)); 36 f = xmalloc(sizeof(struct cgit_filter));
37 f->cmd = xstrdup(cmd); 37 f->cmd = xstrdup(cmd);
38 f->argv = xmalloc((2 + extra_args) * sizeof(char *)); 38 f->argv = xmalloc((2 + extra_args) * sizeof(char *));
39 f->argv[0] = f->cmd; 39 f->argv[0] = f->cmd;
40 f->argv[1] = NULL; 40 f->argv[1] = NULL;
41 return f; 41 return f;
42} 42}
43 43
44static void process_cached_repolist(const char *path); 44static void process_cached_repolist(const char *path);
45 45
46void repo_config(struct cgit_repo *repo, const char *name, const char *value) 46void repo_config(struct cgit_repo *repo, const char *name, const char *value)
47{ 47{
48 if (!strcmp(name, "name")) 48 if (!strcmp(name, "name"))
49 repo->name = xstrdup(value); 49 repo->name = xstrdup(value);
50 else if (!strcmp(name, "clone-url")) 50 else if (!strcmp(name, "clone-url"))
51 repo->clone_url = xstrdup(value); 51 repo->clone_url = xstrdup(value);
52 else if (!strcmp(name, "desc")) 52 else if (!strcmp(name, "desc"))
53 repo->desc = xstrdup(value); 53 repo->desc = xstrdup(value);
54 else if (!strcmp(name, "owner")) 54 else if (!strcmp(name, "owner"))
55 repo->owner = xstrdup(value); 55 repo->owner = xstrdup(value);
56 else if (!strcmp(name, "defbranch")) 56 else if (!strcmp(name, "defbranch"))
57 repo->defbranch = xstrdup(value); 57 repo->defbranch = xstrdup(value);
58 else if (!strcmp(name, "snapshots")) 58 else if (!strcmp(name, "snapshots"))
59 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 59 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
60 else if (!strcmp(name, "enable-commit-graph")) 60 else if (!strcmp(name, "enable-commit-graph"))
61 repo->enable_commit_graph = ctx.cfg.enable_commit_graph * atoi(value); 61 repo->enable_commit_graph = ctx.cfg.enable_commit_graph * atoi(value);
62 else if (!strcmp(name, "enable-log-filecount")) 62 else if (!strcmp(name, "enable-log-filecount"))
63 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 63 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
64 else if (!strcmp(name, "enable-log-linecount")) 64 else if (!strcmp(name, "enable-log-linecount"))
65 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 65 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
66 else if (!strcmp(name, "enable-remote-branches")) 66 else if (!strcmp(name, "enable-remote-branches"))
67 repo->enable_remote_branches = atoi(value); 67 repo->enable_remote_branches = atoi(value);
68 else if (!strcmp(name, "enable-subject-links")) 68 else if (!strcmp(name, "enable-subject-links"))
69 repo->enable_subject_links = atoi(value); 69 repo->enable_subject_links = atoi(value);
70 else if (!strcmp(name, "max-stats")) 70 else if (!strcmp(name, "max-stats"))
71 repo->max_stats = cgit_find_stats_period(value, NULL); 71 repo->max_stats = cgit_find_stats_period(value, NULL);
72 else if (!strcmp(name, "module-link")) 72 else if (!strcmp(name, "module-link"))
73 repo->module_link= xstrdup(value); 73 repo->module_link= xstrdup(value);
74 else if (!strcmp(name, "section")) 74 else if (!strcmp(name, "section"))
75 repo->section = xstrdup(value); 75 repo->section = xstrdup(value);
76 else if (!strcmp(name, "readme") && value != NULL) { 76 else if (!strcmp(name, "readme") && value != NULL)
77 repo->readme = xstrdup(value); 77 repo->readme = xstrdup(value);
78 } else if (ctx.cfg.enable_filter_overrides) { 78 else if (!strcmp(name, "logo") && value != NULL)
79 repo->logo = xstrdup(value);
80 else if (!strcmp(name, "logo-link") && value != NULL)
81 repo->logo_link = xstrdup(value);
82 else if (ctx.cfg.enable_filter_overrides) {
79 if (!strcmp(name, "about-filter")) 83 if (!strcmp(name, "about-filter"))
80 repo->about_filter = new_filter(value, 0); 84 repo->about_filter = new_filter(value, 0);
81 else if (!strcmp(name, "commit-filter")) 85 else if (!strcmp(name, "commit-filter"))
82 repo->commit_filter = new_filter(value, 0); 86 repo->commit_filter = new_filter(value, 0);
83 else if (!strcmp(name, "source-filter")) 87 else if (!strcmp(name, "source-filter"))
84 repo->source_filter = new_filter(value, 1); 88 repo->source_filter = new_filter(value, 1);
85 } 89 }
86} 90}
87 91
88void config_cb(const char *name, const char *value) 92void config_cb(const char *name, const char *value)
89{ 93{
90 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 94 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
91 ctx.cfg.section = xstrdup(value); 95 ctx.cfg.section = xstrdup(value);
92 else if (!strcmp(name, "repo.url")) 96 else if (!strcmp(name, "repo.url"))
93 ctx.repo = cgit_add_repo(value); 97 ctx.repo = cgit_add_repo(value);
94 else if (ctx.repo && !strcmp(name, "repo.path")) 98 else if (ctx.repo && !strcmp(name, "repo.path"))
95 ctx.repo->path = trim_end(value, '/'); 99 ctx.repo->path = trim_end(value, '/');
96 else if (ctx.repo && !prefixcmp(name, "repo.")) 100 else if (ctx.repo && !prefixcmp(name, "repo."))
97 repo_config(ctx.repo, name + 5, value); 101 repo_config(ctx.repo, name + 5, value);
98 else if (!strcmp(name, "readme")) 102 else if (!strcmp(name, "readme"))
99 ctx.cfg.readme = xstrdup(value); 103 ctx.cfg.readme = xstrdup(value);
100 else if (!strcmp(name, "root-title")) 104 else if (!strcmp(name, "root-title"))
101 ctx.cfg.root_title = xstrdup(value); 105 ctx.cfg.root_title = xstrdup(value);
102 else if (!strcmp(name, "root-desc")) 106 else if (!strcmp(name, "root-desc"))
103 ctx.cfg.root_desc = xstrdup(value); 107 ctx.cfg.root_desc = xstrdup(value);
104 else if (!strcmp(name, "root-readme")) 108 else if (!strcmp(name, "root-readme"))
105 ctx.cfg.root_readme = xstrdup(value); 109 ctx.cfg.root_readme = xstrdup(value);
106 else if (!strcmp(name, "css")) 110 else if (!strcmp(name, "css"))
107 ctx.cfg.css = xstrdup(value); 111 ctx.cfg.css = xstrdup(value);
108 else if (!strcmp(name, "favicon")) 112 else if (!strcmp(name, "favicon"))
109 ctx.cfg.favicon = xstrdup(value); 113 ctx.cfg.favicon = xstrdup(value);
110 else if (!strcmp(name, "footer")) 114 else if (!strcmp(name, "footer"))
111 ctx.cfg.footer = xstrdup(value); 115 ctx.cfg.footer = xstrdup(value);
112 else if (!strcmp(name, "head-include")) 116 else if (!strcmp(name, "head-include"))
113 ctx.cfg.head_include = xstrdup(value); 117 ctx.cfg.head_include = xstrdup(value);
114 else if (!strcmp(name, "header")) 118 else if (!strcmp(name, "header"))
115 ctx.cfg.header = xstrdup(value); 119 ctx.cfg.header = xstrdup(value);
116 else if (!strcmp(name, "logo")) 120 else if (!strcmp(name, "logo"))
117 ctx.cfg.logo = xstrdup(value); 121 ctx.cfg.logo = xstrdup(value);
118 else if (!strcmp(name, "index-header")) 122 else if (!strcmp(name, "index-header"))
119 ctx.cfg.index_header = xstrdup(value); 123 ctx.cfg.index_header = xstrdup(value);
120 else if (!strcmp(name, "index-info")) 124 else if (!strcmp(name, "index-info"))
121 ctx.cfg.index_info = xstrdup(value); 125 ctx.cfg.index_info = xstrdup(value);
122 else if (!strcmp(name, "logo-link")) 126 else if (!strcmp(name, "logo-link"))
123 ctx.cfg.logo_link = xstrdup(value); 127 ctx.cfg.logo_link = xstrdup(value);
124 else if (!strcmp(name, "module-link")) 128 else if (!strcmp(name, "module-link"))
125 ctx.cfg.module_link = xstrdup(value); 129 ctx.cfg.module_link = xstrdup(value);
126 else if (!strcmp(name, "strict-export")) 130 else if (!strcmp(name, "strict-export"))
diff --git a/cgit.css b/cgit.css
index 008cff8..1d90057 100644
--- a/cgit.css
+++ b/cgit.css
@@ -248,97 +248,97 @@ table#downloads th {
248 248
249div#blob { 249div#blob {
250 border: solid 1px black; 250 border: solid 1px black;
251} 251}
252 252
253div.error { 253div.error {
254 color: red; 254 color: red;
255 font-weight: bold; 255 font-weight: bold;
256 margin: 1em 2em; 256 margin: 1em 2em;
257} 257}
258 258
259a.ls-blob, a.ls-dir, a.ls-mod { 259a.ls-blob, a.ls-dir, a.ls-mod {
260 font-family: monospace; 260 font-family: monospace;
261} 261}
262 262
263td.ls-size { 263td.ls-size {
264 text-align: right; 264 text-align: right;
265 font-family: monospace; 265 font-family: monospace;
266 width: 10em; 266 width: 10em;
267} 267}
268 268
269td.ls-mode { 269td.ls-mode {
270 font-family: monospace; 270 font-family: monospace;
271 width: 10em; 271 width: 10em;
272} 272}
273 273
274table.blob { 274table.blob {
275 margin-top: 0.5em; 275 margin-top: 0.5em;
276 border-top: solid 1px black; 276 border-top: solid 1px black;
277} 277}
278 278
279table.blob td.lines { 279table.blob td.lines {
280 margin: 0; padding: 0 0 0 0.5em; 280 margin: 0; padding: 0 0 0 0.5em;
281 vertical-align: top; 281 vertical-align: top;
282 color: black; 282 color: black;
283} 283}
284 284
285table.blob td.linenumbers { 285table.blob td.linenumbers {
286 margin: 0; padding: 0 0.5em 0 0.5em; 286 margin: 0; padding: 0 0.5em 0 0.5em;
287 vertical-align: top; 287 vertical-align: top;
288 text-align: right; 288 text-align: right;
289 border-right: 1px solid gray; 289 border-right: 1px solid gray;
290} 290}
291 291
292table.blob pre { 292table.blob pre {
293 padding: 0; margin: 0; 293 padding: 0; margin: 0;
294} 294}
295 295
296table.blob a.no { 296table.blob a.no, table.ssdiff a.no {
297 color: gray; 297 color: gray;
298 text-align: right; 298 text-align: right;
299 text-decoration: none; 299 text-decoration: none;
300} 300}
301 301
302table.blob a.no a:hover { 302table.blob a.no a:hover {
303 color: black; 303 color: black;
304} 304}
305 305
306table.bin-blob { 306table.bin-blob {
307 margin-top: 0.5em; 307 margin-top: 0.5em;
308 border: solid 1px black; 308 border: solid 1px black;
309} 309}
310 310
311table.bin-blob th { 311table.bin-blob th {
312 font-family: monospace; 312 font-family: monospace;
313 white-space: pre; 313 white-space: pre;
314 border: solid 1px #777; 314 border: solid 1px #777;
315 padding: 0.5em 1em; 315 padding: 0.5em 1em;
316} 316}
317 317
318table.bin-blob td { 318table.bin-blob td {
319 font-family: monospace; 319 font-family: monospace;
320 white-space: pre; 320 white-space: pre;
321 border-left: solid 1px #777; 321 border-left: solid 1px #777;
322 padding: 0em 1em; 322 padding: 0em 1em;
323} 323}
324 324
325table.nowrap td { 325table.nowrap td {
326 white-space: nowrap; 326 white-space: nowrap;
327} 327}
328 328
329table.commit-info { 329table.commit-info {
330 border-collapse: collapse; 330 border-collapse: collapse;
331 margin-top: 1.5em; 331 margin-top: 1.5em;
332} 332}
333 333
334table.commit-info th { 334table.commit-info th {
335 text-align: left; 335 text-align: left;
336 font-weight: normal; 336 font-weight: normal;
337 padding: 0.1em 1em 0.1em 0.1em; 337 padding: 0.1em 1em 0.1em 0.1em;
338 vertical-align: top; 338 vertical-align: top;
339} 339}
340 340
341table.commit-info td { 341table.commit-info td {
342 font-weight: normal; 342 font-weight: normal;
343 padding: 0.1em 1em 0.1em 0.1em; 343 padding: 0.1em 1em 0.1em 0.1em;
344} 344}
diff --git a/cgit.h b/cgit.h
index 74aa340..b5f00fc 100644
--- a/cgit.h
+++ b/cgit.h
@@ -26,96 +26,98 @@
26/* 26/*
27 * Dateformats used on misc. pages 27 * Dateformats used on misc. pages
28 */ 28 */
29#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 29#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
30#define FMT_SHORTDATE "%Y-%m-%d" 30#define FMT_SHORTDATE "%Y-%m-%d"
31#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 31#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
32 32
33 33
34/* 34/*
35 * Limits used for relative dates 35 * Limits used for relative dates
36 */ 36 */
37#define TM_MIN 60 37#define TM_MIN 60
38#define TM_HOUR (TM_MIN * 60) 38#define TM_HOUR (TM_MIN * 60)
39#define TM_DAY (TM_HOUR * 24) 39#define TM_DAY (TM_HOUR * 24)
40#define TM_WEEK (TM_DAY * 7) 40#define TM_WEEK (TM_DAY * 7)
41#define TM_YEAR (TM_DAY * 365) 41#define TM_YEAR (TM_DAY * 365)
42#define TM_MONTH (TM_YEAR / 12.0) 42#define TM_MONTH (TM_YEAR / 12.0)
43 43
44 44
45/* 45/*
46 * Default encoding 46 * Default encoding
47 */ 47 */
48#define PAGE_ENCODING "UTF-8" 48#define PAGE_ENCODING "UTF-8"
49 49
50typedef void (*configfn)(const char *name, const char *value); 50typedef void (*configfn)(const char *name, const char *value);
51typedef void (*filepair_fn)(struct diff_filepair *pair); 51typedef void (*filepair_fn)(struct diff_filepair *pair);
52typedef void (*linediff_fn)(char *line, int len); 52typedef void (*linediff_fn)(char *line, int len);
53 53
54struct cgit_filter { 54struct cgit_filter {
55 char *cmd; 55 char *cmd;
56 char **argv; 56 char **argv;
57 int old_stdout; 57 int old_stdout;
58 int pipe_fh[2]; 58 int pipe_fh[2];
59 int pid; 59 int pid;
60 int exitstatus; 60 int exitstatus;
61}; 61};
62 62
63struct cgit_repo { 63struct cgit_repo {
64 char *url; 64 char *url;
65 char *name; 65 char *name;
66 char *path; 66 char *path;
67 char *desc; 67 char *desc;
68 char *owner; 68 char *owner;
69 char *defbranch; 69 char *defbranch;
70 char *module_link; 70 char *module_link;
71 char *readme; 71 char *readme;
72 char *section; 72 char *section;
73 char *clone_url; 73 char *clone_url;
74 char *logo;
75 char *logo_link;
74 int snapshots; 76 int snapshots;
75 int enable_commit_graph; 77 int enable_commit_graph;
76 int enable_log_filecount; 78 int enable_log_filecount;
77 int enable_log_linecount; 79 int enable_log_linecount;
78 int enable_remote_branches; 80 int enable_remote_branches;
79 int enable_subject_links; 81 int enable_subject_links;
80 int max_stats; 82 int max_stats;
81 time_t mtime; 83 time_t mtime;
82 struct cgit_filter *about_filter; 84 struct cgit_filter *about_filter;
83 struct cgit_filter *commit_filter; 85 struct cgit_filter *commit_filter;
84 struct cgit_filter *source_filter; 86 struct cgit_filter *source_filter;
85}; 87};
86 88
87typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 89typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
88 const char *value); 90 const char *value);
89 91
90struct cgit_repolist { 92struct cgit_repolist {
91 int length; 93 int length;
92 int count; 94 int count;
93 struct cgit_repo *repos; 95 struct cgit_repo *repos;
94}; 96};
95 97
96struct commitinfo { 98struct commitinfo {
97 struct commit *commit; 99 struct commit *commit;
98 char *author; 100 char *author;
99 char *author_email; 101 char *author_email;
100 unsigned long author_date; 102 unsigned long author_date;
101 char *committer; 103 char *committer;
102 char *committer_email; 104 char *committer_email;
103 unsigned long committer_date; 105 unsigned long committer_date;
104 char *subject; 106 char *subject;
105 char *msg; 107 char *msg;
106 char *msg_encoding; 108 char *msg_encoding;
107}; 109};
108 110
109struct taginfo { 111struct taginfo {
110 char *tagger; 112 char *tagger;
111 char *tagger_email; 113 char *tagger_email;
112 unsigned long tagger_date; 114 unsigned long tagger_date;
113 char *msg; 115 char *msg;
114}; 116};
115 117
116struct refinfo { 118struct refinfo {
117 const char *refname; 119 const char *refname;
118 struct object *object; 120 struct object *object;
119 union { 121 union {
120 struct taginfo *tag; 122 struct taginfo *tag;
121 struct commitinfo *commit; 123 struct commitinfo *commit;
diff --git a/cgit.png b/cgit.png
index d7f70bc..0bdf5a7 100644
--- a/cgit.png
+++ b/cgit.png
Binary files differ
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index a832830..c3698a6 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -342,96 +342,105 @@ virtual-root::
342 will also cause cgit to generate 'virtual urls', i.e. urls like 342 will also cause cgit to generate 'virtual urls', i.e. urls like
343 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 343 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
344 value: none. 344 value: none.
345 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 345 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
346 same kind of virtual urls, so this option will probably be deprecated. 346 same kind of virtual urls, so this option will probably be deprecated.
347 347
348REPOSITORY SETTINGS 348REPOSITORY SETTINGS
349------------------- 349-------------------
350repo.about-filter:: 350repo.about-filter::
351 Override the default about-filter. Default value: none. See also: 351 Override the default about-filter. Default value: none. See also:
352 "enable-filter-overrides". 352 "enable-filter-overrides".
353 353
354repo.clone-url:: 354repo.clone-url::
355 A list of space-separated urls which can be used to clone this repo. 355 A list of space-separated urls which can be used to clone this repo.
356 Default value: none. 356 Default value: none.
357 357
358repo.commit-filter:: 358repo.commit-filter::
359 Override the default commit-filter. Default value: none. See also: 359 Override the default commit-filter. Default value: none. See also:
360 "enable-filter-overrides". 360 "enable-filter-overrides".
361 361
362repo.defbranch:: 362repo.defbranch::
363 The name of the default branch for this repository. If no such branch 363 The name of the default branch for this repository. If no such branch
364 exists in the repository, the first branch name (when sorted) is used 364 exists in the repository, the first branch name (when sorted) is used
365 as default instead. Default value: "master". 365 as default instead. Default value: "master".
366 366
367repo.desc:: 367repo.desc::
368 The value to show as repository description. Default value: none. 368 The value to show as repository description. Default value: none.
369 369
370repo.enable-commit-graph:: 370repo.enable-commit-graph::
371 A flag which can be used to disable the global setting 371 A flag which can be used to disable the global setting
372 `enable-commit-graph'. Default value: none. 372 `enable-commit-graph'. Default value: none.
373 373
374repo.enable-log-filecount:: 374repo.enable-log-filecount::
375 A flag which can be used to disable the global setting 375 A flag which can be used to disable the global setting
376 `enable-log-filecount'. Default value: none. 376 `enable-log-filecount'. Default value: none.
377 377
378repo.enable-log-linecount:: 378repo.enable-log-linecount::
379 A flag which can be used to disable the global setting 379 A flag which can be used to disable the global setting
380 `enable-log-linecount'. Default value: none. 380 `enable-log-linecount'. Default value: none.
381 381
382repo.enable-remote-branches:: 382repo.enable-remote-branches::
383 Flag which, when set to "1", will make cgit display remote branches 383 Flag which, when set to "1", will make cgit display remote branches
384 in the summary and refs views. Default value: <enable-remote-branches>. 384 in the summary and refs views. Default value: <enable-remote-branches>.
385 385
386repo.enable-subject-links:: 386repo.enable-subject-links::
387 A flag which can be used to override the global setting 387 A flag which can be used to override the global setting
388 `enable-subject-links'. Default value: none. 388 `enable-subject-links'. Default value: none.
389 389
390repo.logo::
391 Url which specifies the source of an image which will be used as a logo
392 on this repo's pages. Default value: global logo.
393
394repo.logo-link::
395 Url loaded when clicking on the cgit logo image. If unspecified the
396 calculated url of the repository index page will be used. Default
397 value: global logo-link.
398
390repo.max-stats:: 399repo.max-stats::
391 Override the default maximum statistics period. Valid values are equal 400 Override the default maximum statistics period. Valid values are equal
392 to the values specified for the global "max-stats" setting. Default 401 to the values specified for the global "max-stats" setting. Default
393 value: none. 402 value: none.
394 403
395repo.name:: 404repo.name::
396 The value to show as repository name. Default value: <repo.url>. 405 The value to show as repository name. Default value: <repo.url>.
397 406
398repo.owner:: 407repo.owner::
399 A value used to identify the owner of the repository. Default value: 408 A value used to identify the owner of the repository. Default value:
400 none. 409 none.
401 410
402repo.path:: 411repo.path::
403 An absolute path to the repository directory. For non-bare repositories 412 An absolute path to the repository directory. For non-bare repositories
404 this is the .git-directory. Default value: none. 413 this is the .git-directory. Default value: none.
405 414
406repo.readme:: 415repo.readme::
407 A path (relative to <repo.path>) which specifies a file to include 416 A path (relative to <repo.path>) which specifies a file to include
408 verbatim as the "About" page for this repo. You may also specify a 417 verbatim as the "About" page for this repo. You may also specify a
409 git refspec by head or by hash by prepending the refspec followed by 418 git refspec by head or by hash by prepending the refspec followed by
410 a colon. For example, "master:docs/readme.mkd" Default value: <readme>. 419 a colon. For example, "master:docs/readme.mkd" Default value: <readme>.
411 420
412repo.snapshots:: 421repo.snapshots::
413 A mask of allowed snapshot-formats for this repo, restricted by the 422 A mask of allowed snapshot-formats for this repo, restricted by the
414 "snapshots" global setting. Default value: <snapshots>. 423 "snapshots" global setting. Default value: <snapshots>.
415 424
416repo.section:: 425repo.section::
417 Override the current section name for this repository. Default value: 426 Override the current section name for this repository. Default value:
418 none. 427 none.
419 428
420repo.source-filter:: 429repo.source-filter::
421 Override the default source-filter. Default value: none. See also: 430 Override the default source-filter. Default value: none. See also:
422 "enable-filter-overrides". 431 "enable-filter-overrides".
423 432
424repo.url:: 433repo.url::
425 The relative url used to access the repository. This must be the first 434 The relative url used to access the repository. This must be the first
426 setting specified for each repo. Default value: none. 435 setting specified for each repo. Default value: none.
427 436
428 437
429REPOSITORY-SPECIFIC CGITRC FILE 438REPOSITORY-SPECIFIC CGITRC FILE
430------------------------------- 439-------------------------------
431When the option "scan-path" is used to auto-discover git repositories, cgit 440When the option "scan-path" is used to auto-discover git repositories, cgit
432will try to parse the file "cgitrc" within any found repository. Such a 441will try to parse the file "cgitrc" within any found repository. Such a
433repo-specific config file may contain any of the repo-specific options 442repo-specific config file may contain any of the repo-specific options
434described above, except "repo.url" and "repo.path". Additionally, the "filter" 443described above, except "repo.url" and "repo.path". Additionally, the "filter"
435options are only acknowledged in repo-specific config files when 444options are only acknowledged in repo-specific config files when
436"enable-filter-overrides" is set to "1". 445"enable-filter-overrides" is set to "1".
437 446
diff --git a/ui-diff.c b/ui-diff.c
index 7ff7e46..a53425d 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -1,84 +1,95 @@
1/* ui-diff.c: show diff between two blobs 1/* ui-diff.c: show diff between two blobs
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;
37static struct diff_filepair *current_filepair;
38
39struct diff_filespec *cgit_get_current_old_file(void)
40{
41 return current_filepair->one;
42}
43
44struct diff_filespec *cgit_get_current_new_file(void)
45{
46 return current_filepair->two;
47}
37 48
38static void print_fileinfo(struct fileinfo *info) 49static void print_fileinfo(struct fileinfo *info)
39{ 50{
40 char *class; 51 char *class;
41 52
42 switch (info->status) { 53 switch (info->status) {
43 case DIFF_STATUS_ADDED: 54 case DIFF_STATUS_ADDED:
44 class = "add"; 55 class = "add";
45 break; 56 break;
46 case DIFF_STATUS_COPIED: 57 case DIFF_STATUS_COPIED:
47 class = "cpy"; 58 class = "cpy";
48 break; 59 break;
49 case DIFF_STATUS_DELETED: 60 case DIFF_STATUS_DELETED:
50 class = "del"; 61 class = "del";
51 break; 62 break;
52 case DIFF_STATUS_MODIFIED: 63 case DIFF_STATUS_MODIFIED:
53 class = "upd"; 64 class = "upd";
54 break; 65 break;
55 case DIFF_STATUS_RENAMED: 66 case DIFF_STATUS_RENAMED:
56 class = "mov"; 67 class = "mov";
57 break; 68 break;
58 case DIFF_STATUS_TYPE_CHANGED: 69 case DIFF_STATUS_TYPE_CHANGED:
59 class = "typ"; 70 class = "typ";
60 break; 71 break;
61 case DIFF_STATUS_UNKNOWN: 72 case DIFF_STATUS_UNKNOWN:
62 class = "unk"; 73 class = "unk";
63 break; 74 break;
64 case DIFF_STATUS_UNMERGED: 75 case DIFF_STATUS_UNMERGED:
65 class = "stg"; 76 class = "stg";
66 break; 77 break;
67 default: 78 default:
68 die("bug: unhandled diff status %c", info->status); 79 die("bug: unhandled diff status %c", info->status);
69 } 80 }
70 81
71 html("<tr>"); 82 html("<tr>");
72 htmlf("<td class='mode'>"); 83 htmlf("<td class='mode'>");
73 if (is_null_sha1(info->new_sha1)) { 84 if (is_null_sha1(info->new_sha1)) {
74 cgit_print_filemode(info->old_mode); 85 cgit_print_filemode(info->old_mode);
75 } else { 86 } else {
76 cgit_print_filemode(info->new_mode); 87 cgit_print_filemode(info->new_mode);
77 } 88 }
78 89
79 if (info->old_mode != info->new_mode && 90 if (info->old_mode != info->new_mode &&
80 !is_null_sha1(info->old_sha1) && 91 !is_null_sha1(info->old_sha1) &&
81 !is_null_sha1(info->new_sha1)) { 92 !is_null_sha1(info->new_sha1)) {
82 html("<span class='modechange'>["); 93 html("<span class='modechange'>[");
83 cgit_print_filemode(info->old_mode); 94 cgit_print_filemode(info->old_mode);
84 html("]</span>"); 95 html("]</span>");
@@ -239,96 +250,97 @@ static void header(unsigned char *sha1, char *path1, int mode1,
239 250
240 if (!subproject) { 251 if (!subproject) {
241 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); 252 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
242 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); 253 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
243 htmlf("<br/>index %s..%s", abbrev1, abbrev2); 254 htmlf("<br/>index %s..%s", abbrev1, abbrev2);
244 free(abbrev1); 255 free(abbrev1);
245 free(abbrev2); 256 free(abbrev2);
246 if (mode1 != 0 && mode2 != 0) { 257 if (mode1 != 0 && mode2 != 0) {
247 htmlf(" %.6o", mode1); 258 htmlf(" %.6o", mode1);
248 if (mode2 != mode1) 259 if (mode2 != mode1)
249 htmlf("..%.6o", mode2); 260 htmlf("..%.6o", mode2);
250 } 261 }
251 html("<br/>--- a/"); 262 html("<br/>--- a/");
252 if (mode1 != 0) 263 if (mode1 != 0)
253 cgit_tree_link(path1, NULL, NULL, ctx.qry.head, 264 cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
254 sha1_to_hex(old_rev_sha1), path1); 265 sha1_to_hex(old_rev_sha1), path1);
255 else 266 else
256 html_txt(path1); 267 html_txt(path1);
257 html("<br/>+++ b/"); 268 html("<br/>+++ b/");
258 if (mode2 != 0) 269 if (mode2 != 0)
259 cgit_tree_link(path2, NULL, NULL, ctx.qry.head, 270 cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
260 sha1_to_hex(new_rev_sha1), path2); 271 sha1_to_hex(new_rev_sha1), path2);
261 else 272 else
262 html_txt(path2); 273 html_txt(path2);
263 } 274 }
264 html("</div>"); 275 html("</div>");
265} 276}
266 277
267static void print_ssdiff_link() 278static void print_ssdiff_link()
268{ 279{
269 if (!strcmp(ctx.qry.page, "diff")) { 280 if (!strcmp(ctx.qry.page, "diff")) {
270 if (use_ssdiff) 281 if (use_ssdiff)
271 cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head, 282 cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head,
272 ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1); 283 ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1);
273 else 284 else
274 cgit_diff_link("Side-by-side diff", NULL, NULL, 285 cgit_diff_link("Side-by-side diff", NULL, NULL,
275 ctx.qry.head, ctx.qry.sha1, 286 ctx.qry.head, ctx.qry.sha1,
276 ctx.qry.sha2, ctx.qry.path, 1); 287 ctx.qry.sha2, ctx.qry.path, 1);
277 } 288 }
278} 289}
279 290
280static void filepair_cb(struct diff_filepair *pair) 291static void filepair_cb(struct diff_filepair *pair)
281{ 292{
282 unsigned long old_size = 0; 293 unsigned long old_size = 0;
283 unsigned long new_size = 0; 294 unsigned long new_size = 0;
284 int binary = 0; 295 int binary = 0;
285 linediff_fn print_line_fn = print_line; 296 linediff_fn print_line_fn = print_line;
286 297
298 current_filepair = pair;
287 if (use_ssdiff) { 299 if (use_ssdiff) {
288 cgit_ssdiff_header_begin(); 300 cgit_ssdiff_header_begin();
289 print_line_fn = cgit_ssdiff_line_cb; 301 print_line_fn = cgit_ssdiff_line_cb;
290 } 302 }
291 header(pair->one->sha1, pair->one->path, pair->one->mode, 303 header(pair->one->sha1, pair->one->path, pair->one->mode,
292 pair->two->sha1, pair->two->path, pair->two->mode); 304 pair->two->sha1, pair->two->path, pair->two->mode);
293 if (use_ssdiff) 305 if (use_ssdiff)
294 cgit_ssdiff_header_end(); 306 cgit_ssdiff_header_end();
295 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 307 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
296 if (S_ISGITLINK(pair->one->mode)) 308 if (S_ISGITLINK(pair->one->mode))
297 print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 309 print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
298 if (S_ISGITLINK(pair->two->mode)) 310 if (S_ISGITLINK(pair->two->mode))
299 print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 311 print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
300 if (use_ssdiff) 312 if (use_ssdiff)
301 cgit_ssdiff_footer(); 313 cgit_ssdiff_footer();
302 return; 314 return;
303 } 315 }
304 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 316 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
305 &new_size, &binary, ctx.qry.context, 317 &new_size, &binary, ctx.qry.context,
306 ctx.qry.ignorews, print_line_fn)) 318 ctx.qry.ignorews, print_line_fn))
307 cgit_print_error("Error running diff"); 319 cgit_print_error("Error running diff");
308 if (binary) { 320 if (binary) {
309 if (use_ssdiff) 321 if (use_ssdiff)
310 html("<tr><td colspan='4'>Binary files differ</td></tr>"); 322 html("<tr><td colspan='4'>Binary files differ</td></tr>");
311 else 323 else
312 html("Binary files differ"); 324 html("Binary files differ");
313 } 325 }
314 if (use_ssdiff) 326 if (use_ssdiff)
315 cgit_ssdiff_footer(); 327 cgit_ssdiff_footer();
316} 328}
317 329
318void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) 330void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix)
319{ 331{
320 enum object_type type; 332 enum object_type type;
321 unsigned long size; 333 unsigned long size;
322 struct commit *commit, *commit2; 334 struct commit *commit, *commit2;
323 335
324 if (!new_rev) 336 if (!new_rev)
325 new_rev = ctx.qry.head; 337 new_rev = ctx.qry.head;
326 get_sha1(new_rev, new_rev_sha1); 338 get_sha1(new_rev, new_rev_sha1);
327 type = sha1_object_info(new_rev_sha1, &size); 339 type = sha1_object_info(new_rev_sha1, &size);
328 if (type == OBJ_BAD) { 340 if (type == OBJ_BAD) {
329 cgit_print_error(fmt("Bad object name: %s", new_rev)); 341 cgit_print_error(fmt("Bad object name: %s", new_rev));
330 return; 342 return;
331 } 343 }
332 commit = lookup_commit_reference(new_rev_sha1); 344 commit = lookup_commit_reference(new_rev_sha1);
333 if (!commit || parse_commit(commit)) 345 if (!commit || parse_commit(commit))
334 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1))); 346 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1)));
diff --git a/ui-diff.h b/ui-diff.h
index 70b2926..12d0c62 100644
--- a/ui-diff.h
+++ b/ui-diff.h
@@ -1,10 +1,16 @@
1#ifndef UI_DIFF_H 1#ifndef UI_DIFF_H
2#define UI_DIFF_H 2#define UI_DIFF_H
3 3
4extern void cgit_print_diffstat(const unsigned char *old_sha1, 4extern void cgit_print_diffstat(const unsigned char *old_sha1,
5 const unsigned char *new_sha1); 5 const unsigned char *new_sha1);
6 6
7extern void cgit_print_diff(const char *new_hex, const char *old_hex, 7extern void cgit_print_diff(const char *new_hex, const char *old_hex,
8 const char *prefix); 8 const char *prefix);
9 9
10extern struct diff_filespec *cgit_get_current_old_file(void);
11extern struct diff_filespec *cgit_get_current_new_file(void);
12
13extern unsigned char old_rev_sha1[20];
14extern unsigned char new_rev_sha1[20];
15
10#endif /* UI_DIFF_H */ 16#endif /* UI_DIFF_H */
diff --git a/ui-shared.c b/ui-shared.c
index ae29615..7efae7a 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -711,107 +711,117 @@ void cgit_add_hidden_formfields(int incl_head, int incl_search,
711 711
712 if (incl_head && ctx.qry.head && ctx.repo->defbranch && 712 if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
713 strcmp(ctx.qry.head, ctx.repo->defbranch)) 713 strcmp(ctx.qry.head, ctx.repo->defbranch))
714 html_hidden("h", ctx.qry.head); 714 html_hidden("h", ctx.qry.head);
715 715
716 if (ctx.qry.sha1) 716 if (ctx.qry.sha1)
717 html_hidden("id", ctx.qry.sha1); 717 html_hidden("id", ctx.qry.sha1);
718 if (ctx.qry.sha2) 718 if (ctx.qry.sha2)
719 html_hidden("id2", ctx.qry.sha2); 719 html_hidden("id2", ctx.qry.sha2);
720 if (ctx.qry.showmsg) 720 if (ctx.qry.showmsg)
721 html_hidden("showmsg", "1"); 721 html_hidden("showmsg", "1");
722 722
723 if (incl_search) { 723 if (incl_search) {
724 if (ctx.qry.grep) 724 if (ctx.qry.grep)
725 html_hidden("qt", ctx.qry.grep); 725 html_hidden("qt", ctx.qry.grep);
726 if (ctx.qry.search) 726 if (ctx.qry.search)
727 html_hidden("q", ctx.qry.search); 727 html_hidden("q", ctx.qry.search);
728 } 728 }
729} 729}
730 730
731static const char *hc(struct cgit_context *ctx, const char *page) 731static const char *hc(struct cgit_context *ctx, const char *page)
732{ 732{
733 return strcmp(ctx->qry.page, page) ? NULL : "active"; 733 return strcmp(ctx->qry.page, page) ? NULL : "active";
734} 734}
735 735
736static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path) 736static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path)
737{ 737{
738 char *old_path = ctx->qry.path; 738 char *old_path = ctx->qry.path;
739 char *p = path, *q, *end = path + strlen(path); 739 char *p = path, *q, *end = path + strlen(path);
740 740
741 ctx->qry.path = NULL; 741 ctx->qry.path = NULL;
742 cgit_self_link("root", NULL, NULL, ctx); 742 cgit_self_link("root", NULL, NULL, ctx);
743 ctx->qry.path = p = path; 743 ctx->qry.path = p = path;
744 while (p < end) { 744 while (p < end) {
745 if (!(q = strchr(p, '/'))) 745 if (!(q = strchr(p, '/')))
746 q = end; 746 q = end;
747 *q = '\0'; 747 *q = '\0';
748 html_txt("/"); 748 html_txt("/");
749 cgit_self_link(p, NULL, NULL, ctx); 749 cgit_self_link(p, NULL, NULL, ctx);
750 if (q < end) 750 if (q < end)
751 *q = '/'; 751 *q = '/';
752 p = q + 1; 752 p = q + 1;
753 } 753 }
754 ctx->qry.path = old_path; 754 ctx->qry.path = old_path;
755} 755}
756 756
757static void print_header(struct cgit_context *ctx) 757static void print_header(struct cgit_context *ctx)
758{ 758{
759 char *logo = NULL, *logo_link = NULL;
760
759 html("<table id='header'>\n"); 761 html("<table id='header'>\n");
760 html("<tr>\n"); 762 html("<tr>\n");
761 763
762 if (ctx->cfg.logo && ctx->cfg.logo[0] != 0) { 764 if (ctx->repo && ctx->repo->logo && *ctx->repo->logo)
765 logo = ctx->repo->logo;
766 else
767 logo = ctx->cfg.logo;
768 if (ctx->repo && ctx->repo->logo_link && *ctx->repo->logo_link)
769 logo_link = ctx->repo->logo_link;
770 else
771 logo_link = ctx->cfg.logo_link;
772 if (logo && *logo) {
763 html("<td class='logo' rowspan='2'><a href='"); 773 html("<td class='logo' rowspan='2'><a href='");
764 if (ctx->cfg.logo_link) 774 if (logo_link && *logo_link)
765 html_attr(ctx->cfg.logo_link); 775 html_attr(logo_link);
766 else 776 else
767 html_attr(cgit_rooturl()); 777 html_attr(cgit_rooturl());
768 html("'><img src='"); 778 html("'><img src='");
769 html_attr(ctx->cfg.logo); 779 html_attr(logo);
770 html("' alt='cgit logo'/></a></td>\n"); 780 html("' alt='cgit logo'/></a></td>\n");
771 } 781 }
772 782
773 html("<td class='main'>"); 783 html("<td class='main'>");
774 if (ctx->repo) { 784 if (ctx->repo) {
775 cgit_index_link("index", NULL, NULL, NULL, 0); 785 cgit_index_link("index", NULL, NULL, NULL, 0);
776 html(" : "); 786 html(" : ");
777 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL); 787 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL);
778 html("</td><td class='form'>"); 788 html("</td><td class='form'>");
779 html("<form method='get' action=''>\n"); 789 html("<form method='get' action=''>\n");
780 cgit_add_hidden_formfields(0, 1, ctx->qry.page); 790 cgit_add_hidden_formfields(0, 1, ctx->qry.page);
781 html("<select name='h' onchange='this.form.submit();'>\n"); 791 html("<select name='h' onchange='this.form.submit();'>\n");
782 for_each_branch_ref(print_branch_option, ctx->qry.head); 792 for_each_branch_ref(print_branch_option, ctx->qry.head);
783 html("</select> "); 793 html("</select> ");
784 html("<input type='submit' name='' value='switch'/>"); 794 html("<input type='submit' name='' value='switch'/>");
785 html("</form>"); 795 html("</form>");
786 } else 796 } else
787 html_txt(ctx->cfg.root_title); 797 html_txt(ctx->cfg.root_title);
788 html("</td></tr>\n"); 798 html("</td></tr>\n");
789 799
790 html("<tr><td class='sub'>"); 800 html("<tr><td class='sub'>");
791 if (ctx->repo) { 801 if (ctx->repo) {
792 html_txt(ctx->repo->desc); 802 html_txt(ctx->repo->desc);
793 html("</td><td class='sub right'>"); 803 html("</td><td class='sub right'>");
794 html_txt(ctx->repo->owner); 804 html_txt(ctx->repo->owner);
795 } else { 805 } else {
796 if (ctx->cfg.root_desc) 806 if (ctx->cfg.root_desc)
797 html_txt(ctx->cfg.root_desc); 807 html_txt(ctx->cfg.root_desc);
798 else if (ctx->cfg.index_info) 808 else if (ctx->cfg.index_info)
799 html_include(ctx->cfg.index_info); 809 html_include(ctx->cfg.index_info);
800 } 810 }
801 html("</td></tr></table>\n"); 811 html("</td></tr></table>\n");
802} 812}
803 813
804void cgit_print_pageheader(struct cgit_context *ctx) 814void cgit_print_pageheader(struct cgit_context *ctx)
805{ 815{
806 html("<div id='cgit'>"); 816 html("<div id='cgit'>");
807 if (!ctx->cfg.noheader) 817 if (!ctx->cfg.noheader)
808 print_header(ctx); 818 print_header(ctx);
809 819
810 html("<table class='tabs'><tr><td>\n"); 820 html("<table class='tabs'><tr><td>\n");
811 if (ctx->repo) { 821 if (ctx->repo) {
812 cgit_summary_link("summary", NULL, hc(ctx, "summary"), 822 cgit_summary_link("summary", NULL, hc(ctx, "summary"),
813 ctx->qry.head); 823 ctx->qry.head);
814 cgit_refs_link("refs", NULL, hc(ctx, "refs"), ctx->qry.head, 824 cgit_refs_link("refs", NULL, hc(ctx, "refs"), ctx->qry.head,
815 ctx->qry.sha1, NULL); 825 ctx->qry.sha1, NULL);
816 cgit_log_link("log", NULL, hc(ctx, "log"), ctx->qry.head, 826 cgit_log_link("log", NULL, hc(ctx, "log"), ctx->qry.head,
817 NULL, ctx->qry.vpath, 0, NULL, NULL, 827 NULL, ctx->qry.vpath, 0, NULL, NULL,
diff --git a/ui-ssdiff.c b/ui-ssdiff.c
index 408e620..2481585 100644
--- a/ui-ssdiff.c
+++ b/ui-ssdiff.c
@@ -1,51 +1,52 @@
1#include "cgit.h" 1#include "cgit.h"
2#include "html.h" 2#include "html.h"
3#include "ui-shared.h" 3#include "ui-shared.h"
4#include "ui-diff.h"
4 5
5extern int use_ssdiff; 6extern int use_ssdiff;
6 7
7static int current_old_line, current_new_line; 8static int current_old_line, current_new_line;
8 9
9struct deferred_lines { 10struct deferred_lines {
10 int line_no; 11 int line_no;
11 char *line; 12 char *line;
12 struct deferred_lines *next; 13 struct deferred_lines *next;
13}; 14};
14 15
15static struct deferred_lines *deferred_old, *deferred_old_last; 16static struct deferred_lines *deferred_old, *deferred_old_last;
16static struct deferred_lines *deferred_new, *deferred_new_last; 17static struct deferred_lines *deferred_new, *deferred_new_last;
17 18
18static char *longest_common_subsequence(char *A, char *B) 19static char *longest_common_subsequence(char *A, char *B)
19{ 20{
20 int i, j, ri; 21 int i, j, ri;
21 int m = strlen(A); 22 int m = strlen(A);
22 int n = strlen(B); 23 int n = strlen(B);
23 int L[m + 1][n + 1]; 24 int L[m + 1][n + 1];
24 int tmp1, tmp2; 25 int tmp1, tmp2;
25 int lcs_length; 26 int lcs_length;
26 char *result; 27 char *result;
27 28
28 for (i = m; i >= 0; i--) { 29 for (i = m; i >= 0; i--) {
29 for (j = n; j >= 0; j--) { 30 for (j = n; j >= 0; j--) {
30 if (A[i] == '\0' || B[j] == '\0') { 31 if (A[i] == '\0' || B[j] == '\0') {
31 L[i][j] = 0; 32 L[i][j] = 0;
32 } else if (A[i] == B[j]) { 33 } else if (A[i] == B[j]) {
33 L[i][j] = 1 + L[i + 1][j + 1]; 34 L[i][j] = 1 + L[i + 1][j + 1];
34 } else { 35 } else {
35 tmp1 = L[i + 1][j]; 36 tmp1 = L[i + 1][j];
36 tmp2 = L[i][j + 1]; 37 tmp2 = L[i][j + 1];
37 L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2); 38 L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
38 } 39 }
39 } 40 }
40 } 41 }
41 42
42 lcs_length = L[0][0]; 43 lcs_length = L[0][0];
43 result = xmalloc(lcs_length + 2); 44 result = xmalloc(lcs_length + 2);
44 memset(result, 0, sizeof(*result) * (lcs_length + 2)); 45 memset(result, 0, sizeof(*result) * (lcs_length + 2));
45 46
46 ri = 0; 47 ri = 0;
47 i = 0; 48 i = 0;
48 j = 0; 49 j = 0;
49 while (i < m && j < n) { 50 while (i < m && j < n) {
50 if (A[i] == B[j]) { 51 if (A[i] == B[j]) {
51 result[ri] = A[i]; 52 result[ri] = A[i];
@@ -146,122 +147,135 @@ static void deferred_old_add(char *line, int line_no)
146 147
147static void deferred_new_add(char *line, int line_no) 148static void deferred_new_add(char *line, int line_no)
148{ 149{
149 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines)); 150 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
150 item->line = xstrdup(line); 151 item->line = xstrdup(line);
151 item->line_no = line_no; 152 item->line_no = line_no;
152 item->next = NULL; 153 item->next = NULL;
153 if (deferred_new) { 154 if (deferred_new) {
154 deferred_new_last->next = item; 155 deferred_new_last->next = item;
155 deferred_new_last = item; 156 deferred_new_last = item;
156 } else { 157 } else {
157 deferred_new = deferred_new_last = item; 158 deferred_new = deferred_new_last = item;
158 } 159 }
159} 160}
160 161
161static void print_part_with_lcs(char *class, char *line, char *lcs) 162static void print_part_with_lcs(char *class, char *line, char *lcs)
162{ 163{
163 int line_len = strlen(line); 164 int line_len = strlen(line);
164 int i, j; 165 int i, j;
165 char c[2] = " "; 166 char c[2] = " ";
166 int same = 1; 167 int same = 1;
167 168
168 j = 0; 169 j = 0;
169 for (i = 0; i < line_len; i++) { 170 for (i = 0; i < line_len; i++) {
170 c[0] = line[i]; 171 c[0] = line[i];
171 if (same) { 172 if (same) {
172 if (line[i] == lcs[j]) 173 if (line[i] == lcs[j])
173 j += 1; 174 j += 1;
174 else { 175 else {
175 same = 0; 176 same = 0;
176 htmlf("<span class='%s'>", class); 177 htmlf("<span class='%s'>", class);
177 } 178 }
178 } else if (line[i] == lcs[j]) { 179 } else if (line[i] == lcs[j]) {
179 same = 1; 180 same = 1;
180 htmlf("</span>"); 181 htmlf("</span>");
181 j += 1; 182 j += 1;
182 } 183 }
183 html_txt(c); 184 html_txt(c);
184 } 185 }
185} 186}
186 187
187static void print_ssdiff_line(char *class, 188static void print_ssdiff_line(char *class,
188 int old_line_no, 189 int old_line_no,
189 char *old_line, 190 char *old_line,
190 int new_line_no, 191 int new_line_no,
191 char *new_line, int individual_chars) 192 char *new_line, int individual_chars)
192{ 193{
193 char *lcs = NULL; 194 char *lcs = NULL;
195
194 if (old_line) 196 if (old_line)
195 old_line = replace_tabs(old_line + 1); 197 old_line = replace_tabs(old_line + 1);
196 if (new_line) 198 if (new_line)
197 new_line = replace_tabs(new_line + 1); 199 new_line = replace_tabs(new_line + 1);
198 if (individual_chars && old_line && new_line) 200 if (individual_chars && old_line && new_line)
199 lcs = longest_common_subsequence(old_line, new_line); 201 lcs = longest_common_subsequence(old_line, new_line);
200 html("<tr>"); 202 html("<tr>\n");
201 if (old_line_no > 0) 203 if (old_line_no > 0) {
202 htmlf("<td class='lineno'>%d</td><td class='%s'>", 204 struct diff_filespec *old_file = cgit_get_current_old_file();
203 old_line_no, class); 205 char *lineno_str = fmt("n%d", old_line_no);
204 else if (old_line) 206 char *id_str = fmt("%s#%s", is_null_sha1(old_file->sha1)?"HEAD":sha1_to_hex(old_rev_sha1), lineno_str);
207 html("<td class='lineno'><a class='no' href='");
208 html(cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str));
209 htmlf("' id='%s' name='%s'>%s</a>", lineno_str, lineno_str, lineno_str + 1);
210 html("</td>");
211 htmlf("<td class='%s'>", class);
212 } else if (old_line)
205 htmlf("<td class='lineno'></td><td class='%s'>", class); 213 htmlf("<td class='lineno'></td><td class='%s'>", class);
206 else 214 else
207 htmlf("<td class='lineno'></td><td class='%s_dark'>", class); 215 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
208 if (old_line) { 216 if (old_line) {
209 if (lcs) 217 if (lcs)
210 print_part_with_lcs("del", old_line, lcs); 218 print_part_with_lcs("del", old_line, lcs);
211 else 219 else
212 html_txt(old_line); 220 html_txt(old_line);
213 } 221 }
214 222
215 html("</td>"); 223 html("</td>\n");
216 if (new_line_no > 0) 224 if (new_line_no > 0) {
217 htmlf("<td class='lineno'>%d</td><td class='%s'>", 225 struct diff_filespec *new_file = cgit_get_current_new_file();
218 new_line_no, class); 226 char *lineno_str = fmt("n%d", new_line_no);
219 else if (new_line) 227 char *id_str = fmt("%s#%s", is_null_sha1(new_file->sha1)?"HEAD":sha1_to_hex(new_rev_sha1), lineno_str);
228 html("<td class='lineno'><a class='no' href='");
229 html(cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str));
230 htmlf("' id='%s' name='%s'>%s</a>", lineno_str, lineno_str, lineno_str + 1);
231 html("</td>");
232 htmlf("<td class='%s'>", class);
233 } else if (new_line)
220 htmlf("<td class='lineno'></td><td class='%s'>", class); 234 htmlf("<td class='lineno'></td><td class='%s'>", class);
221 else 235 else
222 htmlf("<td class='lineno'></td><td class='%s_dark'>", class); 236 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
223 if (new_line) { 237 if (new_line) {
224 if (lcs) 238 if (lcs)
225 print_part_with_lcs("add", new_line, lcs); 239 print_part_with_lcs("add", new_line, lcs);
226 else 240 else
227 html_txt(new_line); 241 html_txt(new_line);
228 } 242 }
229 243
230 html("</td></tr>"); 244 html("</td></tr>");
231 if (lcs) 245 if (lcs)
232 free(lcs); 246 free(lcs);
233 if (new_line) 247 if (new_line)
234 free(new_line); 248 free(new_line);
235 if (old_line) 249 if (old_line)
236 free(old_line); 250 free(old_line);
237} 251}
238 252
239static void print_deferred_old_lines() 253static void print_deferred_old_lines()
240{ 254{
241 struct deferred_lines *iter_old, *tmp; 255 struct deferred_lines *iter_old, *tmp;
242 iter_old = deferred_old; 256 iter_old = deferred_old;
243 while (iter_old) { 257 while (iter_old) {
244 print_ssdiff_line("del", iter_old->line_no, 258 print_ssdiff_line("del", iter_old->line_no,
245 iter_old->line, -1, NULL, 0); 259 iter_old->line, -1, NULL, 0);
246 tmp = iter_old->next; 260 tmp = iter_old->next;
247 free(iter_old); 261 free(iter_old);
248 iter_old = tmp; 262 iter_old = tmp;
249 } 263 }
250} 264}
251 265
252static void print_deferred_new_lines() 266static void print_deferred_new_lines()
253{ 267{
254 struct deferred_lines *iter_new, *tmp; 268 struct deferred_lines *iter_new, *tmp;
255 iter_new = deferred_new; 269 iter_new = deferred_new;
256 while (iter_new) { 270 while (iter_new) {
257 print_ssdiff_line("add", -1, NULL, 271 print_ssdiff_line("add", -1, NULL,
258 iter_new->line_no, iter_new->line, 0); 272 iter_new->line_no, iter_new->line, 0);
259 tmp = iter_new->next; 273 tmp = iter_new->next;
260 free(iter_new); 274 free(iter_new);
261 iter_new = tmp; 275 iter_new = tmp;
262 } 276 }
263} 277}
264 278
265static void print_deferred_changed_lines() 279static void print_deferred_changed_lines()
266{ 280{
267 struct deferred_lines *iter_old, *iter_new, *tmp; 281 struct deferred_lines *iter_old, *iter_new, *tmp;