summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--Makefile2
-rw-r--r--cgit.c19
-rw-r--r--cgit.css5
-rw-r--r--cgit.h6
-rw-r--r--cgitrc.5.txt14
-rw-r--r--cmd.c2
-rw-r--r--parsing.c4
-rw-r--r--scan-tree.c2
-rw-r--r--shared.c80
-rw-r--r--ui-atom.c4
-rw-r--r--ui-commit.c13
-rw-r--r--ui-log.c11
-rw-r--r--ui-plain.c68
-rw-r--r--ui-shared.c1
14 files changed, 205 insertions, 26 deletions
diff --git a/Makefile b/Makefile
index 0a5055b..3a4d974 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
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
diff --git a/cgit.c b/cgit.c
index 9452884..c263872 100644
--- a/cgit.c
+++ b/cgit.c
@@ -61,8 +61,10 @@ void repo_config(struct cgit_repo *repo, const char *name, const char *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);
@@ -140,16 +142,18 @@ void config_cb(const char *name, const char *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(expand_macros(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);
@@ -164,8 +168,10 @@ void config_cb(const char *name, const char *value)
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);
@@ -176,11 +182,11 @@ void config_cb(const char *name, const char *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)
180 process_cached_repolist(value); 186 process_cached_repolist(expand_macros(value));
181 else 187 else
182 scan_tree(value, repo_config); 188 scan_tree(expand_macros(value), repo_config);
183 else if (!strcmp(name, "source-filter")) 189 else if (!strcmp(name, "source-filter"))
184 ctx.cfg.source_filter = new_filter(value, 1); 190 ctx.cfg.source_filter = new_filter(value, 1);
185 else if (!strcmp(name, "summary-log")) 191 else if (!strcmp(name, "summary-log"))
186 ctx.cfg.summary_log = atoi(value); 192 ctx.cfg.summary_log = atoi(value);
@@ -202,9 +208,9 @@ void config_cb(const char *name, const char *value)
202 ctx.cfg.local_time = atoi(value); 208 ctx.cfg.local_time = atoi(value);
203 else if (!prefixcmp(name, "mimetype.")) 209 else if (!prefixcmp(name, "mimetype."))
204 add_mimetype(name + 9, value); 210 add_mimetype(name + 9, value);
205 else if (!strcmp(name, "include")) 211 else if (!strcmp(name, "include"))
206 parse_configfile(value, config_cb); 212 parse_configfile(expand_macros(value), config_cb);
207} 213}
208 214
209static void querystring_cb(const char *name, const char *value) 215static void querystring_cb(const char *name, const char *value)
210{ 216{
@@ -249,8 +255,10 @@ static void querystring_cb(const char *name, const char *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 } else if (!strcmp(name, "context")) { 261 } else if (!strcmp(name, "context")) {
254 ctx.qry.context = atoi(value); 262 ctx.qry.context = atoi(value);
255 } else if (!strcmp(name, "ignorews")) { 263 } else if (!strcmp(name, "ignorews")) {
256 ctx.qry.ignorews = atoi(value); 264 ctx.qry.ignorews = atoi(value);
@@ -295,8 +303,9 @@ static void prepare_context(struct cgit_context *ctx)
295 ctx->cfg.section = ""; 303 ctx->cfg.section = "";
296 ctx->cfg.summary_branches = 10; 304 ctx->cfg.summary_branches = 10;
297 ctx->cfg.summary_log = 10; 305 ctx->cfg.summary_log = 10;
298 ctx->cfg.summary_tags = 10; 306 ctx->cfg.summary_tags = 10;
307 ctx->cfg.max_atom_items = 10;
299 ctx->cfg.ssdiff = 0; 308 ctx->cfg.ssdiff = 0;
300 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); 309 ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG"));
301 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); 310 ctx->env.http_host = xstrdupn(getenv("HTTP_HOST"));
302 ctx->env.https = xstrdupn(getenv("HTTPS")); 311 ctx->env.https = xstrdupn(getenv("HTTPS"));
@@ -697,9 +706,9 @@ int main(int argc, const char **argv)
697 cgit_repolist.count = 0; 706 cgit_repolist.count = 0;
698 cgit_repolist.repos = NULL; 707 cgit_repolist.repos = NULL;
699 708
700 cgit_parse_args(argc, argv); 709 cgit_parse_args(argc, argv);
701 parse_configfile(ctx.env.cgit_config, config_cb); 710 parse_configfile(expand_macros(ctx.env.cgit_config), config_cb);
702 ctx.repo = NULL; 711 ctx.repo = NULL;
703 http_parse_querystring(ctx.qry.raw, querystring_cb); 712 http_parse_querystring(ctx.qry.raw, querystring_cb);
704 713
705 /* If virtual-root isn't specified in cgitrc, lets pretend 714 /* If virtual-root isn't specified in cgitrc, lets pretend
diff --git a/cgit.css b/cgit.css
index 28a2eeb..6e47eb3 100644
--- a/cgit.css
+++ b/cgit.css
@@ -530,9 +530,12 @@ a.deco {
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
diff --git a/cgit.h b/cgit.h
index 1bdfbc6..e9e2718 100644
--- a/cgit.h
+++ b/cgit.h
@@ -72,8 +72,9 @@ struct cgit_repo {
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;
@@ -144,8 +145,9 @@ struct cgit_query {
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 int context; 150 int context;
149 int ignorews; 151 int ignorews;
150 char *vpath; 152 char *vpath;
151}; 153};
@@ -183,10 +185,12 @@ struct cgit_config {
183 int enable_index_links; 185 int enable_index_links;
184 int enable_log_filecount; 186 int enable_log_filecount;
185 int enable_log_linecount; 187 int enable_log_linecount;
186 int enable_remote_branches; 188 int enable_remote_branches;
189 int enable_subject_links;
187 int enable_tree_linenumbers; 190 int enable_tree_linenumbers;
188 int local_time; 191 int local_time;
192 int max_atom_items;
189 int max_repo_count; 193 int max_repo_count;
190 int max_commit_count; 194 int max_commit_count;
191 int max_lock_attempts; 195 int max_lock_attempts;
192 int max_msg_len; 196 int max_msg_len;
@@ -299,5 +303,7 @@ extern int cgit_open_filter(struct cgit_filter *filter);
299extern int cgit_close_filter(struct cgit_filter *filter); 303extern int cgit_close_filter(struct cgit_filter *filter);
300 304
301extern int readfile(const char *path, char **buf, size_t *size); 305extern int readfile(const char *path, char **buf, size_t *size);
302 306
307extern char *expand_macros(const char *txt);
308
303#endif /* CGIT_H */ 309#endif /* CGIT_H */
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 1f7ac1e..a853522 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -114,8 +114,14 @@ enable-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
@@ -165,8 +171,12 @@ logo-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
@@ -320,8 +330,12 @@ repo.enable-log-linecount::
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.
diff --git a/cmd.c b/cmd.c
index 605876b..6dc9f5e 100644
--- a/cmd.c
+++ b/cmd.c
@@ -32,9 +32,9 @@ static void HEAD_fn(struct cgit_context *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{
diff --git a/parsing.c b/parsing.c
index f3f3b15..f37c49d 100644
--- a/parsing.c
+++ b/parsing.c
@@ -189,8 +189,12 @@ struct commitinfo *cgit_parse_commit(struct commit *commit)
189 } else 189 } else
190 ret->subject = xstrdup(p); 190 ret->subject = xstrdup(p);
191 191
192 if (ret->msg_encoding) { 192 if (ret->msg_encoding) {
193 reencode(&ret->author, PAGE_ENCODING, ret->msg_encoding);
194 reencode(&ret->author_email, PAGE_ENCODING, ret->msg_encoding);
195 reencode(&ret->committer, PAGE_ENCODING, ret->msg_encoding);
196 reencode(&ret->committer_email, PAGE_ENCODING, ret->msg_encoding);
193 reencode(&ret->subject, PAGE_ENCODING, ret->msg_encoding); 197 reencode(&ret->subject, PAGE_ENCODING, ret->msg_encoding);
194 reencode(&ret->msg, PAGE_ENCODING, ret->msg_encoding); 198 reencode(&ret->msg, PAGE_ENCODING, ret->msg_encoding);
195 } 199 }
196 200
diff --git a/scan-tree.c b/scan-tree.c
index dbca797..1e18f3c 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -55,8 +55,10 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
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;
diff --git a/shared.c b/shared.c
index d0973ab..b42c2a2 100644
--- a/shared.c
+++ b/shared.c
@@ -58,8 +58,9 @@ struct cgit_repo *cgit_add_repo(const char *url)
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;
@@ -278,8 +279,12 @@ int cgit_diff_files(const unsigned char *old_sha1,
278 279
279 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || 280 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
280 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { 281 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
281 *binary = 1; 282 *binary = 1;
283 if (file1.size)
284 free(file1.ptr);
285 if (file2.size)
286 free(file2.ptr);
282 return 0; 287 return 0;
283 } 288 }
284 289
285 memset(&diff_params, 0, sizeof(diff_params)); 290 memset(&diff_params, 0, sizeof(diff_params));
@@ -292,8 +297,12 @@ int cgit_diff_files(const unsigned char *old_sha1,
292 emit_params.flags = XDL_EMIT_FUNCNAMES; 297 emit_params.flags = XDL_EMIT_FUNCNAMES;
293 emit_cb.outf = filediff_cb; 298 emit_cb.outf = filediff_cb;
294 emit_cb.priv = fn; 299 emit_cb.priv = fn;
295 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 300 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
301 if (file1.size)
302 free(file1.ptr);
303 if (file2.size)
304 free(file2.ptr);
296 return 0; 305 return 0;
297} 306}
298 307
299void cgit_diff_tree(const unsigned char *old_sha1, 308void cgit_diff_tree(const unsigned char *old_sha1,
@@ -427,4 +436,75 @@ int readfile(const char *path, char **buf, size_t *size)
427 (*buf)[*size] = '\0'; 436 (*buf)[*size] = '\0';
428 close(fd); 437 close(fd);
429 return (*size == st.st_size ? 0 : e); 438 return (*size == st.st_size ? 0 : e);
430} 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
@@ -84,9 +84,11 @@ void cgit_print_atom(char *tip, char *path, int max_count)
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++] = "--";
diff --git a/ui-commit.c b/ui-commit.c
index 2d98ed9..a11bc5f 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -14,12 +14,12 @@
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;
@@ -85,11 +85,14 @@ void cgit_print_commit(char *hex, const char *prefix)
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>");
diff --git a/ui-log.c b/ui-log.c
index 354ee08..ee93653 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -149,12 +149,15 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
149 tip = ctx.qry.head; 149 tip = ctx.qry.head;
150 150
151 argv[1] = disambiguate_ref(tip); 151 argv[1] = disambiguate_ref(tip);
152 152
153 if (grep && pattern && (!strcmp(grep, "grep") || 153 if (grep && pattern) {
154 !strcmp(grep, "author") || 154 if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
155 !strcmp(grep, "committer"))) 155 !strcmp(grep, "committer"))
156 argv[argc++] = fmt("--%s=%s", grep, pattern); 156 argv[argc++] = fmt("--%s=%s", grep, pattern);
157 if (!strcmp(grep, "range"))
158 argv[1] = pattern;
159 }
157 160
158 if (path) { 161 if (path) {
159 argv[argc++] = "--"; 162 argv[argc++] = "--";
160 argv[argc++] = path; 163 argv[argc++] = path;
diff --git a/ui-plain.c b/ui-plain.c
index 66cb19c..da76406 100644
--- a/ui-plain.c
+++ b/ui-plain.c
@@ -9,10 +9,9 @@
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{
@@ -52,19 +51,65 @@ static void print_object(const unsigned char *sha1, const char *path)
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)
@@ -76,9 +121,8 @@ void cgit_print_plain(struct cgit_context *ctx)
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 }
@@ -86,9 +130,17 @@ void cgit_print_plain(struct cgit_context *ctx)
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}
diff --git a/ui-shared.c b/ui-shared.c
index f46c935..ae29615 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -839,8 +839,9 @@ void cgit_print_pageheader(struct cgit_context *ctx)
839 html("<select name='qt'>\n"); 839 html("<select name='qt'>\n");
840 html_option("grep", "log msg", ctx->qry.grep); 840 html_option("grep", "log msg", ctx->qry.grep);
841 html_option("author", "author", ctx->qry.grep); 841 html_option("author", "author", ctx->qry.grep);
842 html_option("committer", "committer", ctx->qry.grep); 842 html_option("committer", "committer", ctx->qry.grep);
843 html_option("range", "range", ctx->qry.grep);
843 html("</select>\n"); 844 html("</select>\n");
844 html("<input class='txt' type='text' size='10' name='q' value='"); 845 html("<input class='txt' type='text' size='10' name='q' value='");
845 html_attr(ctx->qry.search); 846 html_attr(ctx->qry.search);
846 html("'/>\n"); 847 html("'/>\n");