summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c9
-rw-r--r--cgit.css5
-rw-r--r--cgit.h4
-rw-r--r--cgitrc.5.txt14
-rw-r--r--cmd.c2
-rw-r--r--scan-tree.c2
-rw-r--r--shared.c9
-rw-r--r--ui-atom.c4
-rw-r--r--ui-commit.c13
-rw-r--r--ui-plain.c68
11 files changed, 115 insertions, 17 deletions
diff --git a/Makefile b/Makefile
index 0a5055b..3a4d974 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,13 @@
1CGIT_VERSION = v0.8.3.1 1CGIT_VERSION = v0.8.3.2
2CGIT_SCRIPT_NAME = cgit.cgi 2CGIT_SCRIPT_NAME = cgit.cgi
3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit 3CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH) 4CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
5CGIT_CONFIG = /etc/cgitrc 5CGIT_CONFIG = /etc/cgitrc
6CACHE_ROOT = /var/cache/cgit 6CACHE_ROOT = /var/cache/cgit
7SHA1_HEADER = <openssl/sha.h> 7SHA1_HEADER = <openssl/sha.h>
8GIT_VER = 1.7.0 8GIT_VER = 1.7.0
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
10INSTALL = install 10INSTALL = install
11 11
12# Define NO_STRCASESTR if you don't have strcasestr. 12# Define NO_STRCASESTR if you don't have strcasestr.
13# 13#
diff --git a/cgit.c b/cgit.c
index 2c3ad73..d4fcfa7 100644
--- a/cgit.c
+++ b/cgit.c
@@ -53,24 +53,26 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *value)
53 else if (!strcmp(name, "owner")) 53 else if (!strcmp(name, "owner"))
54 repo->owner = xstrdup(value); 54 repo->owner = xstrdup(value);
55 else if (!strcmp(name, "defbranch")) 55 else if (!strcmp(name, "defbranch"))
56 repo->defbranch = xstrdup(value); 56 repo->defbranch = xstrdup(value);
57 else if (!strcmp(name, "snapshots")) 57 else if (!strcmp(name, "snapshots"))
58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 58 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
59 else if (!strcmp(name, "enable-log-filecount")) 59 else if (!strcmp(name, "enable-log-filecount"))
60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 60 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
61 else if (!strcmp(name, "enable-log-linecount")) 61 else if (!strcmp(name, "enable-log-linecount"))
62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 62 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
63 else if (!strcmp(name, "enable-remote-branches")) 63 else if (!strcmp(name, "enable-remote-branches"))
64 repo->enable_remote_branches = atoi(value); 64 repo->enable_remote_branches = atoi(value);
65 else if (!strcmp(name, "enable-subject-links"))
66 repo->enable_subject_links = atoi(value);
65 else if (!strcmp(name, "max-stats")) 67 else if (!strcmp(name, "max-stats"))
66 repo->max_stats = cgit_find_stats_period(value, NULL); 68 repo->max_stats = cgit_find_stats_period(value, NULL);
67 else if (!strcmp(name, "module-link")) 69 else if (!strcmp(name, "module-link"))
68 repo->module_link= xstrdup(value); 70 repo->module_link= xstrdup(value);
69 else if (!strcmp(name, "section")) 71 else if (!strcmp(name, "section"))
70 repo->section = xstrdup(value); 72 repo->section = xstrdup(value);
71 else if (!strcmp(name, "readme") && value != NULL) { 73 else if (!strcmp(name, "readme") && value != NULL) {
72 if (*value == '/') 74 if (*value == '/')
73 repo->readme = xstrdup(value); 75 repo->readme = xstrdup(value);
74 else 76 else
75 repo->readme = xstrdup(fmt("%s/%s", repo->path, value)); 77 repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
76 } else if (ctx.cfg.enable_filter_overrides) { 78 } else if (ctx.cfg.enable_filter_overrides) {
@@ -132,48 +134,52 @@ void config_cb(const char *name, const char *value)
132 else if (!strcmp(name, "snapshots")) 134 else if (!strcmp(name, "snapshots"))
133 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 135 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
134 else if (!strcmp(name, "enable-filter-overrides")) 136 else if (!strcmp(name, "enable-filter-overrides"))
135 ctx.cfg.enable_filter_overrides = atoi(value); 137 ctx.cfg.enable_filter_overrides = atoi(value);
136 else if (!strcmp(name, "enable-index-links")) 138 else if (!strcmp(name, "enable-index-links"))
137 ctx.cfg.enable_index_links = atoi(value); 139 ctx.cfg.enable_index_links = atoi(value);
138 else if (!strcmp(name, "enable-log-filecount")) 140 else if (!strcmp(name, "enable-log-filecount"))
139 ctx.cfg.enable_log_filecount = atoi(value); 141 ctx.cfg.enable_log_filecount = atoi(value);
140 else if (!strcmp(name, "enable-log-linecount")) 142 else if (!strcmp(name, "enable-log-linecount"))
141 ctx.cfg.enable_log_linecount = atoi(value); 143 ctx.cfg.enable_log_linecount = atoi(value);
142 else if (!strcmp(name, "enable-remote-branches")) 144 else if (!strcmp(name, "enable-remote-branches"))
143 ctx.cfg.enable_remote_branches = atoi(value); 145 ctx.cfg.enable_remote_branches = atoi(value);
146 else if (!strcmp(name, "enable-subject-links"))
147 ctx.cfg.enable_subject_links = atoi(value);
144 else if (!strcmp(name, "enable-tree-linenumbers")) 148 else if (!strcmp(name, "enable-tree-linenumbers"))
145 ctx.cfg.enable_tree_linenumbers = atoi(value); 149 ctx.cfg.enable_tree_linenumbers = atoi(value);
146 else if (!strcmp(name, "max-stats")) 150 else if (!strcmp(name, "max-stats"))
147 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 151 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
148 else if (!strcmp(name, "cache-size")) 152 else if (!strcmp(name, "cache-size"))
149 ctx.cfg.cache_size = atoi(value); 153 ctx.cfg.cache_size = atoi(value);
150 else if (!strcmp(name, "cache-root")) 154 else if (!strcmp(name, "cache-root"))
151 ctx.cfg.cache_root = xstrdup(value); 155 ctx.cfg.cache_root = xstrdup(value);
152 else if (!strcmp(name, "cache-root-ttl")) 156 else if (!strcmp(name, "cache-root-ttl"))
153 ctx.cfg.cache_root_ttl = atoi(value); 157 ctx.cfg.cache_root_ttl = atoi(value);
154 else if (!strcmp(name, "cache-repo-ttl")) 158 else if (!strcmp(name, "cache-repo-ttl"))
155 ctx.cfg.cache_repo_ttl = atoi(value); 159 ctx.cfg.cache_repo_ttl = atoi(value);
156 else if (!strcmp(name, "cache-scanrc-ttl")) 160 else if (!strcmp(name, "cache-scanrc-ttl"))
157 ctx.cfg.cache_scanrc_ttl = atoi(value); 161 ctx.cfg.cache_scanrc_ttl = atoi(value);
158 else if (!strcmp(name, "cache-static-ttl")) 162 else if (!strcmp(name, "cache-static-ttl"))
159 ctx.cfg.cache_static_ttl = atoi(value); 163 ctx.cfg.cache_static_ttl = atoi(value);
160 else if (!strcmp(name, "cache-dynamic-ttl")) 164 else if (!strcmp(name, "cache-dynamic-ttl"))
161 ctx.cfg.cache_dynamic_ttl = atoi(value); 165 ctx.cfg.cache_dynamic_ttl = atoi(value);
162 else if (!strcmp(name, "about-filter")) 166 else if (!strcmp(name, "about-filter"))
163 ctx.cfg.about_filter = new_filter(value, 0); 167 ctx.cfg.about_filter = new_filter(value, 0);
164 else if (!strcmp(name, "commit-filter")) 168 else if (!strcmp(name, "commit-filter"))
165 ctx.cfg.commit_filter = new_filter(value, 0); 169 ctx.cfg.commit_filter = new_filter(value, 0);
166 else if (!strcmp(name, "embedded")) 170 else if (!strcmp(name, "embedded"))
167 ctx.cfg.embedded = atoi(value); 171 ctx.cfg.embedded = atoi(value);
172 else if (!strcmp(name, "max-atom-items"))
173 ctx.cfg.max_atom_items = atoi(value);
168 else if (!strcmp(name, "max-message-length")) 174 else if (!strcmp(name, "max-message-length"))
169 ctx.cfg.max_msg_len = atoi(value); 175 ctx.cfg.max_msg_len = atoi(value);
170 else if (!strcmp(name, "max-repodesc-length")) 176 else if (!strcmp(name, "max-repodesc-length"))
171 ctx.cfg.max_repodesc_len = atoi(value); 177 ctx.cfg.max_repodesc_len = atoi(value);
172 else if (!strcmp(name, "max-blob-size")) 178 else if (!strcmp(name, "max-blob-size"))
173 ctx.cfg.max_blob_size = atoi(value); 179 ctx.cfg.max_blob_size = atoi(value);
174 else if (!strcmp(name, "max-repo-count")) 180 else if (!strcmp(name, "max-repo-count"))
175 ctx.cfg.max_repo_count = atoi(value); 181 ctx.cfg.max_repo_count = atoi(value);
176 else if (!strcmp(name, "max-commit-count")) 182 else if (!strcmp(name, "max-commit-count"))
177 ctx.cfg.max_commit_count = atoi(value); 183 ctx.cfg.max_commit_count = atoi(value);
178 else if (!strcmp(name, "scan-path")) 184 else if (!strcmp(name, "scan-path"))
179 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 185 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
@@ -241,24 +247,26 @@ static void querystring_cb(const char *name, const char *value)
241 } else if (!strcmp(name, "name")) { 247 } else if (!strcmp(name, "name")) {
242 ctx.qry.name = xstrdup(value); 248 ctx.qry.name = xstrdup(value);
243 } else if (!strcmp(name, "mimetype")) { 249 } else if (!strcmp(name, "mimetype")) {
244 ctx.qry.mimetype = xstrdup(value); 250 ctx.qry.mimetype = xstrdup(value);
245 } else if (!strcmp(name, "s")){ 251 } else if (!strcmp(name, "s")){
246 ctx.qry.sort = xstrdup(value); 252 ctx.qry.sort = xstrdup(value);
247 } else if (!strcmp(name, "showmsg")) { 253 } else if (!strcmp(name, "showmsg")) {
248 ctx.qry.showmsg = atoi(value); 254 ctx.qry.showmsg = atoi(value);
249 } else if (!strcmp(name, "period")) { 255 } else if (!strcmp(name, "period")) {
250 ctx.qry.period = xstrdup(value); 256 ctx.qry.period = xstrdup(value);
251 } else if (!strcmp(name, "ss")) { 257 } else if (!strcmp(name, "ss")) {
252 ctx.qry.ssdiff = atoi(value); 258 ctx.qry.ssdiff = atoi(value);
259 } else if (!strcmp(name, "all")) {
260 ctx.qry.show_all = atoi(value);
253 } 261 }
254} 262}
255 263
256char *xstrdupn(const char *str) 264char *xstrdupn(const char *str)
257{ 265{
258 return (str ? xstrdup(str) : NULL); 266 return (str ? xstrdup(str) : NULL);
259} 267}
260 268
261static void prepare_context(struct cgit_context *ctx) 269static void prepare_context(struct cgit_context *ctx)
262{ 270{
263 memset(ctx, 0, sizeof(*ctx)); 271 memset(ctx, 0, sizeof(*ctx));
264 ctx->cfg.agefile = "info/web/last-modified"; 272 ctx->cfg.agefile = "info/web/last-modified";
@@ -283,24 +291,25 @@ static void prepare_context(struct cgit_context *ctx)
283 ctx->cfg.max_blob_size = 0; 291 ctx->cfg.max_blob_size = 0;
284 ctx->cfg.max_stats = 0; 292 ctx->cfg.max_stats = 0;
285 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 293 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
286 ctx->cfg.renamelimit = -1; 294 ctx->cfg.renamelimit = -1;
287 ctx->cfg.robots = "index, nofollow"; 295 ctx->cfg.robots = "index, nofollow";
288 ctx->cfg.root_title = "Git repository browser"; 296 ctx->cfg.root_title = "Git repository browser";
289 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 297 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
290 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 298 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
291 ctx->cfg.section = ""; 299 ctx->cfg.section = "";
292 ctx->cfg.summary_branches = 10; 300 ctx->cfg.summary_branches = 10;
293 ctx->cfg.summary_log = 10; 301 ctx->cfg.summary_log = 10;
294 ctx->cfg.summary_tags = 10; 302 ctx->cfg.summary_tags = 10;
303 ctx->cfg.max_atom_items = 10;
295 ctx->cfg.ssdiff = 0; 304 ctx->cfg.ssdiff = 0;
296 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 305 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
297 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 306 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
298 ctx->env.https = xstrdupn(getenv("HTTPS")); 307 ctx->env.https = xstrdupn(getenv("HTTPS"));
299 ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); 308 ctx->env.no_http = xstrdupn(getenv("NO_HTTP"));
300 ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); 309 ctx->env.path_info = xstrdupn(getenv("PATH_INFO"));
301 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); 310 ctx->env.query_string = xstrdupn(getenv("QUERY_STRING"));
302 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); 311 ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD"));
303 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); 312 ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME"));
304 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); 313 ctx->env.server_name = xstrdupn(getenv("SERVER_NAME"));
305 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); 314 ctx->env.server_port = xstrdupn(getenv("SERVER_PORT"));
306 ctx->page.mimetype = "text/html"; 315 ctx->page.mimetype = "text/html";
diff --git a/cgit.css b/cgit.css
index 28a2eeb..6e47eb3 100644
--- a/cgit.css
+++ b/cgit.css
@@ -522,25 +522,28 @@ a.remote-deco {
522 margin: 0px 0.5em; 522 margin: 0px 0.5em;
523 padding: 0px 0.25em; 523 padding: 0px 0.25em;
524 background-color: #ccccff; 524 background-color: #ccccff;
525 border: solid 1px #000077; 525 border: solid 1px #000077;
526} 526}
527a.deco { 527a.deco {
528 margin: 0px 0.5em; 528 margin: 0px 0.5em;
529 padding: 0px 0.25em; 529 padding: 0px 0.25em;
530 background-color: #ff8888; 530 background-color: #ff8888;
531 border: solid 1px #770000; 531 border: solid 1px #770000;
532} 532}
533 533
534div.commit-subject a { 534div.commit-subject a.branch-deco,
535div.commit-subject a.tag-deco,
536div.commit-subject a.remote-deco,
537div.commit-subject a.deco {
535 margin-left: 1em; 538 margin-left: 1em;
536 font-size: 75%; 539 font-size: 75%;
537} 540}
538 541
539table.stats { 542table.stats {
540 border: solid 1px black; 543 border: solid 1px black;
541 border-collapse: collapse; 544 border-collapse: collapse;
542} 545}
543 546
544table.stats th { 547table.stats th {
545 text-align: left; 548 text-align: left;
546 padding: 1px 0.5em; 549 padding: 1px 0.5em;
diff --git a/cgit.h b/cgit.h
index f990b15..80c3902 100644
--- a/cgit.h
+++ b/cgit.h
@@ -64,24 +64,25 @@ struct cgit_repo {
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *module_link; 68 char *module_link;
69 char *readme; 69 char *readme;
70 char *section; 70 char *section;
71 char *clone_url; 71 char *clone_url;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75 int enable_remote_branches; 75 int enable_remote_branches;
76 int enable_subject_links;
76 int max_stats; 77 int max_stats;
77 time_t mtime; 78 time_t mtime;
78 struct cgit_filter *about_filter; 79 struct cgit_filter *about_filter;
79 struct cgit_filter *commit_filter; 80 struct cgit_filter *commit_filter;
80 struct cgit_filter *source_filter; 81 struct cgit_filter *source_filter;
81}; 82};
82 83
83typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name, 84typedef void (*repo_config_fn)(struct cgit_repo *repo, const char *name,
84 const char *value); 85 const char *value);
85 86
86struct cgit_repolist { 87struct cgit_repolist {
87 int length; 88 int length;
@@ -136,24 +137,25 @@ struct cgit_query {
136 char *sha1; 137 char *sha1;
137 char *sha2; 138 char *sha2;
138 char *path; 139 char *path;
139 char *name; 140 char *name;
140 char *mimetype; 141 char *mimetype;
141 char *url; 142 char *url;
142 char *period; 143 char *period;
143 int ofs; 144 int ofs;
144 int nohead; 145 int nohead;
145 char *sort; 146 char *sort;
146 int showmsg; 147 int showmsg;
147 int ssdiff; 148 int ssdiff;
149 int show_all;
148 char *vpath; 150 char *vpath;
149}; 151};
150 152
151struct cgit_config { 153struct cgit_config {
152 char *agefile; 154 char *agefile;
153 char *cache_root; 155 char *cache_root;
154 char *clone_prefix; 156 char *clone_prefix;
155 char *css; 157 char *css;
156 char *favicon; 158 char *favicon;
157 char *footer; 159 char *footer;
158 char *head_include; 160 char *head_include;
159 char *header; 161 char *header;
@@ -173,26 +175,28 @@ struct cgit_config {
173 int cache_dynamic_ttl; 175 int cache_dynamic_ttl;
174 int cache_max_create_time; 176 int cache_max_create_time;
175 int cache_repo_ttl; 177 int cache_repo_ttl;
176 int cache_root_ttl; 178 int cache_root_ttl;
177 int cache_scanrc_ttl; 179 int cache_scanrc_ttl;
178 int cache_static_ttl; 180 int cache_static_ttl;
179 int embedded; 181 int embedded;
180 int enable_filter_overrides; 182 int enable_filter_overrides;
181 int enable_index_links; 183 int enable_index_links;
182 int enable_log_filecount; 184 int enable_log_filecount;
183 int enable_log_linecount; 185 int enable_log_linecount;
184 int enable_remote_branches; 186 int enable_remote_branches;
187 int enable_subject_links;
185 int enable_tree_linenumbers; 188 int enable_tree_linenumbers;
186 int local_time; 189 int local_time;
190 int max_atom_items;
187 int max_repo_count; 191 int max_repo_count;
188 int max_commit_count; 192 int max_commit_count;
189 int max_lock_attempts; 193 int max_lock_attempts;
190 int max_msg_len; 194 int max_msg_len;
191 int max_repodesc_len; 195 int max_repodesc_len;
192 int max_blob_size; 196 int max_blob_size;
193 int max_stats; 197 int max_stats;
194 int nocache; 198 int nocache;
195 int noplainemail; 199 int noplainemail;
196 int noheader; 200 int noheader;
197 int renamelimit; 201 int renamelimit;
198 int snapshots; 202 int snapshots;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 1f7ac1e..a853522 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -106,24 +106,30 @@ enable-log-filecount::
106 value: "0". 106 value: "0".
107 107
108enable-log-linecount:: 108enable-log-linecount::
109 Flag which, when set to "1", will make cgit print the number of added 109 Flag which, when set to "1", will make cgit print the number of added
110 and removed lines for each commit on the repository log page. Default 110 and removed lines for each commit on the repository log page. Default
111 value: "0". 111 value: "0".
112 112
113enable-remote-branches:: 113enable-remote-branches::
114 Flag which, when set to "1", will make cgit display remote branches 114 Flag which, when set to "1", will make cgit display remote branches
115 in the summary and refs views. Default value: "0". See also: 115 in the summary and refs views. Default value: "0". See also:
116 "repo.enable-remote-branches". 116 "repo.enable-remote-branches".
117 117
118enable-subject-links::
119 Flag which, when set to "1", will make cgit use the subject of the
120 parent commit as link text when generating links to parent commits
121 in commit view. Default value: "0". See also:
122 "repo.enable-subject-links".
123
118enable-tree-linenumbers:: 124enable-tree-linenumbers::
119 Flag which, when set to "1", will make cgit generate linenumber links 125 Flag which, when set to "1", will make cgit generate linenumber links
120 for plaintext blobs printed in the tree view. Default value: "1". 126 for plaintext blobs printed in the tree view. Default value: "1".
121 127
122favicon:: 128favicon::
123 Url used as link to a shortcut icon for cgit. If specified, it is 129 Url used as link to a shortcut icon for cgit. If specified, it is
124 suggested to use the value "/favicon.ico" since certain browsers will 130 suggested to use the value "/favicon.ico" since certain browsers will
125 ignore other values. Default value: none. 131 ignore other values. Default value: none.
126 132
127footer:: 133footer::
128 The content of the file specified with this option will be included 134 The content of the file specified with this option will be included
129 verbatim at the bottom of all pages (i.e. it replaces the standard 135 verbatim at the bottom of all pages (i.e. it replaces the standard
@@ -157,24 +163,28 @@ local-time::
157 Flag which, if set to "1", makes cgit print commit and tag times in the 163 Flag which, if set to "1", makes cgit print commit and tag times in the
158 servers timezone. Default value: "0". 164 servers timezone. Default value: "0".
159 165
160logo:: 166logo::
161 Url which specifies the source of an image which will be used as a logo 167 Url which specifies the source of an image which will be used as a logo
162 on all cgit pages. Default value: "/cgit.png". 168 on all cgit pages. Default value: "/cgit.png".
163 169
164logo-link:: 170logo-link::
165 Url loaded when clicking on the cgit logo image. If unspecified the 171 Url loaded when clicking on the cgit logo image. If unspecified the
166 calculated url of the repository index page will be used. Default 172 calculated url of the repository index page will be used. Default
167 value: none. 173 value: none.
168 174
175max-atom-items::
176 Specifies the number of items to display in atom feeds view. Default
177 value: "10".
178
169max-commit-count:: 179max-commit-count::
170 Specifies the number of entries to list per page in "log" view. Default 180 Specifies the number of entries to list per page in "log" view. Default
171 value: "50". 181 value: "50".
172 182
173max-message-length:: 183max-message-length::
174 Specifies the maximum number of commit message characters to display in 184 Specifies the maximum number of commit message characters to display in
175 "log" view. Default value: "80". 185 "log" view. Default value: "80".
176 186
177max-repo-count:: 187max-repo-count::
178 Specifies the number of entries to list per page on therepository 188 Specifies the number of entries to list per page on therepository
179 index page. Default value: "50". 189 index page. Default value: "50".
180 190
@@ -312,24 +322,28 @@ repo.desc::
312repo.enable-log-filecount:: 322repo.enable-log-filecount::
313 A flag which can be used to disable the global setting 323 A flag which can be used to disable the global setting
314 `enable-log-filecount'. Default value: none. 324 `enable-log-filecount'. Default value: none.
315 325
316repo.enable-log-linecount:: 326repo.enable-log-linecount::
317 A flag which can be used to disable the global setting 327 A flag which can be used to disable the global setting
318 `enable-log-linecount'. Default value: none. 328 `enable-log-linecount'. Default value: none.
319 329
320repo.enable-remote-branches:: 330repo.enable-remote-branches::
321 Flag which, when set to "1", will make cgit display remote branches 331 Flag which, when set to "1", will make cgit display remote branches
322 in the summary and refs views. Default value: <enable-remote-branches>. 332 in the summary and refs views. Default value: <enable-remote-branches>.
323 333
334repo.enable-subject-links::
335 A flag which can be used to override the global setting
336 `enable-subject-links'. Default value: none.
337
324repo.max-stats:: 338repo.max-stats::
325 Override the default maximum statistics period. Valid values are equal 339 Override the default maximum statistics period. Valid values are equal
326 to the values specified for the global "max-stats" setting. Default 340 to the values specified for the global "max-stats" setting. Default
327 value: none. 341 value: none.
328 342
329repo.name:: 343repo.name::
330 The value to show as repository name. Default value: <repo.url>. 344 The value to show as repository name. Default value: <repo.url>.
331 345
332repo.owner:: 346repo.owner::
333 A value used to identify the owner of the repository. Default value: 347 A value used to identify the owner of the repository. Default value:
334 none. 348 none.
335 349
diff --git a/cmd.c b/cmd.c
index 605876b..6dc9f5e 100644
--- a/cmd.c
+++ b/cmd.c
@@ -24,25 +24,25 @@
24#include "ui-stats.h" 24#include "ui-stats.h"
25#include "ui-summary.h" 25#include "ui-summary.h"
26#include "ui-tag.h" 26#include "ui-tag.h"
27#include "ui-tree.h" 27#include "ui-tree.h"
28 28
29static void HEAD_fn(struct cgit_context *ctx) 29static void HEAD_fn(struct cgit_context *ctx)
30{ 30{
31 cgit_clone_head(ctx); 31 cgit_clone_head(ctx);
32} 32}
33 33
34static void atom_fn(struct cgit_context *ctx) 34static 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}
38 38
39static void about_fn(struct cgit_context *ctx) 39static void about_fn(struct cgit_context *ctx)
40{ 40{
41 if (ctx->repo) 41 if (ctx->repo)
42 cgit_print_repo_readme(ctx->qry.path); 42 cgit_print_repo_readme(ctx->qry.path);
43 else 43 else
44 cgit_print_site_readme(); 44 cgit_print_site_readme();
45} 45}
46 46
47static void blob_fn(struct cgit_context *ctx) 47static void blob_fn(struct cgit_context *ctx)
48{ 48{
diff --git a/scan-tree.c b/scan-tree.c
index dbca797..1e18f3c 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -47,24 +47,26 @@ static void repo_config(const char *name, const char *value)
47static void add_repo(const char *base, const char *path, repo_config_fn fn) 47static void add_repo(const char *base, const char *path, repo_config_fn fn)
48{ 48{
49 struct stat st; 49 struct stat st;
50 struct passwd *pwd; 50 struct passwd *pwd;
51 char *p; 51 char *p;
52 size_t size; 52 size_t size;
53 53
54 if (stat(path, &st)) { 54 if (stat(path, &st)) {
55 fprintf(stderr, "Error accessing %s: %s (%d)\n", 55 fprintf(stderr, "Error accessing %s: %s (%d)\n",
56 path, strerror(errno), errno); 56 path, strerror(errno), errno);
57 return; 57 return;
58 } 58 }
59 if (!stat(fmt("%s/noweb", path), &st))
60 return;
59 if ((pwd = getpwuid(st.st_uid)) == NULL) { 61 if ((pwd = getpwuid(st.st_uid)) == NULL) {
60 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 62 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
61 path, strerror(errno), errno); 63 path, strerror(errno), errno);
62 return; 64 return;
63 } 65 }
64 if (base == path) 66 if (base == path)
65 p = fmt("%s", path); 67 p = fmt("%s", path);
66 else 68 else
67 p = fmt("%s", path + strlen(base) + 1); 69 p = fmt("%s", path + strlen(base) + 1);
68 70
69 if (!strcmp(p + strlen(p) - 5, "/.git")) 71 if (!strcmp(p + strlen(p) - 5, "/.git"))
70 p[strlen(p) - 5] = '\0'; 72 p[strlen(p) - 5] = '\0';
diff --git a/shared.c b/shared.c
index 6e8f0ce..58837dc 100644
--- a/shared.c
+++ b/shared.c
@@ -50,24 +50,25 @@ struct cgit_repo *cgit_add_repo(const char *url)
50 memset(ret, 0, sizeof(struct cgit_repo)); 50 memset(ret, 0, sizeof(struct cgit_repo));
51 ret->url = trim_end(url, '/'); 51 ret->url = trim_end(url, '/');
52 ret->name = ret->url; 52 ret->name = ret->url;
53 ret->path = NULL; 53 ret->path = NULL;
54 ret->desc = "[no description]"; 54 ret->desc = "[no description]";
55 ret->owner = NULL; 55 ret->owner = NULL;
56 ret->section = ctx.cfg.section; 56 ret->section = ctx.cfg.section;
57 ret->defbranch = "master"; 57 ret->defbranch = "master";
58 ret->snapshots = ctx.cfg.snapshots; 58 ret->snapshots = ctx.cfg.snapshots;
59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->enable_remote_branches = ctx.cfg.enable_remote_branches; 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;
63 ret->module_link = ctx.cfg.module_link; 64 ret->module_link = ctx.cfg.module_link;
64 ret->readme = NULL; 65 ret->readme = NULL;
65 ret->mtime = -1; 66 ret->mtime = -1;
66 ret->about_filter = ctx.cfg.about_filter; 67 ret->about_filter = ctx.cfg.about_filter;
67 ret->commit_filter = ctx.cfg.commit_filter; 68 ret->commit_filter = ctx.cfg.commit_filter;
68 ret->source_filter = ctx.cfg.source_filter; 69 ret->source_filter = ctx.cfg.source_filter;
69 return ret; 70 return ret;
70} 71}
71 72
72struct cgit_repo *cgit_get_repoinfo(const char *url) 73struct cgit_repo *cgit_get_repoinfo(const char *url)
73{ 74{
@@ -269,36 +270,44 @@ int cgit_diff_files(const unsigned char *old_sha1,
269 xdemitconf_t emit_params; 270 xdemitconf_t emit_params;
270 xdemitcb_t emit_cb; 271 xdemitcb_t emit_cb;
271 272
272 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 273 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
273 return 1; 274 return 1;
274 275
275 *old_size = file1.size; 276 *old_size = file1.size;
276 *new_size = file2.size; 277 *new_size = file2.size;
277 278
278 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || 279 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
279 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { 280 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
280 *binary = 1; 281 *binary = 1;
282 if (file1.size)
283 free(file1.ptr);
284 if (file2.size)
285 free(file2.ptr);
281 return 0; 286 return 0;
282 } 287 }
283 288
284 memset(&diff_params, 0, sizeof(diff_params)); 289 memset(&diff_params, 0, sizeof(diff_params));
285 memset(&emit_params, 0, sizeof(emit_params)); 290 memset(&emit_params, 0, sizeof(emit_params));
286 memset(&emit_cb, 0, sizeof(emit_cb)); 291 memset(&emit_cb, 0, sizeof(emit_cb));
287 diff_params.flags = XDF_NEED_MINIMAL; 292 diff_params.flags = XDF_NEED_MINIMAL;
288 emit_params.ctxlen = 3; 293 emit_params.ctxlen = 3;
289 emit_params.flags = XDL_EMIT_FUNCNAMES; 294 emit_params.flags = XDL_EMIT_FUNCNAMES;
290 emit_cb.outf = filediff_cb; 295 emit_cb.outf = filediff_cb;
291 emit_cb.priv = fn; 296 emit_cb.priv = fn;
292 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 297 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
298 if (file1.size)
299 free(file1.ptr);
300 if (file2.size)
301 free(file2.ptr);
293 return 0; 302 return 0;
294} 303}
295 304
296void cgit_diff_tree(const unsigned char *old_sha1, 305void cgit_diff_tree(const unsigned char *old_sha1,
297 const unsigned char *new_sha1, 306 const unsigned char *new_sha1,
298 filepair_fn fn, const char *prefix) 307 filepair_fn fn, const char *prefix)
299{ 308{
300 struct diff_options opt; 309 struct diff_options opt;
301 int ret; 310 int ret;
302 int prefixlen; 311 int prefixlen;
303 312
304 diff_setup(&opt); 313 diff_setup(&opt);
diff --git a/ui-atom.c b/ui-atom.c
index 808b2d0..9f049ae 100644
--- a/ui-atom.c
+++ b/ui-atom.c
@@ -76,25 +76,27 @@ void add_entry(struct commit *commit, char *host)
76 cgit_free_commitinfo(info); 76 cgit_free_commitinfo(info);
77} 77}
78 78
79 79
80void cgit_print_atom(char *tip, char *path, int max_count) 80void cgit_print_atom(char *tip, char *path, int max_count)
81{ 81{
82 char *host; 82 char *host;
83 const char *argv[] = {NULL, tip, NULL, NULL, NULL}; 83 const char *argv[] = {NULL, tip, NULL, NULL, NULL};
84 struct commit *commit; 84 struct commit *commit;
85 struct rev_info rev; 85 struct rev_info rev;
86 int argc = 2; 86 int argc = 2;
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;
90 92
91 if (path) { 93 if (path) {
92 argv[argc++] = "--"; 94 argv[argc++] = "--";
93 argv[argc++] = path; 95 argv[argc++] = path;
94 } 96 }
95 97
96 init_revisions(&rev, NULL); 98 init_revisions(&rev, NULL);
97 rev.abbrev = DEFAULT_ABBREV; 99 rev.abbrev = DEFAULT_ABBREV;
98 rev.commit_format = CMIT_FMT_DEFAULT; 100 rev.commit_format = CMIT_FMT_DEFAULT;
99 rev.verbose_header = 1; 101 rev.verbose_header = 1;
100 rev.show_root_diff = 0; 102 rev.show_root_diff = 0;
diff --git a/ui-commit.c b/ui-commit.c
index 2d98ed9..a11bc5f 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -6,28 +6,28 @@
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12#include "ui-diff.h" 12#include "ui-diff.h"
13#include "ui-log.h" 13#include "ui-log.h"
14 14
15void cgit_print_commit(char *hex, const char *prefix) 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 unsigned char sha1[20]; 20 unsigned char sha1[20];
21 char *tmp; 21 char *tmp, *tmp2;
22 int parents = 0; 22 int parents = 0;
23 23
24 if (!hex) 24 if (!hex)
25 hex = ctx.qry.head; 25 hex = ctx.qry.head;
26 26
27 if (get_sha1(hex, sha1)) { 27 if (get_sha1(hex, sha1)) {
28 cgit_print_error(fmt("Bad object id: %s", hex)); 28 cgit_print_error(fmt("Bad object id: %s", hex));
29 return; 29 return;
30 } 30 }
31 commit = lookup_commit_reference(sha1); 31 commit = lookup_commit_reference(sha1);
32 if (!commit) { 32 if (!commit) {
33 cgit_print_error(fmt("Bad commit reference: %s", hex)); 33 cgit_print_error(fmt("Bad commit reference: %s", hex));
@@ -77,27 +77,30 @@ void cgit_print_commit(char *hex, const char *prefix)
77 } 77 }
78 html("</td></tr>\n"); 78 html("</td></tr>\n");
79 for (p = commit->parents; p ; p = p->next) { 79 for (p = commit->parents; p ; p = p->next) {
80 parent = lookup_commit_reference(p->item->object.sha1); 80 parent = lookup_commit_reference(p->item->object.sha1);
81 if (!parent) { 81 if (!parent) {
82 html("<tr><td colspan='3'>"); 82 html("<tr><td colspan='3'>");
83 cgit_print_error("Error reading parent commit"); 83 cgit_print_error("Error reading parent commit");
84 html("</td></tr>"); 84 html("</td></tr>");
85 continue; 85 continue;
86 } 86 }
87 html("<tr><th>parent</th>" 87 html("<tr><th>parent</th>"
88 "<td colspan='2' class='sha1'>"); 88 "<td colspan='2' class='sha1'>");
89 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 89 tmp = tmp2 = sha1_to_hex(p->item->object.sha1);
90 ctx.qry.head, 90 if (ctx.repo->enable_subject_links) {
91 sha1_to_hex(p->item->object.sha1), prefix, 0); 91 parent_info = cgit_parse_commit(parent);
92 tmp2 = parent_info->subject;
93 }
94 cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix, 0);
92 html(" ("); 95 html(" (");
93 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, 96 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
94 sha1_to_hex(p->item->object.sha1), prefix, 0); 97 sha1_to_hex(p->item->object.sha1), prefix, 0);
95 html(")</td></tr>"); 98 html(")</td></tr>");
96 parents++; 99 parents++;
97 } 100 }
98 if (ctx.repo->snapshots) { 101 if (ctx.repo->snapshots) {
99 html("<tr><th>download</th><td colspan='2' class='sha1'>"); 102 html("<tr><th>download</th><td colspan='2' class='sha1'>");
100 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, 103 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
101 hex, ctx.repo->snapshots); 104 hex, ctx.repo->snapshots);
102 html("</td></tr>"); 105 html("</td></tr>");
103 } 106 }
diff --git a/ui-plain.c b/ui-plain.c
index 66cb19c..da76406 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -1,26 +1,25 @@
1/* ui-plain.c: functions for output of plain blobs by path 1/* ui-plain.c: functions for output of plain blobs by path
2 * 2 *
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13char *curr_rev; 13int match_baselen;
14char *match_path;
15int match; 14int match;
16 15
17static void print_object(const unsigned char *sha1, const char *path) 16static void print_object(const unsigned char *sha1, const char *path)
18{ 17{
19 enum object_type type; 18 enum object_type type;
20 char *buf, *ext; 19 char *buf, *ext;
21 unsigned long size; 20 unsigned long size;
22 struct string_list_item *mime; 21 struct string_list_item *mime;
23 22
24 type = sha1_object_info(sha1, &size); 23 type = sha1_object_info(sha1, &size);
25 if (type == OBJ_BAD) { 24 if (type == OBJ_BAD) {
26 html_status(404, "Not found", 0); 25 html_status(404, "Not found", 0);
@@ -44,51 +43,104 @@ static void print_object(const unsigned char *sha1, const char *path)
44 ctx.page.mimetype = "application/octet-stream"; 43 ctx.page.mimetype = "application/octet-stream";
45 else 44 else
46 ctx.page.mimetype = "text/plain"; 45 ctx.page.mimetype = "text/plain";
47 } 46 }
48 ctx.page.filename = fmt("%s", path); 47 ctx.page.filename = fmt("%s", path);
49 ctx.page.size = size; 48 ctx.page.size = size;
50 ctx.page.etag = sha1_to_hex(sha1); 49 ctx.page.etag = sha1_to_hex(sha1);
51 cgit_print_http_headers(&ctx); 50 cgit_print_http_headers(&ctx);
52 html_raw(buf, size); 51 html_raw(buf, size);
53 match = 1; 52 match = 1;
54} 53}
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,
57 const char *pathname, unsigned mode, int stage, 88 const char *pathname, unsigned mode, int stage,
58 void *cbdata) 89 void *cbdata)
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;
68} 113}
69 114
70void cgit_print_plain(struct cgit_context *ctx) 115void cgit_print_plain(struct cgit_context *ctx)
71{ 116{
72 const char *rev = ctx->qry.sha1; 117 const char *rev = ctx->qry.sha1;
73 unsigned char sha1[20]; 118 unsigned char sha1[20];
74 struct commit *commit; 119 struct commit *commit;
75 const char *paths[] = {ctx->qry.path, NULL}; 120 const char *paths[] = {ctx->qry.path, NULL};
76 121
77 if (!rev) 122 if (!rev)
78 rev = ctx->qry.head; 123 rev = ctx->qry.head;
79 124
80 curr_rev = xstrdup(rev);
81 if (get_sha1(rev, sha1)) { 125 if (get_sha1(rev, sha1)) {
82 html_status(404, "Not found", 0); 126 html_status(404, "Not found", 0);
83 return; 127 return;
84 } 128 }
85 commit = lookup_commit_reference(sha1); 129 commit = lookup_commit_reference(sha1);
86 if (!commit || parse_commit(commit)) { 130 if (!commit || parse_commit(commit)) {
87 html_status(404, "Not found", 0); 131 html_status(404, "Not found", 0);
88 return; 132 return;
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);
92 if (!match) 142 if (!match)
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}