summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--Makefile20
-rw-r--r--cgit.c78
-rw-r--r--cgit.css148
-rw-r--r--cgit.h23
-rw-r--r--cgitrc.5.txt58
-rw-r--r--cmd.c48
-rw-r--r--cmd.h3
-rwxr-xr-xfilters/commit-links.sh16
-rwxr-xr-xfilters/syntax-highlighting.sh29
m---------git0
-rw-r--r--html.c70
-rw-r--r--html.h18
-rw-r--r--scan-tree.c71
-rw-r--r--scan-tree.h3
-rw-r--r--shared.c88
-rw-r--r--ui-atom.c4
-rw-r--r--ui-blob.c35
-rw-r--r--ui-blob.h1
-rw-r--r--ui-commit.c46
-rw-r--r--ui-commit.h2
-rw-r--r--ui-diff.c82
-rw-r--r--ui-log.c51
-rw-r--r--ui-patch.c8
-rw-r--r--ui-patch.h2
-rw-r--r--ui-plain.c70
-rw-r--r--ui-refs.c4
-rw-r--r--ui-shared.c270
-rw-r--r--ui-shared.h71
-rw-r--r--ui-snapshot.c14
-rw-r--r--ui-ssdiff.c369
-rw-r--r--ui-ssdiff.h13
-rw-r--r--ui-stats.c8
-rw-r--r--ui-summary.c20
-rw-r--r--ui-tag.c24
-rw-r--r--ui-tree.c23
35 files changed, 1498 insertions, 292 deletions
diff --git a/Makefile b/Makefile
index 5162020..2a15469 100644
--- a/Makefile
+++ b/Makefile
@@ -7,3 +7,3 @@ CACHE_ROOT = /var/cache/cgit
7SHA1_HEADER = <openssl/sha.h> 7SHA1_HEADER = <openssl/sha.h>
8GIT_VER = 1.6.4.3 8GIT_VER = 1.7.2.2
9GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2 9GIT_URL = http://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.bz2
@@ -13,2 +13,5 @@ INSTALL = install
13# 13#
14# Define NO_OPENSSL to disable linking with OpenSSL and use bundled SHA1
15# implementation (slower).
16#
14# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin). 17# Define NEEDS_LIBICONV if linking with libc is not enough (eg. Darwin).
@@ -70,3 +73,3 @@ endif
70 73
71EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lcrypto 74EXTLIBS = git/libgit.a git/xdiff/lib.a -lz -lpthread
72OBJECTS = 75OBJECTS =
@@ -92,2 +95,3 @@ OBJECTS += ui-shared.o
92OBJECTS += ui-snapshot.o 95OBJECTS += ui-snapshot.o
96OBJECTS += ui-ssdiff.o
93OBJECTS += ui-stats.o 97OBJECTS += ui-stats.o
@@ -125,2 +129,8 @@ ifdef NO_STRCASESTR
125endif 129endif
130ifdef NO_OPENSSL
131 CFLAGS += -DNO_OPENSSL
132 GIT_OPTIONS += NO_OPENSSL=1
133else
134 EXTLIBS += -lcrypto
135endif
126 136
@@ -131,7 +141,9 @@ cgit.o: VERSION
131 141
142ifneq "$(MAKECMDGOALS)" "clean"
132-include $(OBJECTS:.o=.d) 143-include $(OBJECTS:.o=.d)
144endif
133 145
134libgit: 146libgit:
135 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 libgit.a 147 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) libgit.a
136 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 xdiff/lib.a 148 $(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) NO_CURL=1 $(GIT_OPTIONS) xdiff/lib.a
137 149
diff --git a/cgit.c b/cgit.c
index 6c7e811..d6146e2 100644
--- a/cgit.c
+++ b/cgit.c
@@ -3,2 +3,3 @@
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
4 * 5 *
@@ -23,3 +24,3 @@ void add_mimetype(const char *name, const char *value)
23 24
24 item = string_list_insert(xstrdup(name), &ctx.cfg.mimetypes); 25 item = string_list_insert(&ctx.cfg.mimetypes, xstrdup(name));
25 item->util = xstrdup(value); 26 item->util = xstrdup(value);
@@ -62,2 +63,6 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 63 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
64 else if (!strcmp(name, "enable-remote-branches"))
65 repo->enable_remote_branches = atoi(value);
66 else if (!strcmp(name, "enable-subject-links"))
67 repo->enable_subject_links = atoi(value);
63 else if (!strcmp(name, "max-stats")) 68 else if (!strcmp(name, "max-stats"))
@@ -69,3 +74,4 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
69 else if (!strcmp(name, "readme") && value != NULL) { 74 else if (!strcmp(name, "readme") && value != NULL) {
70 if (*value == '/') 75 char *colon;
76 if (*value == '/' || ((colon = strchr(value, ':')) != NULL && colon != value && *(colon + 1) != '\0'))
71 repo->readme = xstrdup(value); 77 repo->readme = xstrdup(value);
@@ -133,2 +139,4 @@ void config_cb(const char *name, const char *value)
133 ctx.cfg.enable_filter_overrides = atoi(value); 139 ctx.cfg.enable_filter_overrides = atoi(value);
140 else if (!strcmp(name, "enable-gitweb-owner"))
141 ctx.cfg.enable_gitweb_owner = atoi(value);
134 else if (!strcmp(name, "enable-index-links")) 142 else if (!strcmp(name, "enable-index-links"))
@@ -139,2 +147,6 @@ void config_cb(const char *name, const char *value)
139 ctx.cfg.enable_log_linecount = atoi(value); 147 ctx.cfg.enable_log_linecount = atoi(value);
148 else if (!strcmp(name, "enable-remote-branches"))
149 ctx.cfg.enable_remote_branches = atoi(value);
150 else if (!strcmp(name, "enable-subject-links"))
151 ctx.cfg.enable_subject_links = atoi(value);
140 else if (!strcmp(name, "enable-tree-linenumbers")) 152 else if (!strcmp(name, "enable-tree-linenumbers"))
@@ -146,3 +158,3 @@ void config_cb(const char *name, const char *value)
146 else if (!strcmp(name, "cache-root")) 158 else if (!strcmp(name, "cache-root"))
147 ctx.cfg.cache_root = xstrdup(value); 159 ctx.cfg.cache_root = xstrdup(expand_macros(value));
148 else if (!strcmp(name, "cache-root-ttl")) 160 else if (!strcmp(name, "cache-root-ttl"))
@@ -163,2 +175,4 @@ void config_cb(const char *name, const char *value)
163 ctx.cfg.embedded = atoi(value); 175 ctx.cfg.embedded = atoi(value);
176 else if (!strcmp(name, "max-atom-items"))
177 ctx.cfg.max_atom_items = atoi(value);
164 else if (!strcmp(name, "max-message-length")) 178 else if (!strcmp(name, "max-message-length"))
@@ -167,2 +181,4 @@ void config_cb(const char *name, const char *value)
167 ctx.cfg.max_repodesc_len = atoi(value); 181 ctx.cfg.max_repodesc_len = atoi(value);
182 else if (!strcmp(name, "max-blob-size"))
183 ctx.cfg.max_blob_size = atoi(value);
168 else if (!strcmp(name, "max-repo-count")) 184 else if (!strcmp(name, "max-repo-count"))
@@ -171,7 +187,12 @@ void config_cb(const char *name, const char *value)
171 ctx.cfg.max_commit_count = atoi(value); 187 ctx.cfg.max_commit_count = atoi(value);
188 else if (!strcmp(name, "project-list"))
189 ctx.cfg.project_list = xstrdup(expand_macros(value));
172 else if (!strcmp(name, "scan-path")) 190 else if (!strcmp(name, "scan-path"))
173 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 191 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
174 process_cached_repolist(value); 192 process_cached_repolist(expand_macros(value));
193 else if (ctx.cfg.project_list)
194 scan_projects(expand_macros(value),
195 ctx.cfg.project_list, repo_config);
175 else 196 else
176 scan_tree(value, repo_config); 197 scan_tree(expand_macros(value), repo_config);
177 else if (!strcmp(name, "source-filter")) 198 else if (!strcmp(name, "source-filter"))
@@ -184,2 +205,4 @@ void config_cb(const char *name, const char *value)
184 ctx.cfg.summary_tags = atoi(value); 205 ctx.cfg.summary_tags = atoi(value);
206 else if (!strcmp(name, "side-by-side-diffs"))
207 ctx.cfg.ssdiff = atoi(value);
185 else if (!strcmp(name, "agefile")) 208 else if (!strcmp(name, "agefile"))
@@ -188,2 +211,4 @@ void config_cb(const char *name, const char *value)
188 ctx.cfg.renamelimit = atoi(value); 211 ctx.cfg.renamelimit = atoi(value);
212 else if (!strcmp(name, "remove-suffix"))
213 ctx.cfg.remove_suffix = atoi(value);
189 else if (!strcmp(name, "robots")) 214 else if (!strcmp(name, "robots"))
@@ -197,3 +222,3 @@ void config_cb(const char *name, const char *value)
197 else if (!strcmp(name, "include")) 222 else if (!strcmp(name, "include"))
198 parse_configfile(value, config_cb); 223 parse_configfile(expand_macros(value), config_cb);
199} 224}
@@ -211,2 +236,4 @@ static void querystring_cb(const char *name, const char *value)
211 } else if (!strcmp(name, "url")) { 236 } else if (!strcmp(name, "url")) {
237 if (*value == '/')
238 value++;
212 ctx.qry.url = xstrdup(value); 239 ctx.qry.url = xstrdup(value);
@@ -240,2 +267,10 @@ static void querystring_cb(const char *name, const char *value)
240 ctx.qry.period = xstrdup(value); 267 ctx.qry.period = xstrdup(value);
268 } else if (!strcmp(name, "ss")) {
269 ctx.qry.ssdiff = atoi(value);
270 } else if (!strcmp(name, "all")) {
271 ctx.qry.show_all = atoi(value);
272 } else if (!strcmp(name, "context")) {
273 ctx.qry.context = atoi(value);
274 } else if (!strcmp(name, "ignorews")) {
275 ctx.qry.ignorews = atoi(value);
241 } 276 }
@@ -264,2 +299,3 @@ static void prepare_context(struct cgit_context *ctx)
264 ctx->cfg.local_time = 0; 299 ctx->cfg.local_time = 0;
300 ctx->cfg.enable_gitweb_owner = 1;
265 ctx->cfg.enable_tree_linenumbers = 1; 301 ctx->cfg.enable_tree_linenumbers = 1;
@@ -270,5 +306,8 @@ static void prepare_context(struct cgit_context *ctx)
270 ctx->cfg.max_repodesc_len = 80; 306 ctx->cfg.max_repodesc_len = 80;
307 ctx->cfg.max_blob_size = 0;
271 ctx->cfg.max_stats = 0; 308 ctx->cfg.max_stats = 0;
272 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 309 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
310 ctx->cfg.project_list = NULL;
273 ctx->cfg.renamelimit = -1; 311 ctx->cfg.renamelimit = -1;
312 ctx->cfg.remove_suffix = 0;
274 ctx->cfg.robots = "index, nofollow"; 313 ctx->cfg.robots = "index, nofollow";
@@ -281,2 +320,4 @@ static void prepare_context(struct cgit_context *ctx)
281 ctx->cfg.summary_tags = 10; 320 ctx->cfg.summary_tags = 10;
321 ctx->cfg.max_atom_items = 10;
322 ctx->cfg.ssdiff = 0;
282 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 323 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
@@ -412,2 +453,8 @@ static void process_request(void *cbdata)
412 453
454 /* If cmd->want_vpath is set, assume ctx->qry.path contains a "virtual"
455 * in-project path limit to be made available at ctx->qry.vpath.
456 * Otherwise, no path limit is in effect (ctx->qry.vpath = NULL).
457 */
458 ctx->qry.vpath = cmd->want_vpath ? ctx->qry.path : NULL;
459
413 if (cmd->want_repo && !ctx->repo) { 460 if (cmd->want_repo && !ctx->repo) {
@@ -543,2 +590,5 @@ static int generate_cached_repolist(const char *path, const char *cached_rc)
543 idx = cgit_repolist.count; 590 idx = cgit_repolist.count;
591 if (ctx.cfg.project_list)
592 scan_projects(path, ctx.cfg.project_list, repo_config);
593 else
544 scan_tree(path, repo_config); 594 scan_tree(path, repo_config);
@@ -557,5 +607,8 @@ static void process_cached_repolist(const char *path)
557 time_t age; 607 time_t age;
608 unsigned long hash;
558 609
559 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, 610 hash = hash_str(path);
560 hash_str(path))); 611 if (ctx.cfg.project_list)
612 hash += hash_str(ctx.cfg.project_list);
613 cached_rc = xstrdup(fmt("%s/rc-%8x", ctx.cfg.cache_root, hash));
561 614
@@ -566,4 +619,9 @@ static void process_cached_repolist(const char *path)
566 */ 619 */
567 if (generate_cached_repolist(path, cached_rc)) 620 if (generate_cached_repolist(path, cached_rc)) {
621 if (ctx.cfg.project_list)
622 scan_projects(path, ctx.cfg.project_list,
623 repo_config);
624 else
568 scan_tree(path, repo_config); 625 scan_tree(path, repo_config);
626 }
569 return; 627 return;
@@ -676,3 +734,3 @@ int main(int argc, const char **argv)
676 cgit_parse_args(argc, argv); 734 cgit_parse_args(argc, argv);
677 parse_configfile(ctx.env.cgit_config, config_cb); 735 parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
678 ctx.repo = NULL; 736 ctx.repo = NULL;
diff --git a/cgit.css b/cgit.css
index c47ebc9..0c88b65 100644
--- a/cgit.css
+++ b/cgit.css
@@ -66,3 +66,3 @@ table#header td.sub {
66table.tabs { 66table.tabs {
67 /* border-bottom: solid 2px #ccc; */ 67 border-bottom: solid 3px #ccc;
68 border-collapse: collapse; 68 border-collapse: collapse;
@@ -104,2 +104,9 @@ table.tabs td.form select {
104 104
105div.path {
106 margin: 0px;
107 padding: 5px 2em 2px 2em;
108 color: #000;
109 background-color: #eee;
110}
111
105div.content { 112div.content {
@@ -107,3 +114,2 @@ div.content {
107 padding: 2em; 114 padding: 2em;
108 border-top: solid 3px #ccc;
109 border-bottom: solid 3px #ccc; 115 border-bottom: solid 3px #ccc;
@@ -160,2 +166,13 @@ table.list td.logmsg {
160 166
167table.list td.lognotes-label {
168 text-align:right;
169 vertical-align:top;
170}
171
172table.list td.lognotes {
173 font-family: monospace;
174 white-space: pre;
175 padding: 0em 0.5em 2em 0.5em;
176}
177
161table.list td a { 178table.list td a {
@@ -164,2 +181,7 @@ table.list td a {
164 181
182table.list td a.ls-dir {
183 font-weight: bold;
184 color: #00f;
185}
186
165table.list td a:hover { 187table.list td a:hover {
@@ -317,2 +339,20 @@ div.commit-msg {
317 339
340div.notes-header {
341 font-weight: bold;
342 padding-top: 1.5em;
343}
344
345div.notes {
346 white-space: pre;
347 font-family: monospace;
348 border: solid 1px #ee9;
349 background-color: #ffd;
350 padding: 0.3em 2em 0.3em 1em;
351 float: left;
352}
353
354div.notes-footer {
355 clear: left;
356}
357
318div.diffstat-header { 358div.diffstat-header {
@@ -522,3 +562,6 @@ a.deco {
522 562
523div.commit-subject a { 563div.commit-subject a.branch-deco,
564div.commit-subject a.tag-deco,
565div.commit-subject a.remote-deco,
566div.commit-subject a.deco {
524 margin-left: 1em; 567 margin-left: 1em;
@@ -603 +646,100 @@ table.hgraph div.bar {
603} 646}
647
648table.ssdiff {
649 width: 100%;
650}
651
652table.ssdiff td {
653 font-size: 75%;
654 font-family: monospace;
655 white-space: pre;
656 padding: 1px 4px 1px 4px;
657 border-left: solid 1px #aaa;
658 border-right: solid 1px #aaa;
659}
660
661table.ssdiff td.add {
662 color: black;
663 background: #cfc;
664 min-width: 50%;
665}
666
667table.ssdiff td.add_dark {
668 color: black;
669 background: #aca;
670 min-width: 50%;
671}
672
673table.ssdiff span.add {
674 background: #cfc;
675 font-weight: bold;
676}
677
678table.ssdiff td.del {
679 color: black;
680 background: #fcc;
681 min-width: 50%;
682}
683
684table.ssdiff td.del_dark {
685 color: black;
686 background: #caa;
687 min-width: 50%;
688}
689
690table.ssdiff span.del {
691 background: #fcc;
692 font-weight: bold;
693}
694
695table.ssdiff td.changed {
696 color: black;
697 background: #ffc;
698 min-width: 50%;
699}
700
701table.ssdiff td.changed_dark {
702 color: black;
703 background: #cca;
704 min-width: 50%;
705}
706
707table.ssdiff td.lineno {
708 color: black;
709 background: #eee;
710 text-align: right;
711 width: 3em;
712 min-width: 3em;
713}
714
715table.ssdiff td.hunk {
716 color: #black;
717 background: #ccf;
718 border-top: solid 1px #aaa;
719 border-bottom: solid 1px #aaa;
720}
721
722table.ssdiff td.head {
723 border-top: solid 1px #aaa;
724 border-bottom: solid 1px #aaa;
725}
726
727table.ssdiff td.head div.head {
728 font-weight: bold;
729 color: black;
730}
731
732table.ssdiff td.foot {
733 border-top: solid 1px #aaa;
734 border-left: none;
735 border-right: none;
736 border-bottom: none;
737}
738
739table.ssdiff td.space {
740 border: none;
741}
742
743table.ssdiff td.space div {
744 min-height: 3em;
745} \ No newline at end of file
diff --git a/cgit.h b/cgit.h
index 6c6c460..4090cd4 100644
--- a/cgit.h
+++ b/cgit.h
@@ -21,2 +21,3 @@
21#include <utf8.h> 21#include <utf8.h>
22#include <notes.h>
22 23
@@ -74,2 +75,4 @@ struct cgit_repo {
74 int enable_log_linecount; 75 int enable_log_linecount;
76 int enable_remote_branches;
77 int enable_subject_links;
75 int max_stats; 78 int max_stats;
@@ -145,2 +148,7 @@ struct cgit_query {
145 int showmsg; 148 int showmsg;
149 int ssdiff;
150 int show_all;
151 int context;
152 int ignorews;
153 char *vpath;
146}; 154};
@@ -161,2 +169,3 @@ struct cgit_config {
161 char *module_link; 169 char *module_link;
170 char *project_list;
162 char *robots; 171 char *robots;
@@ -177,2 +186,3 @@ struct cgit_config {
177 int enable_filter_overrides; 186 int enable_filter_overrides;
187 int enable_gitweb_owner;
178 int enable_index_links; 188 int enable_index_links;
@@ -180,4 +190,7 @@ struct cgit_config {
180 int enable_log_linecount; 190 int enable_log_linecount;
191 int enable_remote_branches;
192 int enable_subject_links;
181 int enable_tree_linenumbers; 193 int enable_tree_linenumbers;
182 int local_time; 194 int local_time;
195 int max_atom_items;
183 int max_repo_count; 196 int max_repo_count;
@@ -187,2 +200,3 @@ struct cgit_config {
187 int max_repodesc_len; 200 int max_repodesc_len;
201 int max_blob_size;
188 int max_stats; 202 int max_stats;
@@ -192,2 +206,3 @@ struct cgit_config {
192 int renamelimit; 206 int renamelimit;
207 int remove_suffix;
193 int snapshots; 208 int snapshots;
@@ -196,2 +211,3 @@ struct cgit_config {
196 int summary_tags; 211 int summary_tags;
212 int ssdiff;
197 struct string_list mimetypes; 213 struct string_list mimetypes;
@@ -270,3 +286,4 @@ extern int cgit_diff_files(const unsigned char *old_sha1,
270 unsigned long *old_size, unsigned long *new_size, 286 unsigned long *old_size, unsigned long *new_size,
271 int *binary, linediff_fn fn); 287 int *binary, int context, int ignorews,
288 linediff_fn fn);
272 289
@@ -274,3 +291,3 @@ extern void cgit_diff_tree(const unsigned char *old_sha1,
274 const unsigned char *new_sha1, 291 const unsigned char *new_sha1,
275 filepair_fn fn, const char *prefix); 292 filepair_fn fn, const char *prefix, int ignorews);
276 293
@@ -293,2 +310,4 @@ extern int readfile(const char *path, char **buf, size_t *size);
293 310
311extern char *expand_macros(const char *txt);
312
294#endif /* CGIT_H */ 313#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 0c13485..c643fae 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -97,2 +97,7 @@ enable-filter-overrides::
97 97
98enable-gitweb-owner::
99 If set to "1" and scan-path is enabled, we first check each repository
100 for the git config value "gitweb.owner" to determine the owner.
101 Default value: "1". See also: scan-path.
102
98enable-index-links:: 103enable-index-links::
@@ -112,2 +117,13 @@ enable-log-linecount::
112 117
118enable-remote-branches::
119 Flag which, when set to "1", will make cgit display remote branches
120 in the summary and refs views. Default value: "0". See also:
121 "repo.enable-remote-branches".
122
123enable-subject-links::
124 Flag which, when set to "1", will make cgit use the subject of the
125 parent commit as link text when generating links to parent commits
126 in commit view. Default value: "0". See also:
127 "repo.enable-subject-links".
128
113enable-tree-linenumbers:: 129enable-tree-linenumbers::
@@ -163,2 +179,6 @@ logo-link::
163 179
180max-atom-items::
181 Specifies the number of items to display in atom feeds view. Default
182 value: "10".
183
164max-commit-count:: 184max-commit-count::
@@ -179,2 +199,6 @@ max-repodesc-length::
179 199
200max-blob-size::
201 Specifies the maximum size of a blob to display HTML for in KBytes.
202 Default value: "0" (limit disabled).
203
180max-stats:: 204max-stats::
@@ -207,2 +231,12 @@ noheader::
207 231
232project-list::
233 A list of subdirectories inside of scan-path, relative to it, that
234 should loaded as git repositories. This must be defined prior to
235 scan-path. Default value: none. See also: scan-path.
236
237remove-suffix::
238 If set to "1" and scan-path is enabled, if any repositories are found
239 with a suffix of ".git", this suffix will be removed for the url and
240 name. Default value: "0". See also: scan-path.
241
208renamelimit:: 242renamelimit::
@@ -236,3 +270,6 @@ scan-path::
236 the result will be cached as a cgitrc include-file in the cache 270 the result will be cached as a cgitrc include-file in the cache
237 directory. Default value: none. See also: cache-scanrc-ttl. 271 directory. If project-list has been defined prior to scan-path,
272 scan-path loads only the directories listed in the file pointed to by
273 project-list. Default value: none. See also: cache-scanrc-ttl,
274 project-list.
238 275
@@ -243,2 +280,6 @@ section::
243 280
281side-by-side-diffs::
282 If set to "1" shows side-by-side diffs instead of unidiffs per
283 default. Default value: "0".
284
244snapshots:: 285snapshots::
@@ -306,2 +347,10 @@ repo.enable-log-linecount::
306 347
348repo.enable-remote-branches::
349 Flag which, when set to "1", will make cgit display remote branches
350 in the summary and refs views. Default value: <enable-remote-branches>.
351
352repo.enable-subject-links::
353 A flag which can be used to override the global setting
354 `enable-subject-links'. Default value: none.
355
307repo.max-stats:: 356repo.max-stats::
@@ -324,3 +373,5 @@ repo.readme::
324 A path (relative to <repo.path>) which specifies a file to include 373 A path (relative to <repo.path>) which specifies a file to include
325 verbatim as the "About" page for this repo. Default value: none. 374 verbatim as the "About" page for this repo. You may also specify a
375 git refspec by head or by hash by prepending the refspec followed by
376 a colon. For example, "master:docs/readme.mkd" Default value: none.
326 377
@@ -415,3 +466,3 @@ snapshots=tar.gz tar.bz2 zip
415 466
416mimetype.git=image/git 467mimetype.gif=image/gif
417mimetype.html=text/html 468mimetype.html=text/html
@@ -501 +552,2 @@ AUTHOR
501Lars Hjemli <hjemli@gmail.com> 552Lars Hjemli <hjemli@gmail.com>
553Jason A. Donenfeld <Jason@zx2c4.com>
diff --git a/cmd.c b/cmd.c
index 766f903..6dc9f5e 100644
--- a/cmd.c
+++ b/cmd.c
@@ -35,3 +35,3 @@ static void atom_fn(struct cgit_context *ctx)
35{ 35{
36 cgit_print_atom(ctx->qry.head, ctx->qry.path, 10); 36 cgit_print_atom(ctx->qry.head, ctx->qry.path, ctx->cfg.max_atom_items);
37} 37}
@@ -53,3 +53,3 @@ static void commit_fn(struct cgit_context *ctx)
53{ 53{
54 cgit_print_commit(ctx->qry.sha1); 54 cgit_print_commit(ctx->qry.sha1, ctx->qry.path);
55} 55}
@@ -92,3 +92,3 @@ static void patch_fn(struct cgit_context *ctx)
92{ 92{
93 cgit_print_patch(ctx->qry.sha1); 93 cgit_print_patch(ctx->qry.sha1, ctx->qry.path);
94} 94}
@@ -131,4 +131,4 @@ static void tree_fn(struct cgit_context *ctx)
131 131
132#define def_cmd(name, want_repo, want_layout) \ 132#define def_cmd(name, want_repo, want_layout, want_vpath) \
133 {#name, name##_fn, want_repo, want_layout} 133 {#name, name##_fn, want_repo, want_layout, want_vpath}
134 134
@@ -137,21 +137,21 @@ struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
137 static struct cgit_cmd cmds[] = { 137 static struct cgit_cmd cmds[] = {
138 def_cmd(HEAD, 1, 0), 138 def_cmd(HEAD, 1, 0, 0),
139 def_cmd(atom, 1, 0), 139 def_cmd(atom, 1, 0, 0),
140 def_cmd(about, 0, 1), 140 def_cmd(about, 0, 1, 0),
141 def_cmd(blob, 1, 0), 141 def_cmd(blob, 1, 0, 0),
142 def_cmd(commit, 1, 1), 142 def_cmd(commit, 1, 1, 1),
143 def_cmd(diff, 1, 1), 143 def_cmd(diff, 1, 1, 1),
144 def_cmd(info, 1, 0), 144 def_cmd(info, 1, 0, 0),
145 def_cmd(log, 1, 1), 145 def_cmd(log, 1, 1, 1),
146 def_cmd(ls_cache, 0, 0), 146 def_cmd(ls_cache, 0, 0, 0),
147 def_cmd(objects, 1, 0), 147 def_cmd(objects, 1, 0, 0),
148 def_cmd(patch, 1, 0), 148 def_cmd(patch, 1, 0, 1),
149 def_cmd(plain, 1, 0), 149 def_cmd(plain, 1, 0, 0),
150 def_cmd(refs, 1, 1), 150 def_cmd(refs, 1, 1, 0),
151 def_cmd(repolist, 0, 0), 151 def_cmd(repolist, 0, 0, 0),
152 def_cmd(snapshot, 1, 0), 152 def_cmd(snapshot, 1, 0, 0),
153 def_cmd(stats, 1, 1), 153 def_cmd(stats, 1, 1, 1),
154 def_cmd(summary, 1, 1), 154 def_cmd(summary, 1, 1, 0),
155 def_cmd(tag, 1, 1), 155 def_cmd(tag, 1, 1, 0),
156 def_cmd(tree, 1, 1), 156 def_cmd(tree, 1, 1, 1),
157 }; 157 };
diff --git a/cmd.h b/cmd.h
index ec9e691..8dc01bd 100644
--- a/cmd.h
+++ b/cmd.h
@@ -9,3 +9,4 @@ struct cgit_cmd {
9 unsigned int want_repo:1, 9 unsigned int want_repo:1,
10 want_layout:1; 10 want_layout:1,
11 want_vpath:1;
11}; 12};
diff --git a/filters/commit-links.sh b/filters/commit-links.sh
index 165a533..110c609 100755
--- a/filters/commit-links.sh
+++ b/filters/commit-links.sh
@@ -1,5 +1,3 @@
1#!/bin/sh 1#!/bin/sh
2# This script can be used to generate links in commit messages - the first 2# This script can be used to generate links in commit messages.
3# sed expression generates links to commits referenced by their SHA1, while
4# the second expression generates links to a fictional bugtracker.
5# 3#
@@ -8,5 +6,9 @@
8 6
9sed -re ' 7# This expression generates links to commits referenced by their SHA1.
10s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g 8regex=$regex'
11s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g 9s|\b([0-9a-fA-F]{8,40})\b|<a href="./?id=\1">\1</a>|g'
12' 10# This expression generates links to a fictional bugtracker.
11regex=$regex'
12s| #([0-9]+)\b|<a href="http://bugs.example.com/?bug=\1">#\1</a>|g'
13
14sed -re "$regex"
diff --git a/filters/syntax-highlighting.sh b/filters/syntax-highlighting.sh
index 999ad0c..6b1c576 100755
--- a/filters/syntax-highlighting.sh
+++ b/filters/syntax-highlighting.sh
@@ -5,2 +5,6 @@
5# 5#
6# This script requires a shell supporting the ${var##pattern} syntax.
7# It is supported by at least dash and bash, however busybox environments
8# might have to use an external call to sed instead.
9#
6# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax 10# Note: the highlight command (http://www.andre-simon.de/) uses css for syntax
@@ -22,18 +26,9 @@
22 26
23case "$1" in 27# store filename and extension in local vars
24 *.c) 28BASENAME="$1"
25 highlight -f -I -X -S c 29EXTENSION="${BASENAME##*.}"
26 ;; 30
27 *.h) 31# map Makefile and Makefile.* to .mk
28 highlight -f -I -X -S c 32[ "${BASENAME%%.*}" == "Makefile" ] && EXTENSION=mk
29 ;; 33
30 *.sh) 34exec highlight --force -f -I -X -S $EXTENSION 2>/dev/null
31 highlight -f -I -X -S sh
32 ;;
33 *.css)
34 highlight -f -I -X -S css
35 ;;
36 *)
37 highlight -f -I -X -S txt
38 ;;
39esac
diff --git a/git b/git
Subproject 7fb6bcff2dece2ff9fbc5ebfe526d9b2a7e764c Subproject 8c67c392e1620fc3b749aa9e0b8da13bd84226f
diff --git a/html.c b/html.c
index d86b2c1..eaabf72 100644
--- a/html.c
+++ b/html.c
@@ -15,2 +15,28 @@
15 15
16/* Percent-encoding of each character, except: a-zA-Z0-9!$()*,./:;@- */
17static const char* url_escape_table[256] = {
18 "%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09",
19 "%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13",
20 "%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d",
21 "%1e", "%1f", "%20", 0, "%22", "%23", 0, "%25", "%26", "%27", 0, 0, 0,
22 "%2b", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%3c", "%3d",
23 "%3e", "%3f", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
24 0, 0, 0, 0, 0, 0, 0, 0, 0, "%5c", 0, "%5e", 0, "%60", 0, 0, 0, 0, 0,
25 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%7b",
26 "%7c", "%7d", 0, "%7f", "%80", "%81", "%82", "%83", "%84", "%85",
27 "%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
28 "%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99",
29 "%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%a0", "%a1", "%a2", "%a3",
30 "%a4", "%a5", "%a6", "%a7", "%a8", "%a9", "%aa", "%ab", "%ac", "%ad",
31 "%ae", "%af", "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
32 "%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", "%c0", "%c1",
33 "%c2", "%c3", "%c4", "%c5", "%c6", "%c7", "%c8", "%c9", "%ca", "%cb",
34 "%cc", "%cd", "%ce", "%cf", "%d0", "%d1", "%d2", "%d3", "%d4", "%d5",
35 "%d6", "%d7", "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
36 "%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", "%e8", "%e9",
37 "%ea", "%eb", "%ec", "%ed", "%ee", "%ef", "%f0", "%f1", "%f2", "%f3",
38 "%f4", "%f5", "%f6", "%f7", "%f8", "%f9", "%fa", "%fb", "%fc", "%fd",
39 "%fe", "%ff"
40};
41
16int htmlfd = STDOUT_FILENO; 42int htmlfd = STDOUT_FILENO;
@@ -65,5 +91,5 @@ void html_status(int code, const char *msg, int more_headers)
65 91
66void html_txt(char *txt) 92void html_txt(const char *txt)
67{ 93{
68 char *t = txt; 94 const char *t = txt;
69 while(t && *t){ 95 while(t && *t){
@@ -86,5 +112,5 @@ void html_txt(char *txt)
86 112
87void html_ntxt(int len, char *txt) 113void html_ntxt(int len, const char *txt)
88{ 114{
89 char *t = txt; 115 const char *t = txt;
90 while(t && *t && len--){ 116 while(t && *t && len--){
@@ -109,5 +135,5 @@ void html_ntxt(int len, char *txt)
109 135
110void html_attr(char *txt) 136void html_attr(const char *txt)
111{ 137{
112 char *t = txt; 138 const char *t = txt;
113 while(t && *t){ 139 while(t && *t){
@@ -132,10 +158,11 @@ void html_attr(char *txt)
132 158
133void html_url_path(char *txt) 159void html_url_path(const char *txt)
134{ 160{
135 char *t = txt; 161 const char *t = txt;
136 while(t && *t){ 162 while(t && *t){
137 int c = *t; 163 int c = *t;
138 if (c=='"' || c=='#' || c=='\'' || c=='?') { 164 const char *e = url_escape_table[c];
165 if (e && c!='+' && c!='&' && c!='+') {
139 write(htmlfd, txt, t - txt); 166 write(htmlfd, txt, t - txt);
140 write(htmlfd, fmt("%%%2x", c), 3); 167 write(htmlfd, e, 3);
141 txt = t+1; 168 txt = t+1;
@@ -148,10 +175,11 @@ void html_url_path(char *txt)
148 175
149void html_url_arg(char *txt) 176void html_url_arg(const char *txt)
150{ 177{
151 char *t = txt; 178 const char *t = txt;
152 while(t && *t){ 179 while(t && *t){
153 int c = *t; 180 int c = *t;
154 if (c=='"' || c=='#' || c=='%' || c=='&' || c=='\'' || c=='+' || c=='?') { 181 const char *e = url_escape_table[c];
182 if (e) {
155 write(htmlfd, txt, t - txt); 183 write(htmlfd, txt, t - txt);
156 write(htmlfd, fmt("%%%2x", c), 3); 184 write(htmlfd, e, 3);
157 txt = t+1; 185 txt = t+1;
@@ -164,3 +192,3 @@ void html_url_arg(char *txt)
164 192
165void html_hidden(char *name, char *value) 193void html_hidden(const char *name, const char *value)
166{ 194{
@@ -173,3 +201,3 @@ void html_hidden(char *name, char *value)
173 201
174void html_option(char *value, char *text, char *selected_value) 202void html_option(const char *value, const char *text, const char *selected_value)
175{ 203{
@@ -185,3 +213,3 @@ void html_option(char *value, char *text, char *selected_value)
185 213
186void html_link_open(char *url, char *title, char *class) 214void html_link_open(const char *url, const char *title, const char *class)
187{ 215{
@@ -260,10 +288,10 @@ char *convert_query_hexchar(char *txt)
260 288
261int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value)) 289int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value))
262{ 290{
263 char *t, *value = NULL, c; 291 char *t, *txt, *value = NULL, c;
264 292
265 if (!txt) 293 if (!txt_)
266 return 0; 294 return 0;
267 295
268 t = txt = strdup(txt); 296 t = txt = strdup(txt_);
269 if (t == NULL) { 297 if (t == NULL) {
diff --git a/html.h b/html.h
index a55d4b2..16d55ec 100644
--- a/html.h
+++ b/html.h
@@ -9,10 +9,10 @@ extern void htmlf(const char *format,...);
9extern void html_status(int code, const char *msg, int more_headers); 9extern void html_status(int code, const char *msg, int more_headers);
10extern void html_txt(char *txt); 10extern void html_txt(const char *txt);
11extern void html_ntxt(int len, char *txt); 11extern void html_ntxt(int len, const char *txt);
12extern void html_attr(char *txt); 12extern void html_attr(const char *txt);
13extern void html_url_path(char *txt); 13extern void html_url_path(const char *txt);
14extern void html_url_arg(char *txt); 14extern void html_url_arg(const char *txt);
15extern void html_hidden(char *name, char *value); 15extern void html_hidden(const char *name, const char *value);
16extern void html_option(char *value, char *text, char *selected_value); 16extern void html_option(const char *value, const char *text, const char *selected_value);
17extern void html_link_open(char *url, char *title, char *class); 17extern void html_link_open(const char *url, const char *title, const char *class);
18extern void html_link_close(void); 18extern void html_link_close(void);
@@ -21,3 +21,3 @@ extern int html_include(const char *filename);
21 21
22extern int http_parse_querystring(char *txt, void (*fn)(const char *name, const char *value)); 22extern int http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value));
23 23
diff --git a/scan-tree.c b/scan-tree.c
index dbca797..e987824 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -1 +1,10 @@
1/* scan-tree.c
2 *
3 * Copyright (C) 2008-2009 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
5 *
6 * Licensed under GNU General Public License v2
7 * (see COPYING for full license text)
8 */
9
1#include "cgit.h" 10#include "cgit.h"
@@ -40,2 +49,3 @@ struct cgit_repo *repo;
40repo_config_fn config_fn; 49repo_config_fn config_fn;
50char *owner;
41 51
@@ -46,2 +56,9 @@ static void repo_config(const char *name, const char *value)
46 56
57static int git_owner_config(const char *key, const char *value, void *cb)
58{
59 if (!strcmp(key, "gitweb.owner"))
60 owner = xstrdup(value);
61 return 0;
62}
63
47static void add_repo(const char *base, const char *path, repo_config_fn fn) 64static void add_repo(const char *base, const char *path, repo_config_fn fn)
@@ -58,7 +75,8 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
58 } 75 }
59 if ((pwd = getpwuid(st.st_uid)) == NULL) { 76 if (!stat(fmt("%s/noweb", path), &st))
60 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
61 path, strerror(errno), errno);
62 return; 77 return;
63 } 78
79 owner = NULL;
80 if (ctx.cfg.enable_gitweb_owner)
81 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
64 if (base == path) 82 if (base == path)
@@ -72,8 +90,19 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
72 repo = cgit_add_repo(xstrdup(p)); 90 repo = cgit_add_repo(xstrdup(p));
91 if (ctx.cfg.remove_suffix)
92 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
93 *p = '\0';
73 repo->name = repo->url; 94 repo->name = repo->url;
74 repo->path = xstrdup(path); 95 repo->path = xstrdup(path);
75 p = (pwd && pwd->pw_gecos) ? strchr(pwd->pw_gecos, ',') : NULL; 96 while (!owner) {
76 if (p) 97 if ((pwd = getpwuid(st.st_uid)) == NULL) {
98 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
99 path, strerror(errno), errno);
100 break;
101 }
102 if (pwd->pw_gecos)
103 if ((p = strchr(pwd->pw_gecos, ',')))
77 *p = '\0'; 104 *p = '\0';
78 repo->owner = (pwd ? xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name) : ""); 105 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
106 }
107 repo->owner = owner;
79 108
@@ -142,2 +171,30 @@ static void scan_path(const char *base, const char *path, repo_config_fn fn)
142 171
172#define lastc(s) s[strlen(s) - 1]
173
174void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
175{
176 char line[MAX_PATH * 2], *z;
177 FILE *projects;
178 int err;
179
180 projects = fopen(projectsfile, "r");
181 if (!projects) {
182 fprintf(stderr, "Error opening projectsfile %s: %s (%d)\n",
183 projectsfile, strerror(errno), errno);
184 }
185 while (fgets(line, sizeof(line), projects) != NULL) {
186 for (z = &lastc(line);
187 strlen(line) && strchr("\n\r", *z);
188 z = &lastc(line))
189 *z = '\0';
190 if (strlen(line))
191 scan_path(path, fmt("%s/%s", path, line), fn);
192 }
193 if ((err = ferror(projects))) {
194 fprintf(stderr, "Error reading from projectsfile %s: %s (%d)\n",
195 projectsfile, strerror(err), err);
196 }
197 fclose(projects);
198}
199
143void scan_tree(const char *path, repo_config_fn fn) 200void scan_tree(const char *path, repo_config_fn fn)
diff --git a/scan-tree.h b/scan-tree.h
index 11539f4..1afbd4b 100644
--- a/scan-tree.h
+++ b/scan-tree.h
@@ -1,3 +1,2 @@
1 1extern void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn);
2
3extern void scan_tree(const char *path, repo_config_fn fn); 2extern void scan_tree(const char *path, repo_config_fn fn);
diff --git a/shared.c b/shared.c
index 6adf2b6..b42c2a2 100644
--- a/shared.c
+++ b/shared.c
@@ -12,3 +12,2 @@ struct cgit_repolist cgit_repolist;
12struct cgit_context ctx; 12struct cgit_context ctx;
13int cgit_cmd;
14 13
@@ -61,2 +60,4 @@ struct cgit_repo *cgit_add_repo(const char *url)
61 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
62 ret->enable_subject_links = ctx.cfg.enable_subject_links;
62 ret->max_stats = ctx.cfg.max_stats; 63 ret->max_stats = ctx.cfg.max_stats;
@@ -264,3 +265,4 @@ int cgit_diff_files(const unsigned char *old_sha1,
264 const unsigned char *new_sha1, unsigned long *old_size, 265 const unsigned char *new_sha1, unsigned long *old_size,
265 unsigned long *new_size, int *binary, linediff_fn fn) 266 unsigned long *new_size, int *binary, int context,
267 int ignorews, linediff_fn fn)
266{ 268{
@@ -291,3 +293,5 @@ int cgit_diff_files(const unsigned char *old_sha1,
291 diff_params.flags = XDF_NEED_MINIMAL; 293 diff_params.flags = XDF_NEED_MINIMAL;
292 emit_params.ctxlen = 3; 294 if (ignorews)
295 diff_params.flags |= XDF_IGNORE_WHITESPACE;
296 emit_params.ctxlen = context > 0 ? context : 3;
293 emit_params.flags = XDL_EMIT_FUNCNAMES; 297 emit_params.flags = XDL_EMIT_FUNCNAMES;
@@ -305,3 +309,3 @@ void cgit_diff_tree(const unsigned char *old_sha1,
305 const unsigned char *new_sha1, 309 const unsigned char *new_sha1,
306 filepair_fn fn, const char *prefix) 310 filepair_fn fn, const char *prefix, int ignorews)
307{ 311{
@@ -316,2 +320,4 @@ void cgit_diff_tree(const unsigned char *old_sha1,
316 DIFF_OPT_SET(&opt, RECURSIVE); 320 DIFF_OPT_SET(&opt, RECURSIVE);
321 if (ignorews)
322 DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
317 opt.format_callback = cgit_diff_tree_cb; 323 opt.format_callback = cgit_diff_tree_cb;
@@ -340,3 +346,4 @@ void cgit_diff_commit(struct commit *commit, filepair_fn fn)
340 old_sha1 = commit->parents->item->object.sha1; 346 old_sha1 = commit->parents->item->object.sha1;
341 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); 347 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL,
348 ctx.qry.ignorews);
342} 349}
@@ -432 +439,72 @@ int readfile(const char *path, char **buf, size_t *size)
432} 439}
440
441int is_token_char(char c)
442{
443 return isalnum(c) || c == '_';
444}
445
446/* Replace name with getenv(name), return pointer to zero-terminating char
447 */
448char *expand_macro(char *name, int maxlength)
449{
450 char *value;
451 int len;
452
453 len = 0;
454 value = getenv(name);
455 if (value) {
456 len = strlen(value);
457 if (len > maxlength)
458 len = maxlength;
459 strncpy(name, value, len);
460 }
461 return name + len;
462}
463
464#define EXPBUFSIZE (1024 * 8)
465
466/* Replace all tokens prefixed by '$' in the specified text with the
467 * value of the named environment variable.
468 * NB: the return value is a static buffer, i.e. it must be strdup'd
469 * by the caller.
470 */
471char *expand_macros(const char *txt)
472{
473 static char result[EXPBUFSIZE];
474 char *p, *start;
475 int len;
476
477 p = result;
478 start = NULL;
479 while (p < result + EXPBUFSIZE - 1 && txt && *txt) {
480 *p = *txt;
481 if (start) {
482 if (!is_token_char(*txt)) {
483 if (p - start > 0) {
484 *p = '\0';
485 len = result + EXPBUFSIZE - start - 1;
486 p = expand_macro(start, len) - 1;
487 }
488 start = NULL;
489 txt--;
490 }
491 p++;
492 txt++;
493 continue;
494 }
495 if (*txt == '$') {
496 start = p;
497 txt++;
498 continue;
499 }
500 p++;
501 txt++;
502 }
503 *p = '\0';
504 if (start && p - start > 0) {
505 len = result + EXPBUFSIZE - start - 1;
506 p = expand_macro(start, len);
507 *p = '\0';
508 }
509 return result;
510}
diff --git a/ui-atom.c b/ui-atom.c
index 808b2d0..9f049ae 100644
--- a/ui-atom.c
+++ b/ui-atom.c
@@ -87,3 +87,5 @@ void cgit_print_atom(char *tip, char *path, int max_count)
87 87
88 if (!tip) 88 if (ctx.qry.show_all)
89 argv[1] = "--all";
90 else if (!tip)
89 argv[1] = ctx.qry.head; 91 argv[1] = ctx.qry.head;
diff --git a/ui-blob.c b/ui-blob.c
index 89330ce..667a451 100644
--- a/ui-blob.c
+++ b/ui-blob.c
@@ -3,2 +3,3 @@
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
4 * 5 *
@@ -14,2 +15,3 @@ static char *match_path;
14static unsigned char *matched_sha1; 15static unsigned char *matched_sha1;
16static int found_path;
15 17
@@ -21,2 +23,3 @@ static int walk_tree(const unsigned char *sha1, const char *base,int baselen,
21 memmove(matched_sha1,sha1,20); 23 memmove(matched_sha1,sha1,20);
24 found_path = 1;
22 return 0; 25 return 0;
@@ -24,5 +27,35 @@ static int walk_tree(const unsigned char *sha1, const char *base,int baselen,
24 27
25void cgit_print_blob(const char *hex, char *path, const char *head) 28int cgit_print_file(char *path, const char *head)
26{ 29{
30 unsigned char sha1[20];
31 enum object_type type;
32 char *buf;
33 unsigned long size;
34 struct commit *commit;
35 const char *paths[] = {path, NULL};
36 if (get_sha1(head, sha1))
37 return -1;
38 type = sha1_object_info(sha1, &size);
39 if(type == OBJ_COMMIT && path) {
40 commit = lookup_commit_reference(sha1);
41 match_path = path;
42 matched_sha1 = sha1;
43 found_path = 0;
44 read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL);
45 if (!found_path)
46 return -1;
47 type = sha1_object_info(sha1, &size);
48 }
49 if (type == OBJ_BAD)
50 return -1;
51 buf = read_sha1_file(sha1, &type, &size);
52 if (!buf)
53 return -1;
54 buf[size] = '\0';
55 write(htmlfd, buf, size);
56 return 0;
57}
27 58
59void cgit_print_blob(const char *hex, char *path, const char *head)
60{
28 unsigned char sha1[20]; 61 unsigned char sha1[20];
diff --git a/ui-blob.h b/ui-blob.h
index dad275a..d7e7d45 100644
--- a/ui-blob.h
+++ b/ui-blob.h
@@ -3,2 +3,3 @@
3 3
4extern int cgit_print_file(char *path, const char *head);
4extern void cgit_print_blob(const char *hex, char *path, const char *head); 5extern void cgit_print_blob(const char *hex, char *path, const char *head);
diff --git a/ui-commit.c b/ui-commit.c
index f5b0ae5..2b4f677 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -14,9 +14,10 @@
14 14
15void cgit_print_commit(char *hex) 15void cgit_print_commit(char *hex, const char *prefix)
16{ 16{
17 struct commit *commit, *parent; 17 struct commit *commit, *parent;
18 struct commitinfo *info; 18 struct commitinfo *info, *parent_info;
19 struct commit_list *p; 19 struct commit_list *p;
20 struct strbuf notes = STRBUF_INIT;
20 unsigned char sha1[20]; 21 unsigned char sha1[20];
21 char *tmp; 22 char *tmp, *tmp2;
22 int parents = 0; 23 int parents = 0;
@@ -37,2 +38,4 @@ void cgit_print_commit(char *hex)
37 38
39 format_note(NULL, sha1, &notes, PAGE_ENCODING, 0);
40
38 load_ref_decorations(DECORATE_FULL_REFS); 41 load_ref_decorations(DECORATE_FULL_REFS);
@@ -60,5 +63,10 @@ void cgit_print_commit(char *hex)
60 tmp = sha1_to_hex(commit->object.sha1); 63 tmp = sha1_to_hex(commit->object.sha1);
61 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp); 64 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix, 0);
62 html(" ("); 65 html(" (");
63 cgit_patch_link("patch", NULL, NULL, NULL, tmp); 66 cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix);
67 html(") (");
68 if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff))
69 cgit_commit_link("unidiff", NULL, NULL, ctx.qry.head, tmp, prefix, 1);
70 else
71 cgit_commit_link("side-by-side diff", NULL, NULL, ctx.qry.head, tmp, prefix, 1);
64 html(")</td></tr>\n"); 72 html(")</td></tr>\n");
@@ -68,2 +76,6 @@ void cgit_print_commit(char *hex)
68 ctx.qry.head, tmp, NULL); 76 ctx.qry.head, tmp, NULL);
77 if (prefix) {
78 html(" /");
79 cgit_tree_link(prefix, NULL, NULL, ctx.qry.head, tmp, prefix);
80 }
69 html("</td></tr>\n"); 81 html("</td></tr>\n");
@@ -79,7 +91,11 @@ void cgit_print_commit(char *hex)
79 "<td colspan='2' class='sha1'>"); 91 "<td colspan='2' class='sha1'>");
80 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 92 tmp = tmp2 = sha1_to_hex(p->item->object.sha1);
81 ctx.qry.head, sha1_to_hex(p->item->object.sha1)); 93 if (ctx.repo->enable_subject_links) {
94 parent_info = cgit_parse_commit(parent);
95 tmp2 = parent_info->subject;
96 }
97 cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix, 0);
82 html(" ("); 98 html(" (");
83 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, 99 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
84 sha1_to_hex(p->item->object.sha1), NULL); 100 sha1_to_hex(p->item->object.sha1), prefix, 0);
85 html(")</td></tr>"); 101 html(")</td></tr>");
@@ -109,2 +125,13 @@ void cgit_print_commit(char *hex)
109 html("</div>"); 125 html("</div>");
126 if (notes.len != 0) {
127 html("<div class='notes-header'>Notes</div>");
128 html("<div class='notes'>");
129 if (ctx.repo->commit_filter)
130 cgit_open_filter(ctx.repo->commit_filter);
131 html_txt(notes.buf);
132 if (ctx.repo->commit_filter)
133 cgit_close_filter(ctx.repo->commit_filter);
134 html("</div>");
135 html("<div class='notes-footer'></div>");
136 }
110 if (parents < 3) { 137 if (parents < 3) {
@@ -114,4 +141,5 @@ void cgit_print_commit(char *hex)
114 tmp = NULL; 141 tmp = NULL;
115 cgit_print_diff(ctx.qry.sha1, tmp, NULL); 142 cgit_print_diff(ctx.qry.sha1, tmp, prefix);
116 } 143 }
144 strbuf_release(&notes);
117 cgit_free_commitinfo(info); 145 cgit_free_commitinfo(info);
diff --git a/ui-commit.h b/ui-commit.h
index 40bcb31..8198b4b 100644
--- a/ui-commit.h
+++ b/ui-commit.h
@@ -3,3 +3,3 @@
3 3
4extern void cgit_print_commit(char *hex); 4extern void cgit_print_commit(char *hex, const char *prefix);
5 5
diff --git a/ui-diff.c b/ui-diff.c
index 2196745..0dcabe9 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -11,2 +11,3 @@
11#include "ui-shared.h" 11#include "ui-shared.h"
12#include "ui-ssdiff.h"
12 13
@@ -34,2 +35,3 @@ static struct fileinfo {
34 35
36static int use_ssdiff = 0;
35 37
@@ -85,3 +87,3 @@ static void print_fileinfo(struct fileinfo *info)
85 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,
86 ctx.qry.sha2, info->new_path); 88 ctx.qry.sha2, info->new_path, 0);
87 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) 89 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
@@ -127,3 +129,3 @@ static void inspect_filepair(struct diff_filepair *pair)
127 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,
128 &binary, count_diff_lines); 130 &binary, 0, ctx.qry.ignorews, count_diff_lines);
129 if (files >= slots) { 131 if (files >= slots) {
@@ -154,5 +156,5 @@ static void inspect_filepair(struct diff_filepair *pair)
154void cgit_print_diffstat(const unsigned char *old_sha1, 156void cgit_print_diffstat(const unsigned char *old_sha1,
155 const unsigned char *new_sha1) 157 const unsigned char *new_sha1, const char *prefix)
156{ 158{
157 int i; 159 int i, save_context = ctx.qry.context;
158 160
@@ -160,3 +162,18 @@ void cgit_print_diffstat(const unsigned char *old_sha1,
160 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,
161 ctx.qry.sha2, NULL); 163 ctx.qry.sha2, NULL, 0);
164 if (prefix)
165 htmlf(" (limited to '%s')", prefix);
166 html(" (");
167 ctx.qry.context = (save_context > 0 ? save_context : 3) << 1;
168 cgit_self_link("more", NULL, NULL, &ctx);
169 html("/");
170 ctx.qry.context = (save_context > 3 ? save_context : 3) >> 1;
171 cgit_self_link("less", NULL, NULL, &ctx);
172 ctx.qry.context = save_context;
173 html(" context)");
174 html(" (");
175 ctx.qry.ignorews = (ctx.qry.ignorews + 1) % 2;
176 cgit_self_link(ctx.qry.ignorews ? "ignore" : "show", NULL, NULL, &ctx);
177 ctx.qry.ignorews = (ctx.qry.ignorews + 1) % 2;
178 html(" whitespace changes)");
162 html("</div>"); 179 html("</div>");
@@ -164,3 +181,4 @@ void cgit_print_diffstat(const unsigned char *old_sha1,
164 max_changes = 0; 181 max_changes = 0;
165 cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, NULL); 182 cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, prefix,
183 ctx.qry.ignorews);
166 for(i = 0; i<files; i++) 184 for(i = 0; i<files; i++)
@@ -248,2 +266,15 @@ static void header(unsigned char *sha1, char *path1, int mode1,
248 266
267static void print_ssdiff_link()
268{
269 if (!strcmp(ctx.qry.page, "diff")) {
270 if (use_ssdiff)
271 cgit_diff_link("Unidiff", NULL, NULL, ctx.qry.head,
272 ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1);
273 else
274 cgit_diff_link("Side-by-side diff", NULL, NULL,
275 ctx.qry.head, ctx.qry.sha1,
276 ctx.qry.sha2, ctx.qry.path, 1);
277 }
278}
279
249static void filepair_cb(struct diff_filepair *pair) 280static void filepair_cb(struct diff_filepair *pair)
@@ -253,10 +284,19 @@ static void filepair_cb(struct diff_filepair *pair)
253 int binary = 0; 284 int binary = 0;
285 linediff_fn print_line_fn = print_line;
254 286
287 if (use_ssdiff) {
288 cgit_ssdiff_header_begin();
289 print_line_fn = cgit_ssdiff_line_cb;
290 }
255 header(pair->one->sha1, pair->one->path, pair->one->mode, 291 header(pair->one->sha1, pair->one->path, pair->one->mode,
256 pair->two->sha1, pair->two->path, pair->two->mode); 292 pair->two->sha1, pair->two->path, pair->two->mode);
293 if (use_ssdiff)
294 cgit_ssdiff_header_end();
257 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 295 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
258 if (S_ISGITLINK(pair->one->mode)) 296 if (S_ISGITLINK(pair->one->mode))
259 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 297 print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
260 if (S_ISGITLINK(pair->two->mode)) 298 if (S_ISGITLINK(pair->two->mode))
261 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 299 print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
300 if (use_ssdiff)
301 cgit_ssdiff_footer();
262 return; 302 return;
@@ -264,7 +304,14 @@ static void filepair_cb(struct diff_filepair *pair)
264 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 304 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
265 &new_size, &binary, print_line)) 305 &new_size, &binary, ctx.qry.context,
306 ctx.qry.ignorews, print_line_fn))
266 cgit_print_error("Error running diff"); 307 cgit_print_error("Error running diff");
267 if (binary) 308 if (binary) {
309 if (use_ssdiff)
310 html("<tr><td colspan='4'>Binary files differ</td></tr>");
311 else
268 html("Binary files differ"); 312 html("Binary files differ");
269} 313}
314 if (use_ssdiff)
315 cgit_ssdiff_footer();
316}
270 317
@@ -305,7 +352,18 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefi
305 } 352 }
306 cgit_print_diffstat(old_rev_sha1, new_rev_sha1);
307 353
354 if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff))
355 use_ssdiff = 1;
356
357 print_ssdiff_link();
358 cgit_print_diffstat(old_rev_sha1, new_rev_sha1, prefix);
359
360 if (use_ssdiff) {
361 html("<table summary='ssdiff' class='ssdiff'>");
362 } else {
308 html("<table summary='diff' class='diff'>"); 363 html("<table summary='diff' class='diff'>");
309 html("<tr><td>"); 364 html("<tr><td>");
310 cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix); 365 }
366 cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix,
367 ctx.qry.ignorews);
368 if (!use_ssdiff)
311 html("</td></tr>"); 369 html("</td></tr>");
diff --git a/ui-log.c b/ui-log.c
index f3132c9..0536b23 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -35,3 +35,4 @@ void inspect_files(struct diff_filepair *pair)
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, ctx.qry.ignorews,
37 count_lines);
37} 38}
@@ -48,4 +49,5 @@ void show_commit_decorations(struct commit *commit)
48 strncpy(buf, deco->name + 11, sizeof(buf) - 1); 49 strncpy(buf, deco->name + 11, sizeof(buf) - 1);
49 cgit_log_link(buf, NULL, "branch-deco", buf, NULL, NULL, 50 cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
50 0, NULL, NULL, ctx.qry.showmsg); 51 ctx.qry.vpath, 0, NULL, NULL,
52 ctx.qry.showmsg);
51 } 53 }
@@ -62,4 +64,5 @@ void show_commit_decorations(struct commit *commit)
62 cgit_log_link(buf, NULL, "remote-deco", NULL, 64 cgit_log_link(buf, NULL, "remote-deco", NULL,
63 sha1_to_hex(commit->object.sha1), NULL, 65 sha1_to_hex(commit->object.sha1),
64 0, NULL, NULL, ctx.qry.showmsg); 66 ctx.qry.vpath, 0, NULL, NULL,
67 ctx.qry.showmsg);
65 } 68 }
@@ -68,3 +71,4 @@ void show_commit_decorations(struct commit *commit)
68 cgit_commit_link(buf, NULL, "deco", ctx.qry.head, 71 cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
69 sha1_to_hex(commit->object.sha1)); 72 sha1_to_hex(commit->object.sha1),
73 ctx.qry.vpath, 0);
70 } 74 }
@@ -84,3 +88,3 @@ void print_commit(struct commit *commit)
84 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1)); 88 tmp = fmt("id=%s", sha1_to_hex(commit->object.sha1));
85 tmp = cgit_pageurl(ctx.repo->url, "commit", tmp); 89 tmp = cgit_fileurl(ctx.repo->url, "commit", ctx.qry.vpath, tmp);
86 html_link_open(tmp, NULL, NULL); 90 html_link_open(tmp, NULL, NULL);
@@ -91,3 +95,3 @@ void print_commit(struct commit *commit)
91 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, 95 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
92 sha1_to_hex(commit->object.sha1)); 96 sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0);
93 show_commit_decorations(commit); 97 show_commit_decorations(commit);
@@ -109,2 +113,5 @@ void print_commit(struct commit *commit)
109 if (ctx.qry.showmsg) { 113 if (ctx.qry.showmsg) {
114 struct strbuf notes = STRBUF_INIT;
115 format_note(NULL, commit->object.sha1, &notes, PAGE_ENCODING, 0);
116
110 if (ctx.repo->enable_log_filecount) { 117 if (ctx.repo->enable_log_filecount) {
@@ -118,2 +125,11 @@ void print_commit(struct commit *commit)
118 html("</td></tr>\n"); 125 html("</td></tr>\n");
126 if (notes.len != 0) {
127 html("<tr class='nohover'>");
128 html("<td class='lognotes-label'>Notes:</td>");
129 htmlf("<td colspan='%d' class='lognotes'>",
130 cols);
131 html_txt(notes.buf);
132 html("</td></tr>\n");
133 }
134 strbuf_release(&notes);
119 } 135 }
@@ -148,6 +164,9 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
148 164
149 if (grep && pattern && (!strcmp(grep, "grep") || 165 if (grep && pattern) {
150 !strcmp(grep, "author") || 166 if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
151 !strcmp(grep, "committer"))) 167 !strcmp(grep, "committer"))
152 argv[argc++] = fmt("--%s=%s", grep, pattern); 168 argv[argc++] = fmt("--%s=%s", grep, pattern);
169 if (!strcmp(grep, "range"))
170 argv[1] = pattern;
171 }
153 172
@@ -178,3 +197,3 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
178 NULL, ctx.qry.head, ctx.qry.sha1, 197 NULL, ctx.qry.head, ctx.qry.sha1,
179 ctx.qry.path, ctx.qry.ofs, ctx.qry.grep, 198 ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep,
180 ctx.qry.search, ctx.qry.showmsg ? 0 : 1); 199 ctx.qry.search, ctx.qry.showmsg ? 0 : 1);
@@ -215,3 +234,3 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
215 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, 234 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
216 ctx.qry.sha1, ctx.qry.path, 235 ctx.qry.sha1, ctx.qry.vpath,
217 ofs - cnt, ctx.qry.grep, 236 ofs - cnt, ctx.qry.grep,
@@ -222,3 +241,3 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
222 cgit_log_link("[next]", NULL, NULL, ctx.qry.head, 241 cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
223 ctx.qry.sha1, ctx.qry.path, 242 ctx.qry.sha1, ctx.qry.vpath,
224 ofs + cnt, ctx.qry.grep, 243 ofs + cnt, ctx.qry.grep,
@@ -229,4 +248,4 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
229 html("<tr class='nohover'><td colspan='3'>"); 248 html("<tr class='nohover'><td colspan='3'>");
230 cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL, NULL, 0, 249 cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL,
231 NULL, NULL, ctx.qry.showmsg); 250 ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg);
232 html("</td></tr>\n"); 251 html("</td></tr>\n");
diff --git a/ui-patch.c b/ui-patch.c
index 2a8f7a5..ca008f3 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -73,3 +73,3 @@ static void filepair_cb(struct diff_filepair *pair)
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, 0, print_line))
75 html("Error running diff"); 75 html("Error running diff");
@@ -79,3 +79,3 @@ static void filepair_cb(struct diff_filepair *pair)
79 79
80void cgit_print_patch(char *hex) 80void cgit_print_patch(char *hex, const char *prefix)
81{ 81{
@@ -124,3 +124,5 @@ void cgit_print_patch(char *hex)
124 html("---\n"); 124 html("---\n");
125 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL); 125 if (prefix)
126 htmlf("(limited to '%s')\n\n", prefix);
127 cgit_diff_tree(old_sha1, sha1, filepair_cb, prefix, 0);
126 html("--\n"); 128 html("--\n");
diff --git a/ui-patch.h b/ui-patch.h
index 9f68212..1641cea 100644
--- a/ui-patch.h
+++ b/ui-patch.h
@@ -3,3 +3,3 @@
3 3
4extern void cgit_print_patch(char *hex); 4extern void cgit_print_patch(char *hex, const char *prefix);
5 5
diff --git a/ui-plain.c b/ui-plain.c
index 66cb19c..1b2b672 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -12,4 +12,3 @@
12 12
13char *curr_rev; 13int match_baselen;
14char *match_path;
15int match; 14int match;
@@ -37,3 +36,3 @@ static void print_object(const unsigned char *sha1, const char *path)
37 if (ext && *(++ext)) { 36 if (ext && *(++ext)) {
38 mime = string_list_lookup(ext, &ctx.cfg.mimetypes); 37 mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
39 if (mime) 38 if (mime)
@@ -55,2 +54,34 @@ static void print_object(const unsigned char *sha1, const char *path)
55 54
55static void print_dir(const unsigned char *sha1, const char *path,
56 const char *base)
57{
58 char *fullpath;
59 if (path[0] || base[0])
60 fullpath = fmt("/%s%s/", base, path);
61 else
62 fullpath = "/";
63 ctx.page.etag = sha1_to_hex(sha1);
64 cgit_print_http_headers(&ctx);
65 htmlf("<html><head><title>%s</title></head>\n<body>\n"
66 " <h2>%s</h2>\n <ul>\n", fullpath, fullpath);
67 if (path[0] || base[0])
68 html(" <li><a href=\"../\">../</a></li>\n");
69 match = 2;
70}
71
72static void print_dir_entry(const unsigned char *sha1, const char *path,
73 unsigned mode)
74{
75 const char *sep = "";
76 if (S_ISDIR(mode))
77 sep = "/";
78 htmlf(" <li><a href=\"%s%s\">%s%s</a></li>\n", path, sep, path, sep);
79 match = 2;
80}
81
82static void print_dir_tail(void)
83{
84 html(" </ul>\n</body></html>\n");
85}
86
56static int walk_tree(const unsigned char *sha1, const char *base, int baselen, 87static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
@@ -59,9 +90,23 @@ static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
59{ 90{
60 if (S_ISDIR(mode)) 91 if (baselen == match_baselen) {
92 if (S_ISREG(mode))
93 print_object(sha1, pathname);
94 else if (S_ISDIR(mode)) {
95 print_dir(sha1, pathname, base);
96 return READ_TREE_RECURSIVE;
97 }
98 }
99 else if (baselen > match_baselen)
100 print_dir_entry(sha1, pathname, mode);
101 else if (S_ISDIR(mode))
61 return READ_TREE_RECURSIVE; 102 return READ_TREE_RECURSIVE;
62 103
63 if (S_ISREG(mode) && !strncmp(base, match_path, baselen) && 104 return 0;
64 !strcmp(pathname, match_path + baselen)) 105}
65 print_object(sha1, pathname);
66 106
107static int basedir_len(const char *path)
108{
109 char *p = strrchr(path, '/');
110 if (p)
111 return p - path + 1;
67 return 0; 112 return 0;
@@ -79,3 +124,2 @@ void cgit_print_plain(struct cgit_context *ctx)
79 124
80 curr_rev = xstrdup(rev);
81 if (get_sha1(rev, sha1)) { 125 if (get_sha1(rev, sha1)) {
@@ -89,3 +133,9 @@ void cgit_print_plain(struct cgit_context *ctx)
89 } 133 }
90 match_path = ctx->qry.path; 134 if (!paths[0]) {
135 paths[0] = "";
136 match_baselen = -1;
137 print_dir(commit->tree->object.sha1, "", "");
138 }
139 else
140 match_baselen = basedir_len(paths[0]);
91 read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL); 141 read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL);
@@ -93,2 +143,4 @@ void cgit_print_plain(struct cgit_context *ctx)
93 html_status(404, "Not found", 0); 143 html_status(404, "Not found", 0);
144 else if (match == 2)
145 print_dir_tail();
94} 146}
diff --git a/ui-refs.c b/ui-refs.c
index 6571cc4..caddfbc 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -78,3 +78,3 @@ static int print_branch(struct refinfo *ref)
78 if (ref->object->type == OBJ_COMMIT) { 78 if (ref->object->type == OBJ_COMMIT) {
79 cgit_commit_link(info->subject, NULL, NULL, name, NULL); 79 cgit_commit_link(info->subject, NULL, NULL, name, NULL, NULL, 0);
80 html("</td><td>"); 80 html("</td><td>");
@@ -191,2 +191,4 @@ void cgit_print_branches(int maxcount)
191 for_each_branch_ref(cgit_refs_cb, &list); 191 for_each_branch_ref(cgit_refs_cb, &list);
192 if (ctx.repo->enable_remote_branches)
193 for_each_remote_ref(cgit_refs_cb, &list);
192 194
diff --git a/ui-shared.c b/ui-shared.c
index 8a7cc32..ae29615 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -29,3 +29,3 @@ static char *http_date(time_t t)
29 29
30void cgit_print_error(char *msg) 30void cgit_print_error(const char *msg)
31{ 31{
@@ -135,3 +135,3 @@ char *cgit_currurl()
135 135
136static void site_url(char *page, char *search, int ofs) 136static void site_url(const char *page, const char *search, int ofs)
137{ 137{
@@ -162,4 +162,4 @@ static void site_url(char *page, char *search, int ofs)
162 162
163static void site_link(char *page, char *name, char *title, char *class, 163static void site_link(const char *page, const char *name, const char *title,
164 char *search, int ofs) 164 const char *class, const char *search, int ofs)
165{ 165{
@@ -183,4 +183,4 @@ static void site_link(char *page, char *name, char *title, char *class,
183 183
184void cgit_index_link(char *name, char *title, char *class, char *pattern, 184void cgit_index_link(const char *name, const char *title, const char *class,
185 int ofs) 185 const char *pattern, int ofs)
186{ 186{
@@ -189,4 +189,4 @@ void cgit_index_link(char *name, char *title, char *class, char *pattern,
189 189
190static char *repolink(char *title, char *class, char *page, char *head, 190static char *repolink(const char *title, const char *class, const char *page,
191 char *path) 191 const char *head, const char *path)
192{ 192{
@@ -242,4 +242,5 @@ static char *repolink(char *title, char *class, char *page, char *head,
242 242
243static void reporevlink(char *page, char *name, char *title, char *class, 243static void reporevlink(const char *page, const char *name, const char *title,
244 char *head, char *rev, char *path) 244 const char *class, const char *head, const char *rev,
245 const char *path)
245{ 246{
@@ -258,3 +259,4 @@ static void reporevlink(char *page, char *name, char *title, char *class,
258 259
259void cgit_summary_link(char *name, char *title, char *class, char *head) 260void cgit_summary_link(const char *name, const char *title, const char *class,
261 const char *head)
260{ 262{
@@ -263,4 +265,4 @@ void cgit_summary_link(char *name, char *title, char *class, char *head)
263 265
264void cgit_tag_link(char *name, char *title, char *class, char *head, 266void cgit_tag_link(const char *name, const char *title, const char *class,
265 char *rev) 267 const char *head, const char *rev)
266{ 268{
@@ -269,4 +271,4 @@ void cgit_tag_link(char *name, char *title, char *class, char *head,
269 271
270void cgit_tree_link(char *name, char *title, char *class, char *head, 272void cgit_tree_link(const char *name, const char *title, const char *class,
271 char *rev, char *path) 273 const char *head, const char *rev, const char *path)
272{ 274{
@@ -275,4 +277,4 @@ void cgit_tree_link(char *name, char *title, char *class, char *head,
275 277
276void cgit_plain_link(char *name, char *title, char *class, char *head, 278void cgit_plain_link(const char *name, const char *title, const char *class,
277 char *rev, char *path) 279 const char *head, const char *rev, const char *path)
278{ 280{
@@ -281,5 +283,5 @@ void cgit_plain_link(char *name, char *title, char *class, char *head,
281 283
282void cgit_log_link(char *name, char *title, char *class, char *head, 284void cgit_log_link(const char *name, const char *title, const char *class,
283 char *rev, char *path, int ofs, char *grep, char *pattern, 285 const char *head, const char *rev, const char *path,
284 int showmsg) 286 int ofs, const char *grep, const char *pattern, int showmsg)
285{ 287{
@@ -318,4 +320,5 @@ void cgit_log_link(char *name, char *title, char *class, char *head,
318 320
319void cgit_commit_link(char *name, char *title, char *class, char *head, 321void cgit_commit_link(char *name, const char *title, const char *class,
320 char *rev) 322 const char *head, const char *rev, const char *path,
323 int toggle_ssdiff)
321{ 324{
@@ -327,7 +330,35 @@ void cgit_commit_link(char *name, char *title, char *class, char *head,
327 } 330 }
328 reporevlink("commit", name, title, class, head, rev, NULL); 331
332 char *delim;
333
334 delim = repolink(title, class, "commit", head, path);
335 if (rev && strcmp(rev, ctx.qry.head)) {
336 html(delim);
337 html("id=");
338 html_url_arg(rev);
339 delim = "&amp;";
340 }
341 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
342 html(delim);
343 html("ss=1");
344 delim = "&amp;";
345 }
346 if (ctx.qry.context > 0 && ctx.qry.context != 3) {
347 html(delim);
348 html("context=");
349 htmlf("%d", ctx.qry.context);
350 delim = "&amp;";
351 }
352 if (ctx.qry.ignorews) {
353 html(delim);
354 html("ignorews=1");
355 delim = "&amp;";
356 }
357 html("'>");
358 html_txt(name);
359 html("</a>");
329} 360}
330 361
331void cgit_refs_link(char *name, char *title, char *class, char *head, 362void cgit_refs_link(const char *name, const char *title, const char *class,
332 char *rev, char *path) 363 const char *head, const char *rev, const char *path)
333{ 364{
@@ -336,4 +367,5 @@ void cgit_refs_link(char *name, char *title, char *class, char *head,
336 367
337void cgit_snapshot_link(char *name, char *title, char *class, char *head, 368void cgit_snapshot_link(const char *name, const char *title, const char *class,
338 char *rev, char *archivename) 369 const char *head, const char *rev,
370 const char *archivename)
339{ 371{
@@ -342,4 +374,5 @@ void cgit_snapshot_link(char *name, char *title, char *class, char *head,
342 374
343void cgit_diff_link(char *name, char *title, char *class, char *head, 375void cgit_diff_link(const char *name, const char *title, const char *class,
344 char *new_rev, char *old_rev, char *path) 376 const char *head, const char *new_rev, const char *old_rev,
377 const char *path, int toggle_ssdiff)
345{ 378{
@@ -358,2 +391,19 @@ void cgit_diff_link(char *name, char *title, char *class, char *head,
358 html_url_arg(old_rev); 391 html_url_arg(old_rev);
392 delim = "&amp;";
393 }
394 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
395 html(delim);
396 html("ss=1");
397 delim = "&amp;";
398 }
399 if (ctx.qry.context > 0 && ctx.qry.context != 3) {
400 html(delim);
401 html("context=");
402 htmlf("%d", ctx.qry.context);
403 delim = "&amp;";
404 }
405 if (ctx.qry.ignorews) {
406 html(delim);
407 html("ignorews=1");
408 delim = "&amp;";
359 } 409 }
@@ -364,10 +414,10 @@ void cgit_diff_link(char *name, char *title, char *class, char *head,
364 414
365void cgit_patch_link(char *name, char *title, char *class, char *head, 415void cgit_patch_link(const char *name, const char *title, const char *class,
366 char *rev) 416 const char *head, const char *rev, const char *path)
367{ 417{
368 reporevlink("patch", name, title, class, head, rev, NULL); 418 reporevlink("patch", name, title, class, head, rev, path);
369} 419}
370 420
371void cgit_stats_link(char *name, char *title, char *class, char *head, 421void cgit_stats_link(const char *name, const char *title, const char *class,
372 char *path) 422 const char *head, const char *path)
373{ 423{
@@ -376,2 +426,60 @@ void cgit_stats_link(char *name, char *title, char *class, char *head,
376 426
427void cgit_self_link(char *name, const char *title, const char *class,
428 struct cgit_context *ctx)
429{
430 if (!strcmp(ctx->qry.page, "repolist"))
431 return cgit_index_link(name, title, class, ctx->qry.search,
432 ctx->qry.ofs);
433 else if (!strcmp(ctx->qry.page, "summary"))
434 return cgit_summary_link(name, title, class, ctx->qry.head);
435 else if (!strcmp(ctx->qry.page, "tag"))
436 return cgit_tag_link(name, title, class, ctx->qry.head,
437 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL);
438 else if (!strcmp(ctx->qry.page, "tree"))
439 return cgit_tree_link(name, title, class, ctx->qry.head,
440 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
441 ctx->qry.path);
442 else if (!strcmp(ctx->qry.page, "plain"))
443 return cgit_plain_link(name, title, class, ctx->qry.head,
444 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
445 ctx->qry.path);
446 else if (!strcmp(ctx->qry.page, "log"))
447 return cgit_log_link(name, title, class, ctx->qry.head,
448 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
449 ctx->qry.path, ctx->qry.ofs,
450 ctx->qry.grep, ctx->qry.search,
451 ctx->qry.showmsg);
452 else if (!strcmp(ctx->qry.page, "commit"))
453 return cgit_commit_link(name, title, class, ctx->qry.head,
454 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
455 ctx->qry.path, 0);
456 else if (!strcmp(ctx->qry.page, "patch"))
457 return cgit_patch_link(name, title, class, ctx->qry.head,
458 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
459 ctx->qry.path);
460 else if (!strcmp(ctx->qry.page, "refs"))
461 return cgit_refs_link(name, title, class, ctx->qry.head,
462 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
463 ctx->qry.path);
464 else if (!strcmp(ctx->qry.page, "snapshot"))
465 return cgit_snapshot_link(name, title, class, ctx->qry.head,
466 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
467 ctx->qry.path);
468 else if (!strcmp(ctx->qry.page, "diff"))
469 return cgit_diff_link(name, title, class, ctx->qry.head,
470 ctx->qry.sha1, ctx->qry.sha2,
471 ctx->qry.path, 0);
472 else if (!strcmp(ctx->qry.page, "stats"))
473 return cgit_stats_link(name, title, class, ctx->qry.head,
474 ctx->qry.path);
475
476 /* Don't known how to make link for this page */
477 repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path);
478 html("><!-- cgit_self_link() doesn't know how to make link for page '");
479 html_txt(ctx->qry.page);
480 html("' -->");
481 html_txt(name);
482 html("</a>");
483}
484
377void cgit_object_link(struct object *obj) 485void cgit_object_link(struct object *obj)
@@ -385,3 +493,3 @@ void cgit_object_link(struct object *obj)
385 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL, 493 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
386 ctx.qry.head, fullrev); 494 ctx.qry.head, fullrev, NULL, 0);
387 return; 495 return;
@@ -397,3 +505,3 @@ void cgit_object_link(struct object *obj)
397 505
398void cgit_print_date(time_t secs, char *format, int local_time) 506void cgit_print_date(time_t secs, const char *format, int local_time)
399{ 507{
@@ -412,3 +520,3 @@ void cgit_print_date(time_t secs, char *format, int local_time)
412 520
413void cgit_print_age(time_t t, time_t max_relative, char *format) 521void cgit_print_age(time_t t, time_t max_relative, const char *format)
414{ 522{
@@ -511,3 +619,3 @@ void cgit_print_docstart(struct cgit_context *ctx)
511 html_attr(cgit_hosturl()); 619 html_attr(cgit_hosturl());
512 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.path, 620 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath,
513 fmt("h=%s", ctx->qry.head))); 621 fmt("h=%s", ctx->qry.head)));
@@ -591,3 +699,4 @@ int print_archive_ref(const char *refname, const unsigned char *sha1,
591 699
592void cgit_add_hidden_formfields(int incl_head, int incl_search, char *page) 700void cgit_add_hidden_formfields(int incl_head, int incl_search,
701 const char *page)
593{ 702{
@@ -597,4 +706,4 @@ void cgit_add_hidden_formfields(int incl_head, int incl_search, char *page)
597 url = fmt("%s/%s", ctx.qry.repo, page); 706 url = fmt("%s/%s", ctx.qry.repo, page);
598 if (ctx.qry.path) 707 if (ctx.qry.vpath)
599 url = fmt("%s/%s", url, ctx.qry.path); 708 url = fmt("%s/%s", url, ctx.qry.vpath);
600 html_hidden("url", url); 709 html_hidden("url", url);
@@ -621,7 +730,26 @@ void cgit_add_hidden_formfields(int incl_head, int incl_search, char *page)
621 730
622const char *fallback_cmd = "repolist"; 731static const char *hc(struct cgit_context *ctx, const char *page)
732{
733 return strcmp(ctx->qry.page, page) ? NULL : "active";
734}
623 735
624char *hc(struct cgit_cmd *cmd, const char *page) 736static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path)
625{ 737{
626 return (strcmp(cmd ? cmd->name : fallback_cmd, page) ? NULL : "active"); 738 char *old_path = ctx->qry.path;
739 char *p = path, *q, *end = path + strlen(path);
740
741 ctx->qry.path = NULL;
742 cgit_self_link("root", NULL, NULL, ctx);
743 ctx->qry.path = p = path;
744 while (p < end) {
745 if (!(q = strchr(p, '/')))
746 q = end;
747 *q = '\0';
748 html_txt("/");
749 cgit_self_link(p, NULL, NULL, ctx);
750 if (q < end)
751 *q = '/';
752 p = q + 1;
753 }
754 ctx->qry.path = old_path;
627} 755}
@@ -677,7 +805,2 @@ void cgit_print_pageheader(struct cgit_context *ctx)
677{ 805{
678 struct cgit_cmd *cmd = cgit_get_cmd(ctx);
679
680 if (!cmd && ctx->repo)
681 fallback_cmd = "summary";
682
683 html("<div id='cgit'>"); 806 html("<div id='cgit'>");
@@ -688,20 +811,21 @@ void cgit_print_pageheader(struct cgit_context *ctx)
688 if (ctx->repo) { 811 if (ctx->repo) {
689 cgit_summary_link("summary", NULL, hc(cmd, "summary"), 812 cgit_summary_link("summary", NULL, hc(ctx, "summary"),
690 ctx->qry.head); 813 ctx->qry.head);
691 cgit_refs_link("refs", NULL, hc(cmd, "refs"), ctx->qry.head, 814 cgit_refs_link("refs", NULL, hc(ctx, "refs"), ctx->qry.head,
692 ctx->qry.sha1, NULL); 815 ctx->qry.sha1, NULL);
693 cgit_log_link("log", NULL, hc(cmd, "log"), ctx->qry.head, 816 cgit_log_link("log", NULL, hc(ctx, "log"), ctx->qry.head,
694 NULL, NULL, 0, NULL, NULL, ctx->qry.showmsg); 817 NULL, ctx->qry.vpath, 0, NULL, NULL,
695 cgit_tree_link("tree", NULL, hc(cmd, "tree"), ctx->qry.head, 818 ctx->qry.showmsg);
696 ctx->qry.sha1, NULL); 819 cgit_tree_link("tree", NULL, hc(ctx, "tree"), ctx->qry.head,
697 cgit_commit_link("commit", NULL, hc(cmd, "commit"), 820 ctx->qry.sha1, ctx->qry.vpath);
698 ctx->qry.head, ctx->qry.sha1); 821 cgit_commit_link("commit", NULL, hc(ctx, "commit"),
699 cgit_diff_link("diff", NULL, hc(cmd, "diff"), ctx->qry.head, 822 ctx->qry.head, ctx->qry.sha1, ctx->qry.vpath, 0);
700 ctx->qry.sha1, ctx->qry.sha2, NULL); 823 cgit_diff_link("diff", NULL, hc(ctx, "diff"), ctx->qry.head,
824 ctx->qry.sha1, ctx->qry.sha2, ctx->qry.vpath, 0);
701 if (ctx->repo->max_stats) 825 if (ctx->repo->max_stats)
702 cgit_stats_link("stats", NULL, hc(cmd, "stats"), 826 cgit_stats_link("stats", NULL, hc(ctx, "stats"),
703 ctx->qry.head, NULL); 827 ctx->qry.head, ctx->qry.vpath);
704 if (ctx->repo->readme) 828 if (ctx->repo->readme)
705 reporevlink("about", "about", NULL, 829 reporevlink("about", "about", NULL,
706 hc(cmd, "about"), ctx->qry.head, NULL, 830 hc(ctx, "about"), ctx->qry.head, NULL,
707 NULL); 831 NULL);
@@ -711,3 +835,3 @@ void cgit_print_pageheader(struct cgit_context *ctx)
711 html_url_path(cgit_fileurl(ctx->qry.repo, "log", 835 html_url_path(cgit_fileurl(ctx->qry.repo, "log",
712 ctx->qry.path, NULL)); 836 ctx->qry.vpath, NULL));
713 html("'>\n"); 837 html("'>\n");
@@ -718,2 +842,3 @@ void cgit_print_pageheader(struct cgit_context *ctx)
718 html_option("committer", "committer", ctx->qry.grep); 842 html_option("committer", "committer", ctx->qry.grep);
843 html_option("range", "range", ctx->qry.grep);
719 html("</select>\n"); 844 html("</select>\n");
@@ -725,5 +850,5 @@ void cgit_print_pageheader(struct cgit_context *ctx)
725 } else { 850 } else {
726 site_link(NULL, "index", NULL, hc(cmd, "repolist"), NULL, 0); 851 site_link(NULL, "index", NULL, hc(ctx, "repolist"), NULL, 0);
727 if (ctx->cfg.root_readme) 852 if (ctx->cfg.root_readme)
728 site_link("about", "about", NULL, hc(cmd, "about"), 853 site_link("about", "about", NULL, hc(ctx, "about"),
729 NULL, 0); 854 NULL, 0);
@@ -740,2 +865,8 @@ void cgit_print_pageheader(struct cgit_context *ctx)
740 html("</td></tr></table>\n"); 865 html("</td></tr></table>\n");
866 if (ctx->qry.vpath) {
867 html("<div class='path'>");
868 html("path: ");
869 cgit_print_path_crumbs(ctx, ctx->qry.vpath);
870 html("</div>");
871 }
741 html("<div class='content'>"); 872 html("<div class='content'>");
@@ -762,4 +893,10 @@ void cgit_print_snapshot_links(const char *repo, const char *head,
762 const struct cgit_snapshot_format* f; 893 const struct cgit_snapshot_format* f;
894 char *prefix;
763 char *filename; 895 char *filename;
896 unsigned char sha1[20];
764 897
898 if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 &&
899 (hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1]))
900 hex++;
901 prefix = xstrdup(fmt("%s-%s", cgit_repobasename(repo), hex));
765 for (f = cgit_snapshot_formats; f->suffix; f++) { 902 for (f = cgit_snapshot_formats; f->suffix; f++) {
@@ -767,4 +904,3 @@ void cgit_print_snapshot_links(const char *repo, const char *head,
767 continue; 904 continue;
768 filename = fmt("%s-%s%s", cgit_repobasename(repo), hex, 905 filename = fmt("%s%s", prefix, f->suffix);
769 f->suffix);
770 cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename); 906 cgit_snapshot_link(filename, NULL, NULL, NULL, NULL, filename);
diff --git a/ui-shared.h b/ui-shared.h
index b12aa89..3cc1258 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -12,31 +12,46 @@ extern char *cgit_pageurl(const char *reponame, const char *pagename,
12 12
13extern void cgit_index_link(char *name, char *title, char *class, 13extern void cgit_index_link(const char *name, const char *title,
14 char *pattern, int ofs); 14 const char *class, const char *pattern, int ofs);
15extern void cgit_summary_link(char *name, char *title, char *class, char *head); 15extern void cgit_summary_link(const char *name, const char *title,
16extern void cgit_tag_link(char *name, char *title, char *class, char *head, 16 const char *class, const char *head);
17 char *rev); 17extern void cgit_tag_link(const char *name, const char *title,
18extern void cgit_tree_link(char *name, char *title, char *class, char *head, 18 const char *class, const char *head,
19 char *rev, char *path); 19 const char *rev);
20extern void cgit_plain_link(char *name, char *title, char *class, char *head, 20extern void cgit_tree_link(const char *name, const char *title,
21 char *rev, char *path); 21 const char *class, const char *head,
22extern void cgit_log_link(char *name, char *title, char *class, char *head, 22 const char *rev, const char *path);
23 char *rev, char *path, int ofs, char *grep, 23extern void cgit_plain_link(const char *name, const char *title,
24 char *pattern, int showmsg); 24 const char *class, const char *head,
25extern void cgit_commit_link(char *name, char *title, char *class, char *head, 25 const char *rev, const char *path);
26 char *rev); 26extern void cgit_log_link(const char *name, const char *title,
27extern void cgit_patch_link(char *name, char *title, char *class, char *head, 27 const char *class, const char *head, const char *rev,
28 char *rev); 28 const char *path, int ofs, const char *grep,
29extern void cgit_refs_link(char *name, char *title, char *class, char *head, 29 const char *pattern, int showmsg);
30 char *rev, char *path); 30extern void cgit_commit_link(char *name, const char *title,
31extern void cgit_snapshot_link(char *name, char *title, char *class, 31 const char *class, const char *head,
32 char *head, char *rev, char *archivename); 32 const char *rev, const char *path,
33extern void cgit_diff_link(char *name, char *title, char *class, char *head, 33 int toggle_ssdiff);
34 char *new_rev, char *old_rev, char *path); 34extern void cgit_patch_link(const char *name, const char *title,
35extern void cgit_stats_link(char *name, char *title, char *class, char *head, 35 const char *class, const char *head,
36 char *path); 36 const char *rev, const char *path);
37extern void cgit_refs_link(const char *name, const char *title,
38 const char *class, const char *head,
39 const char *rev, const char *path);
40extern void cgit_snapshot_link(const char *name, const char *title,
41 const char *class, const char *head,
42 const char *rev, const char *archivename);
43extern void cgit_diff_link(const char *name, const char *title,
44 const char *class, const char *head,
45 const char *new_rev, const char *old_rev,
46 const char *path, int toggle_ssdiff);
47extern void cgit_stats_link(const char *name, const char *title,
48 const char *class, const char *head,
49 const char *path);
50extern void cgit_self_link(char *name, const char *title,
51 const char *class, struct cgit_context *ctx);
37extern void cgit_object_link(struct object *obj); 52extern void cgit_object_link(struct object *obj);
38 53
39extern void cgit_print_error(char *msg); 54extern void cgit_print_error(const char *msg);
40extern void cgit_print_date(time_t secs, char *format, int local_time); 55extern void cgit_print_date(time_t secs, const char *format, int local_time);
41extern void cgit_print_age(time_t t, time_t max_relative, char *format); 56extern void cgit_print_age(time_t t, time_t max_relative, const char *format);
42extern void cgit_print_http_headers(struct cgit_context *ctx); 57extern void cgit_print_http_headers(struct cgit_context *ctx);
@@ -49,3 +64,3 @@ extern void cgit_print_snapshot_links(const char *repo, const char *head,
49extern void cgit_add_hidden_formfields(int incl_head, int incl_search, 64extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
50 char *page); 65 const char *page);
51#endif /* UI_SHARED_H */ 66#endif /* UI_SHARED_H */
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 4136b3e..1b25dca 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -37,7 +37,13 @@ static int write_tar_bzip2_archive(struct archiver_args *args)
37 37
38static int write_tar_xz_archive(struct archiver_args *args)
39{
40 return write_compressed_tar_archive(args,"xz");
41}
42
38const struct cgit_snapshot_format cgit_snapshot_formats[] = { 43const struct cgit_snapshot_format cgit_snapshot_formats[] = {
39 { ".zip", "application/x-zip", write_zip_archive, 0x1 }, 44 { ".zip", "application/x-zip", write_zip_archive, 0x01 },
40 { ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x2 }, 45 { ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x02 },
41 { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x4 }, 46 { ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x04 },
42 { ".tar", "application/x-tar", write_tar_archive, 0x8 }, 47 { ".tar", "application/x-tar", write_tar_archive, 0x08 },
48 { ".tar.xz", "application/x-xz", write_tar_xz_archive, 0x10 },
43 {} 49 {}
diff --git a/ui-ssdiff.c b/ui-ssdiff.c
new file mode 100644
index 0000000..408e620
--- a/dev/null
+++ b/ui-ssdiff.c
@@ -0,0 +1,369 @@
1#include "cgit.h"
2#include "html.h"
3#include "ui-shared.h"
4
5extern int use_ssdiff;
6
7static int current_old_line, current_new_line;
8
9struct deferred_lines {
10 int line_no;
11 char *line;
12 struct deferred_lines *next;
13};
14
15static struct deferred_lines *deferred_old, *deferred_old_last;
16static struct deferred_lines *deferred_new, *deferred_new_last;
17
18static char *longest_common_subsequence(char *A, char *B)
19{
20 int i, j, ri;
21 int m = strlen(A);
22 int n = strlen(B);
23 int L[m + 1][n + 1];
24 int tmp1, tmp2;
25 int lcs_length;
26 char *result;
27
28 for (i = m; i >= 0; i--) {
29 for (j = n; j >= 0; j--) {
30 if (A[i] == '\0' || B[j] == '\0') {
31 L[i][j] = 0;
32 } else if (A[i] == B[j]) {
33 L[i][j] = 1 + L[i + 1][j + 1];
34 } else {
35 tmp1 = L[i + 1][j];
36 tmp2 = L[i][j + 1];
37 L[i][j] = (tmp1 > tmp2 ? tmp1 : tmp2);
38 }
39 }
40 }
41
42 lcs_length = L[0][0];
43 result = xmalloc(lcs_length + 2);
44 memset(result, 0, sizeof(*result) * (lcs_length + 2));
45
46 ri = 0;
47 i = 0;
48 j = 0;
49 while (i < m && j < n) {
50 if (A[i] == B[j]) {
51 result[ri] = A[i];
52 ri += 1;
53 i += 1;
54 j += 1;
55 } else if (L[i + 1][j] >= L[i][j + 1]) {
56 i += 1;
57 } else {
58 j += 1;
59 }
60 }
61 return result;
62}
63
64static int line_from_hunk(char *line, char type)
65{
66 char *buf1, *buf2;
67 int len;
68
69 buf1 = strchr(line, type);
70 if (buf1 == NULL)
71 return 0;
72 buf1 += 1;
73 buf2 = strchr(buf1, ',');
74 if (buf2 == NULL)
75 return 0;
76 len = buf2 - buf1;
77 buf2 = xmalloc(len + 1);
78 strncpy(buf2, buf1, len);
79 buf2[len] = '\0';
80 int res = atoi(buf2);
81 free(buf2);
82 return res;
83}
84
85static char *replace_tabs(char *line)
86{
87 char *prev_buf = line;
88 char *cur_buf;
89 int linelen = strlen(line);
90 int n_tabs = 0;
91 int i;
92 char *result;
93 char *spaces = " ";
94
95 if (linelen == 0) {
96 result = xmalloc(1);
97 result[0] = '\0';
98 return result;
99 }
100
101 for (i = 0; i < linelen; i++)
102 if (line[i] == '\t')
103 n_tabs += 1;
104 result = xmalloc(linelen + n_tabs * 8 + 1);
105 result[0] = '\0';
106
107 while (1) {
108 cur_buf = strchr(prev_buf, '\t');
109 if (!cur_buf) {
110 strcat(result, prev_buf);
111 break;
112 } else {
113 strcat(result, " ");
114 strncat(result, spaces, 8 - (strlen(result) % 8));
115 strncat(result, prev_buf, cur_buf - prev_buf);
116 }
117 prev_buf = cur_buf + 1;
118 }
119 return result;
120}
121
122static int calc_deferred_lines(struct deferred_lines *start)
123{
124 struct deferred_lines *item = start;
125 int result = 0;
126 while (item) {
127 result += 1;
128 item = item->next;
129 }
130 return result;
131}
132
133static void deferred_old_add(char *line, int line_no)
134{
135 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
136 item->line = xstrdup(line);
137 item->line_no = line_no;
138 item->next = NULL;
139 if (deferred_old) {
140 deferred_old_last->next = item;
141 deferred_old_last = item;
142 } else {
143 deferred_old = deferred_old_last = item;
144 }
145}
146
147static void deferred_new_add(char *line, int line_no)
148{
149 struct deferred_lines *item = xmalloc(sizeof(struct deferred_lines));
150 item->line = xstrdup(line);
151 item->line_no = line_no;
152 item->next = NULL;
153 if (deferred_new) {
154 deferred_new_last->next = item;
155 deferred_new_last = item;
156 } else {
157 deferred_new = deferred_new_last = item;
158 }
159}
160
161static void print_part_with_lcs(char *class, char *line, char *lcs)
162{
163 int line_len = strlen(line);
164 int i, j;
165 char c[2] = " ";
166 int same = 1;
167
168 j = 0;
169 for (i = 0; i < line_len; i++) {
170 c[0] = line[i];
171 if (same) {
172 if (line[i] == lcs[j])
173 j += 1;
174 else {
175 same = 0;
176 htmlf("<span class='%s'>", class);
177 }
178 } else if (line[i] == lcs[j]) {
179 same = 1;
180 htmlf("</span>");
181 j += 1;
182 }
183 html_txt(c);
184 }
185}
186
187static void print_ssdiff_line(char *class,
188 int old_line_no,
189 char *old_line,
190 int new_line_no,
191 char *new_line, int individual_chars)
192{
193 char *lcs = NULL;
194 if (old_line)
195 old_line = replace_tabs(old_line + 1);
196 if (new_line)
197 new_line = replace_tabs(new_line + 1);
198 if (individual_chars && old_line && new_line)
199 lcs = longest_common_subsequence(old_line, new_line);
200 html("<tr>");
201 if (old_line_no > 0)
202 htmlf("<td class='lineno'>%d</td><td class='%s'>",
203 old_line_no, class);
204 else if (old_line)
205 htmlf("<td class='lineno'></td><td class='%s'>", class);
206 else
207 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
208 if (old_line) {
209 if (lcs)
210 print_part_with_lcs("del", old_line, lcs);
211 else
212 html_txt(old_line);
213 }
214
215 html("</td>");
216 if (new_line_no > 0)
217 htmlf("<td class='lineno'>%d</td><td class='%s'>",
218 new_line_no, class);
219 else if (new_line)
220 htmlf("<td class='lineno'></td><td class='%s'>", class);
221 else
222 htmlf("<td class='lineno'></td><td class='%s_dark'>", class);
223 if (new_line) {
224 if (lcs)
225 print_part_with_lcs("add", new_line, lcs);
226 else
227 html_txt(new_line);
228 }
229
230 html("</td></tr>");
231 if (lcs)
232 free(lcs);
233 if (new_line)
234 free(new_line);
235 if (old_line)
236 free(old_line);
237}
238
239static void print_deferred_old_lines()
240{
241 struct deferred_lines *iter_old, *tmp;
242 iter_old = deferred_old;
243 while (iter_old) {
244 print_ssdiff_line("del", iter_old->line_no,
245 iter_old->line, -1, NULL, 0);
246 tmp = iter_old->next;
247 free(iter_old);
248 iter_old = tmp;
249 }
250}
251
252static void print_deferred_new_lines()
253{
254 struct deferred_lines *iter_new, *tmp;
255 iter_new = deferred_new;
256 while (iter_new) {
257 print_ssdiff_line("add", -1, NULL,
258 iter_new->line_no, iter_new->line, 0);
259 tmp = iter_new->next;
260 free(iter_new);
261 iter_new = tmp;
262 }
263}
264
265static void print_deferred_changed_lines()
266{
267 struct deferred_lines *iter_old, *iter_new, *tmp;
268 int n_old_lines = calc_deferred_lines(deferred_old);
269 int n_new_lines = calc_deferred_lines(deferred_new);
270 int individual_chars = (n_old_lines == n_new_lines ? 1 : 0);
271
272 iter_old = deferred_old;
273 iter_new = deferred_new;
274 while (iter_old || iter_new) {
275 if (iter_old && iter_new)
276 print_ssdiff_line("changed", iter_old->line_no,
277 iter_old->line,
278 iter_new->line_no, iter_new->line,
279 individual_chars);
280 else if (iter_old)
281 print_ssdiff_line("changed", iter_old->line_no,
282 iter_old->line, -1, NULL, 0);
283 else if (iter_new)
284 print_ssdiff_line("changed", -1, NULL,
285 iter_new->line_no, iter_new->line, 0);
286 if (iter_old) {
287 tmp = iter_old->next;
288 free(iter_old);
289 iter_old = tmp;
290 }
291
292 if (iter_new) {
293 tmp = iter_new->next;
294 free(iter_new);
295 iter_new = tmp;
296 }
297 }
298}
299
300void cgit_ssdiff_print_deferred_lines()
301{
302 if (!deferred_old && !deferred_new)
303 return;
304 if (deferred_old && !deferred_new)
305 print_deferred_old_lines();
306 else if (!deferred_old && deferred_new)
307 print_deferred_new_lines();
308 else
309 print_deferred_changed_lines();
310 deferred_old = deferred_old_last = NULL;
311 deferred_new = deferred_new_last = NULL;
312}
313
314/*
315 * print a single line returned from xdiff
316 */
317void cgit_ssdiff_line_cb(char *line, int len)
318{
319 char c = line[len - 1];
320 line[len - 1] = '\0';
321 if (line[0] == '@') {
322 current_old_line = line_from_hunk(line, '-');
323 current_new_line = line_from_hunk(line, '+');
324 }
325
326 if (line[0] == ' ') {
327 if (deferred_old || deferred_new)
328 cgit_ssdiff_print_deferred_lines();
329 print_ssdiff_line("ctx", current_old_line, line,
330 current_new_line, line, 0);
331 current_old_line += 1;
332 current_new_line += 1;
333 } else if (line[0] == '+') {
334 deferred_new_add(line, current_new_line);
335 current_new_line += 1;
336 } else if (line[0] == '-') {
337 deferred_old_add(line, current_old_line);
338 current_old_line += 1;
339 } else if (line[0] == '@') {
340 html("<tr><td colspan='4' class='hunk'>");
341 html_txt(line);
342 html("</td></tr>");
343 } else {
344 html("<tr><td colspan='4' class='ctx'>");
345 html_txt(line);
346 html("</td></tr>");
347 }
348 line[len - 1] = c;
349}
350
351void cgit_ssdiff_header_begin()
352{
353 current_old_line = -1;
354 current_new_line = -1;
355 html("<tr><td class='space' colspan='4'><div></div></td></tr>");
356 html("<tr><td class='head' colspan='4'>");
357}
358
359void cgit_ssdiff_header_end()
360{
361 html("</td><tr>");
362}
363
364void cgit_ssdiff_footer()
365{
366 if (deferred_old || deferred_new)
367 cgit_ssdiff_print_deferred_lines();
368 html("<tr><td class='foot' colspan='4'></td></tr>");
369}
diff --git a/ui-ssdiff.h b/ui-ssdiff.h
new file mode 100644
index 0000000..64b4b12
--- a/dev/null
+++ b/ui-ssdiff.h
@@ -0,0 +1,13 @@
1#ifndef UI_SSDIFF_H
2#define UI_SSDIFF_H
3
4extern void cgit_ssdiff_print_deferred_lines();
5
6extern void cgit_ssdiff_line_cb(char *line, int len);
7
8extern void cgit_ssdiff_header_begin();
9extern void cgit_ssdiff_header_end();
10
11extern void cgit_ssdiff_footer();
12
13#endif /* UI_SSDIFF_H */
diff --git a/ui-stats.c b/ui-stats.c
index bdaf9cc..50c2540 100644
--- a/ui-stats.c
+++ b/ui-stats.c
@@ -177,3 +177,3 @@ static void add_commit(struct string_list *authors, struct commit *commit,
177 tmp = xstrdup(info->author); 177 tmp = xstrdup(info->author);
178 author = string_list_insert(tmp, authors); 178 author = string_list_insert(authors, tmp);
179 if (!author->util) 179 if (!author->util)
@@ -188,3 +188,3 @@ static void add_commit(struct string_list *authors, struct commit *commit,
188 tmp = xstrdup(period->pretty(date)); 188 tmp = xstrdup(period->pretty(date));
189 item = string_list_insert(tmp, items); 189 item = string_list_insert(items, tmp);
190 if (item->util) 190 if (item->util)
@@ -281,3 +281,3 @@ void print_combined_authorrow(struct string_list *authors, int from, int to,
281 items = &authorstat->list; 281 items = &authorstat->list;
282 date = string_list_lookup(tmp, items); 282 date = string_list_lookup(items, tmp);
283 if (date) 283 if (date)
@@ -333,3 +333,3 @@ void print_authors(struct string_list *authors, int top,
333 period->inc(tm); 333 period->inc(tm);
334 date = string_list_lookup(tmp, items); 334 date = string_list_lookup(items, tmp);
335 if (!date) 335 if (!date)
diff --git a/ui-summary.c b/ui-summary.c
index a2c018e..02f191e 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -3,2 +3,3 @@
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
4 * 5 *
@@ -12,2 +13,3 @@
12#include "ui-refs.h" 13#include "ui-refs.h"
14#include "ui-blob.h"
13 15
@@ -70,3 +72,3 @@ void cgit_print_repo_readme(char *path)
70{ 72{
71 char *slash, *tmp; 73 char *slash, *tmp, *colon, *ref = 0;
72 74
@@ -77,4 +79,7 @@ void cgit_print_repo_readme(char *path)
77 slash = strrchr(ctx.repo->readme, '/'); 79 slash = strrchr(ctx.repo->readme, '/');
80 if (!slash) {
81 slash = strchr(ctx.repo->readme, ':');
78 if (!slash) 82 if (!slash)
79 return; 83 return;
84 }
80 tmp = xmalloc(slash - ctx.repo->readme + 1 + strlen(path) + 1); 85 tmp = xmalloc(slash - ctx.repo->readme + 1 + strlen(path) + 1);
@@ -84,2 +89,12 @@ void cgit_print_repo_readme(char *path)
84 tmp = ctx.repo->readme; 89 tmp = ctx.repo->readme;
90 colon = strchr(tmp, ':');
91 if (colon && strlen(colon) > 1) {
92 *colon = '\0';
93 ref = tmp;
94 tmp = colon + 1;
95 while ((*tmp == '/' || *tmp == ':') && *tmp != '\0')
96 ++tmp;
97 if (!(*tmp))
98 return;
99 }
85 html("<div id='summary'>"); 100 html("<div id='summary'>");
@@ -87,2 +102,5 @@ void cgit_print_repo_readme(char *path)
87 cgit_open_filter(ctx.repo->about_filter); 102 cgit_open_filter(ctx.repo->about_filter);
103 if (ref)
104 cgit_print_file(tmp, ref);
105 else
88 html_include(tmp); 106 html_include(tmp);
diff --git a/ui-tag.c b/ui-tag.c
index c2d72af..39e4cb8 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -32,2 +32,10 @@ static void print_tag_content(char *buf)
32 32
33void print_download_links(char *revname)
34{
35 html("<tr><th>download</th><td class='sha1'>");
36 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
37 revname, ctx.repo->snapshots);
38 html("</td></tr>");
39}
40
33void cgit_print_tag(char *revname) 41void cgit_print_tag(char *revname)
@@ -58,3 +66,3 @@ void cgit_print_tag(char *revname)
58 html("<table class='commit-info'>\n"); 66 html("<table class='commit-info'>\n");
59 htmlf("<tr><td>Tag name</td><td>"); 67 htmlf("<tr><td>tag name</td><td>");
60 html_txt(revname); 68 html_txt(revname);
@@ -62,3 +70,3 @@ void cgit_print_tag(char *revname)
62 if (info->tagger_date > 0) { 70 if (info->tagger_date > 0) {
63 html("<tr><td>Tag date</td><td>"); 71 html("<tr><td>tag date</td><td>");
64 cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time); 72 cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time);
@@ -67,3 +75,3 @@ void cgit_print_tag(char *revname)
67 if (info->tagger) { 75 if (info->tagger) {
68 html("<tr><td>Tagged by</td><td>"); 76 html("<tr><td>tagged by</td><td>");
69 html_txt(info->tagger); 77 html_txt(info->tagger);
@@ -75,5 +83,7 @@ void cgit_print_tag(char *revname)
75 } 83 }
76 html("<tr><td>Tagged object</td><td>"); 84 html("<tr><td>tagged object</td><td class='sha1'>");
77 cgit_object_link(tag->tagged); 85 cgit_object_link(tag->tagged);
78 html("</td></tr>\n"); 86 html("</td></tr>\n");
87 if (ctx.repo->snapshots)
88 print_download_links(revname);
79 html("</table>\n"); 89 html("</table>\n");
@@ -82,8 +92,10 @@ void cgit_print_tag(char *revname)
82 html("<table class='commit-info'>\n"); 92 html("<table class='commit-info'>\n");
83 htmlf("<tr><td>Tag name</td><td>"); 93 htmlf("<tr><td>tag name</td><td>");
84 html_txt(revname); 94 html_txt(revname);
85 html("</td></tr>\n"); 95 html("</td></tr>\n");
86 html("<tr><td>Tagged object</td><td>"); 96 html("<tr><td>Tagged object</td><td class='sha1'>");
87 cgit_object_link(obj); 97 cgit_object_link(obj);
88 html("</td></tr>\n"); 98 html("</td></tr>\n");
99 if (ctx.repo->snapshots)
100 print_download_links(revname);
89 html("</table>\n"); 101 html("</table>\n");
diff --git a/ui-tree.c b/ui-tree.c
index a164767..75ec9cb 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -104,6 +104,12 @@ static void print_object(const unsigned char *sha1, char *path, const char *base
104 104
105 html(" ("); 105 htmlf("blob: %s (", sha1_to_hex(sha1));
106 cgit_plain_link("plain", NULL, NULL, ctx.qry.head, 106 cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
107 curr_rev, path); 107 curr_rev, path);
108 htmlf(")<br/>blob: %s\n", sha1_to_hex(sha1)); 108 html(")\n");
109
110 if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
111 htmlf("<div class='error'>blob size (%dKB) exceeds display size limit (%dKB).</div>",
112 size / 1024, ctx.cfg.max_blob_size);
113 return;
114 }
109 115
@@ -171,2 +177,4 @@ static int ls_item(const unsigned char *sha1, const char *base, int baselen,
171 fullpath); 177 fullpath);
178 cgit_plain_link("plain", NULL, "button", ctx.qry.head, curr_rev,
179 fullpath);
172 html("</td></tr>\n"); 180 html("</td></tr>\n");
@@ -219,3 +227,2 @@ static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
219 static char buffer[PATH_MAX]; 227 static char buffer[PATH_MAX];
220 char *url;
221 228
@@ -224,8 +231,2 @@ static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
224 strcpy(buffer+baselen, pathname); 231 strcpy(buffer+baselen, pathname);
225 url = cgit_pageurl(ctx.qry.repo, "tree",
226 fmt("h=%s&amp;path=%s", curr_rev, buffer));
227 html("/");
228 cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head,
229 curr_rev, buffer);
230
231 if (strcmp(match_path, buffer)) 232 if (strcmp(match_path, buffer))
@@ -272,6 +273,2 @@ void cgit_print_tree(const char *rev, char *path)
272 273
273 html("path: <a href='");
274 html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev)));
275 html("'>root</a>");
276
277 if (path == NULL) { 274 if (path == NULL) {