summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cmd.c2
-rw-r--r--ui-commit.c2
-rw-r--r--ui-patch.c6
-rw-r--r--ui-patch.h2
-rw-r--r--ui-shared.c4
-rw-r--r--ui-shared.h2
6 files changed, 10 insertions, 8 deletions
diff --git a/cmd.c b/cmd.c
index 893ae25..605876b 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,171 +1,171 @@
1/* cmd.c: the cgit command dispatcher 1/* cmd.c: the cgit command dispatcher
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 "cmd.h" 10#include "cmd.h"
11#include "cache.h" 11#include "cache.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13#include "ui-atom.h" 13#include "ui-atom.h"
14#include "ui-blob.h" 14#include "ui-blob.h"
15#include "ui-clone.h" 15#include "ui-clone.h"
16#include "ui-commit.h" 16#include "ui-commit.h"
17#include "ui-diff.h" 17#include "ui-diff.h"
18#include "ui-log.h" 18#include "ui-log.h"
19#include "ui-patch.h" 19#include "ui-patch.h"
20#include "ui-plain.h" 20#include "ui-plain.h"
21#include "ui-refs.h" 21#include "ui-refs.h"
22#include "ui-repolist.h" 22#include "ui-repolist.h"
23#include "ui-snapshot.h" 23#include "ui-snapshot.h"
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, 10);
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{
49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head); 49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head);
50} 50}
51 51
52static void commit_fn(struct cgit_context *ctx) 52static void commit_fn(struct cgit_context *ctx)
53{ 53{
54 cgit_print_commit(ctx->qry.sha1, ctx->qry.path); 54 cgit_print_commit(ctx->qry.sha1, ctx->qry.path);
55} 55}
56 56
57static void diff_fn(struct cgit_context *ctx) 57static void diff_fn(struct cgit_context *ctx)
58{ 58{
59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); 59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
60} 60}
61 61
62static void info_fn(struct cgit_context *ctx) 62static void info_fn(struct cgit_context *ctx)
63{ 63{
64 cgit_clone_info(ctx); 64 cgit_clone_info(ctx);
65} 65}
66 66
67static void log_fn(struct cgit_context *ctx) 67static void log_fn(struct cgit_context *ctx)
68{ 68{
69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, 69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); 70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
71} 71}
72 72
73static void ls_cache_fn(struct cgit_context *ctx) 73static void ls_cache_fn(struct cgit_context *ctx)
74{ 74{
75 ctx->page.mimetype = "text/plain"; 75 ctx->page.mimetype = "text/plain";
76 ctx->page.filename = "ls-cache.txt"; 76 ctx->page.filename = "ls-cache.txt";
77 cgit_print_http_headers(ctx); 77 cgit_print_http_headers(ctx);
78 cache_ls(ctx->cfg.cache_root); 78 cache_ls(ctx->cfg.cache_root);
79} 79}
80 80
81static void objects_fn(struct cgit_context *ctx) 81static void objects_fn(struct cgit_context *ctx)
82{ 82{
83 cgit_clone_objects(ctx); 83 cgit_clone_objects(ctx);
84} 84}
85 85
86static void repolist_fn(struct cgit_context *ctx) 86static void repolist_fn(struct cgit_context *ctx)
87{ 87{
88 cgit_print_repolist(); 88 cgit_print_repolist();
89} 89}
90 90
91static void patch_fn(struct cgit_context *ctx) 91static 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}
95 95
96static void plain_fn(struct cgit_context *ctx) 96static void plain_fn(struct cgit_context *ctx)
97{ 97{
98 cgit_print_plain(ctx); 98 cgit_print_plain(ctx);
99} 99}
100 100
101static void refs_fn(struct cgit_context *ctx) 101static void refs_fn(struct cgit_context *ctx)
102{ 102{
103 cgit_print_refs(); 103 cgit_print_refs();
104} 104}
105 105
106static void snapshot_fn(struct cgit_context *ctx) 106static void snapshot_fn(struct cgit_context *ctx)
107{ 107{
108 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, ctx->qry.path, 108 cgit_print_snapshot(ctx->qry.head, ctx->qry.sha1, ctx->qry.path,
109 ctx->repo->snapshots, ctx->qry.nohead); 109 ctx->repo->snapshots, ctx->qry.nohead);
110} 110}
111 111
112static void stats_fn(struct cgit_context *ctx) 112static void stats_fn(struct cgit_context *ctx)
113{ 113{
114 cgit_show_stats(ctx); 114 cgit_show_stats(ctx);
115} 115}
116 116
117static void summary_fn(struct cgit_context *ctx) 117static void summary_fn(struct cgit_context *ctx)
118{ 118{
119 cgit_print_summary(); 119 cgit_print_summary();
120} 120}
121 121
122static void tag_fn(struct cgit_context *ctx) 122static void tag_fn(struct cgit_context *ctx)
123{ 123{
124 cgit_print_tag(ctx->qry.sha1); 124 cgit_print_tag(ctx->qry.sha1);
125} 125}
126 126
127static void tree_fn(struct cgit_context *ctx) 127static void tree_fn(struct cgit_context *ctx)
128{ 128{
129 cgit_print_tree(ctx->qry.sha1, ctx->qry.path); 129 cgit_print_tree(ctx->qry.sha1, ctx->qry.path);
130} 130}
131 131
132#define def_cmd(name, want_repo, want_layout, want_vpath) \ 132#define def_cmd(name, want_repo, want_layout, want_vpath) \
133 {#name, name##_fn, want_repo, want_layout, want_vpath} 133 {#name, name##_fn, want_repo, want_layout, want_vpath}
134 134
135struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx) 135struct cgit_cmd *cgit_get_cmd(struct cgit_context *ctx)
136{ 136{
137 static struct cgit_cmd cmds[] = { 137 static struct cgit_cmd cmds[] = {
138 def_cmd(HEAD, 1, 0, 0), 138 def_cmd(HEAD, 1, 0, 0),
139 def_cmd(atom, 1, 0, 0), 139 def_cmd(atom, 1, 0, 0),
140 def_cmd(about, 0, 1, 0), 140 def_cmd(about, 0, 1, 0),
141 def_cmd(blob, 1, 0, 0), 141 def_cmd(blob, 1, 0, 0),
142 def_cmd(commit, 1, 1, 1), 142 def_cmd(commit, 1, 1, 1),
143 def_cmd(diff, 1, 1, 1), 143 def_cmd(diff, 1, 1, 1),
144 def_cmd(info, 1, 0, 0), 144 def_cmd(info, 1, 0, 0),
145 def_cmd(log, 1, 1, 1), 145 def_cmd(log, 1, 1, 1),
146 def_cmd(ls_cache, 0, 0, 0), 146 def_cmd(ls_cache, 0, 0, 0),
147 def_cmd(objects, 1, 0, 0), 147 def_cmd(objects, 1, 0, 0),
148 def_cmd(patch, 1, 0, 1), 148 def_cmd(patch, 1, 0, 1),
149 def_cmd(plain, 1, 0, 0), 149 def_cmd(plain, 1, 0, 0),
150 def_cmd(refs, 1, 1, 0), 150 def_cmd(refs, 1, 1, 0),
151 def_cmd(repolist, 0, 0, 0), 151 def_cmd(repolist, 0, 0, 0),
152 def_cmd(snapshot, 1, 0, 0), 152 def_cmd(snapshot, 1, 0, 0),
153 def_cmd(stats, 1, 1, 1), 153 def_cmd(stats, 1, 1, 1),
154 def_cmd(summary, 1, 1, 0), 154 def_cmd(summary, 1, 1, 0),
155 def_cmd(tag, 1, 1, 0), 155 def_cmd(tag, 1, 1, 0),
156 def_cmd(tree, 1, 1, 1), 156 def_cmd(tree, 1, 1, 1),
157 }; 157 };
158 int i; 158 int i;
159 159
160 if (ctx->qry.page == NULL) { 160 if (ctx->qry.page == NULL) {
161 if (ctx->repo) 161 if (ctx->repo)
162 ctx->qry.page = "summary"; 162 ctx->qry.page = "summary";
163 else 163 else
164 ctx->qry.page = "repolist"; 164 ctx->qry.page = "repolist";
165 } 165 }
166 166
167 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++) 167 for(i = 0; i < sizeof(cmds)/sizeof(*cmds); i++)
168 if (!strcmp(ctx->qry.page, cmds[i].name)) 168 if (!strcmp(ctx->qry.page, cmds[i].name))
169 return &cmds[i]; 169 return &cmds[i];
170 return NULL; 170 return NULL;
171} 171}
diff --git a/ui-commit.c b/ui-commit.c
index 2f4c6d4..b3a2063 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,123 +1,123 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12#include "ui-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;
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;
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));
34 return; 34 return;
35 } 35 }
36 info = cgit_parse_commit(commit); 36 info = cgit_parse_commit(commit);
37 37
38 load_ref_decorations(DECORATE_FULL_REFS); 38 load_ref_decorations(DECORATE_FULL_REFS);
39 39
40 html("<table summary='commit info' class='commit-info'>\n"); 40 html("<table summary='commit info' class='commit-info'>\n");
41 html("<tr><th>author</th><td>"); 41 html("<tr><th>author</th><td>");
42 html_txt(info->author); 42 html_txt(info->author);
43 if (!ctx.cfg.noplainemail) { 43 if (!ctx.cfg.noplainemail) {
44 html(" "); 44 html(" ");
45 html_txt(info->author_email); 45 html_txt(info->author_email);
46 } 46 }
47 html("</td><td class='right'>"); 47 html("</td><td class='right'>");
48 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time); 48 cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time);
49 html("</td></tr>\n"); 49 html("</td></tr>\n");
50 html("<tr><th>committer</th><td>"); 50 html("<tr><th>committer</th><td>");
51 html_txt(info->committer); 51 html_txt(info->committer);
52 if (!ctx.cfg.noplainemail) { 52 if (!ctx.cfg.noplainemail) {
53 html(" "); 53 html(" ");
54 html_txt(info->committer_email); 54 html_txt(info->committer_email);
55 } 55 }
56 html("</td><td class='right'>"); 56 html("</td><td class='right'>");
57 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time); 57 cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time);
58 html("</td></tr>\n"); 58 html("</td></tr>\n");
59 html("<tr><th>commit</th><td colspan='2' class='sha1'>"); 59 html("<tr><th>commit</th><td colspan='2' class='sha1'>");
60 tmp = sha1_to_hex(commit->object.sha1); 60 tmp = sha1_to_hex(commit->object.sha1);
61 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, 0); 61 cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, 0);
62 html(" ("); 62 html(" (");
63 cgit_patch_link("patch", NULL, NULL, NULL, tmp); 63 cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix);
64 html(") ("); 64 html(") (");
65 if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff)) 65 if ((ctx.qry.ssdiff && !ctx.cfg.ssdiff) || (!ctx.qry.ssdiff && ctx.cfg.ssdiff))
66 cgit_commit_link("unidiff", NULL, NULL, ctx.qry.head, tmp, 1); 66 cgit_commit_link("unidiff", NULL, NULL, ctx.qry.head, tmp, 1);
67 else 67 else
68 cgit_commit_link("side-by-side diff", NULL, NULL, ctx.qry.head, tmp, 1); 68 cgit_commit_link("side-by-side diff", NULL, NULL, ctx.qry.head, tmp, 1);
69 html(")</td></tr>\n"); 69 html(")</td></tr>\n");
70 html("<tr><th>tree</th><td colspan='2' class='sha1'>"); 70 html("<tr><th>tree</th><td colspan='2' class='sha1'>");
71 tmp = xstrdup(hex); 71 tmp = xstrdup(hex);
72 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL, 72 cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
73 ctx.qry.head, tmp, NULL); 73 ctx.qry.head, tmp, NULL);
74 html("</td></tr>\n"); 74 html("</td></tr>\n");
75 for (p = commit->parents; p ; p = p->next) { 75 for (p = commit->parents; p ; p = p->next) {
76 parent = lookup_commit_reference(p->item->object.sha1); 76 parent = lookup_commit_reference(p->item->object.sha1);
77 if (!parent) { 77 if (!parent) {
78 html("<tr><td colspan='3'>"); 78 html("<tr><td colspan='3'>");
79 cgit_print_error("Error reading parent commit"); 79 cgit_print_error("Error reading parent commit");
80 html("</td></tr>"); 80 html("</td></tr>");
81 continue; 81 continue;
82 } 82 }
83 html("<tr><th>parent</th>" 83 html("<tr><th>parent</th>"
84 "<td colspan='2' class='sha1'>"); 84 "<td colspan='2' class='sha1'>");
85 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL, 85 cgit_commit_link(sha1_to_hex(p->item->object.sha1), NULL, NULL,
86 ctx.qry.head, sha1_to_hex(p->item->object.sha1), 0); 86 ctx.qry.head, sha1_to_hex(p->item->object.sha1), 0);
87 html(" ("); 87 html(" (");
88 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex, 88 cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
89 sha1_to_hex(p->item->object.sha1), NULL, 0); 89 sha1_to_hex(p->item->object.sha1), NULL, 0);
90 html(")</td></tr>"); 90 html(")</td></tr>");
91 parents++; 91 parents++;
92 } 92 }
93 if (ctx.repo->snapshots) { 93 if (ctx.repo->snapshots) {
94 html("<tr><th>download</th><td colspan='2' class='sha1'>"); 94 html("<tr><th>download</th><td colspan='2' class='sha1'>");
95 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head, 95 cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
96 hex, ctx.repo->snapshots); 96 hex, ctx.repo->snapshots);
97 html("</td></tr>"); 97 html("</td></tr>");
98 } 98 }
99 html("</table>\n"); 99 html("</table>\n");
100 html("<div class='commit-subject'>"); 100 html("<div class='commit-subject'>");
101 if (ctx.repo->commit_filter) 101 if (ctx.repo->commit_filter)
102 cgit_open_filter(ctx.repo->commit_filter); 102 cgit_open_filter(ctx.repo->commit_filter);
103 html_txt(info->subject); 103 html_txt(info->subject);
104 if (ctx.repo->commit_filter) 104 if (ctx.repo->commit_filter)
105 cgit_close_filter(ctx.repo->commit_filter); 105 cgit_close_filter(ctx.repo->commit_filter);
106 show_commit_decorations(commit); 106 show_commit_decorations(commit);
107 html("</div>"); 107 html("</div>");
108 html("<div class='commit-msg'>"); 108 html("<div class='commit-msg'>");
109 if (ctx.repo->commit_filter) 109 if (ctx.repo->commit_filter)
110 cgit_open_filter(ctx.repo->commit_filter); 110 cgit_open_filter(ctx.repo->commit_filter);
111 html_txt(info->msg); 111 html_txt(info->msg);
112 if (ctx.repo->commit_filter) 112 if (ctx.repo->commit_filter)
113 cgit_close_filter(ctx.repo->commit_filter); 113 cgit_close_filter(ctx.repo->commit_filter);
114 html("</div>"); 114 html("</div>");
115 if (parents < 3) { 115 if (parents < 3) {
116 if (parents) 116 if (parents)
117 tmp = sha1_to_hex(commit->parents->item->object.sha1); 117 tmp = sha1_to_hex(commit->parents->item->object.sha1);
118 else 118 else
119 tmp = NULL; 119 tmp = NULL;
120 cgit_print_diff(ctx.qry.sha1, tmp, prefix); 120 cgit_print_diff(ctx.qry.sha1, tmp, prefix);
121 } 121 }
122 cgit_free_commitinfo(info); 122 cgit_free_commitinfo(info);
123} 123}
diff --git a/ui-patch.c b/ui-patch.c
index 2a8f7a5..25dc9fe 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -1,129 +1,131 @@
1/* ui-patch.c: generate patch view 1/* ui-patch.c: generate patch view
2 * 2 *
3 * Copyright (C) 2007 Lars Hjemli 3 * Copyright (C) 2007 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13static void print_line(char *line, int len) 13static void print_line(char *line, int len)
14{ 14{
15 char c = line[len-1]; 15 char c = line[len-1];
16 16
17 line[len-1] = '\0'; 17 line[len-1] = '\0';
18 htmlf("%s\n", line); 18 htmlf("%s\n", line);
19 line[len-1] = c; 19 line[len-1] = c;
20} 20}
21 21
22static void header(unsigned char *sha1, char *path1, int mode1, 22static void header(unsigned char *sha1, char *path1, int mode1,
23 unsigned char *sha2, char *path2, int mode2) 23 unsigned char *sha2, char *path2, int mode2)
24{ 24{
25 char *abbrev1, *abbrev2; 25 char *abbrev1, *abbrev2;
26 int subproject; 26 int subproject;
27 27
28 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); 28 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
29 htmlf("diff --git a/%s b/%s\n", path1, path2); 29 htmlf("diff --git a/%s b/%s\n", path1, path2);
30 30
31 if (is_null_sha1(sha1)) 31 if (is_null_sha1(sha1))
32 path1 = "dev/null"; 32 path1 = "dev/null";
33 if (is_null_sha1(sha2)) 33 if (is_null_sha1(sha2))
34 path2 = "dev/null"; 34 path2 = "dev/null";
35 35
36 if (mode1 == 0) 36 if (mode1 == 0)
37 htmlf("new file mode %.6o\n", mode2); 37 htmlf("new file mode %.6o\n", mode2);
38 38
39 if (mode2 == 0) 39 if (mode2 == 0)
40 htmlf("deleted file mode %.6o\n", mode1); 40 htmlf("deleted file mode %.6o\n", mode1);
41 41
42 if (!subproject) { 42 if (!subproject) {
43 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); 43 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
44 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); 44 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
45 htmlf("index %s..%s", abbrev1, abbrev2); 45 htmlf("index %s..%s", abbrev1, abbrev2);
46 free(abbrev1); 46 free(abbrev1);
47 free(abbrev2); 47 free(abbrev2);
48 if (mode1 != 0 && mode2 != 0) { 48 if (mode1 != 0 && mode2 != 0) {
49 htmlf(" %.6o", mode1); 49 htmlf(" %.6o", mode1);
50 if (mode2 != mode1) 50 if (mode2 != mode1)
51 htmlf("..%.6o", mode2); 51 htmlf("..%.6o", mode2);
52 } 52 }
53 htmlf("\n--- a/%s\n", path1); 53 htmlf("\n--- a/%s\n", path1);
54 htmlf("+++ b/%s\n", path2); 54 htmlf("+++ b/%s\n", path2);
55 } 55 }
56} 56}
57 57
58static void filepair_cb(struct diff_filepair *pair) 58static void filepair_cb(struct diff_filepair *pair)
59{ 59{
60 unsigned long old_size = 0; 60 unsigned long old_size = 0;
61 unsigned long new_size = 0; 61 unsigned long new_size = 0;
62 int binary = 0; 62 int binary = 0;
63 63
64 header(pair->one->sha1, pair->one->path, pair->one->mode, 64 header(pair->one->sha1, pair->one->path, pair->one->mode,
65 pair->two->sha1, pair->two->path, pair->two->mode); 65 pair->two->sha1, pair->two->path, pair->two->mode);
66 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 66 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
67 if (S_ISGITLINK(pair->one->mode)) 67 if (S_ISGITLINK(pair->one->mode))
68 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 68 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
69 if (S_ISGITLINK(pair->two->mode)) 69 if (S_ISGITLINK(pair->two->mode))
70 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 70 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
71 return; 71 return;
72 } 72 }
73 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, 73 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
74 &new_size, &binary, print_line)) 74 &new_size, &binary, print_line))
75 html("Error running diff"); 75 html("Error running diff");
76 if (binary) 76 if (binary)
77 html("Binary files differ\n"); 77 html("Binary files differ\n");
78} 78}
79 79
80void cgit_print_patch(char *hex) 80void cgit_print_patch(char *hex, const char *prefix)
81{ 81{
82 struct commit *commit; 82 struct commit *commit;
83 struct commitinfo *info; 83 struct commitinfo *info;
84 unsigned char sha1[20], old_sha1[20]; 84 unsigned char sha1[20], old_sha1[20];
85 char *patchname; 85 char *patchname;
86 86
87 if (!hex) 87 if (!hex)
88 hex = ctx.qry.head; 88 hex = ctx.qry.head;
89 89
90 if (get_sha1(hex, sha1)) { 90 if (get_sha1(hex, sha1)) {
91 cgit_print_error(fmt("Bad object id: %s", hex)); 91 cgit_print_error(fmt("Bad object id: %s", hex));
92 return; 92 return;
93 } 93 }
94 commit = lookup_commit_reference(sha1); 94 commit = lookup_commit_reference(sha1);
95 if (!commit) { 95 if (!commit) {
96 cgit_print_error(fmt("Bad commit reference: %s", hex)); 96 cgit_print_error(fmt("Bad commit reference: %s", hex));
97 return; 97 return;
98 } 98 }
99 info = cgit_parse_commit(commit); 99 info = cgit_parse_commit(commit);
100 100
101 if (commit->parents && commit->parents->item) 101 if (commit->parents && commit->parents->item)
102 hashcpy(old_sha1, commit->parents->item->object.sha1); 102 hashcpy(old_sha1, commit->parents->item->object.sha1);
103 else 103 else
104 hashclr(old_sha1); 104 hashclr(old_sha1);
105 105
106 patchname = fmt("%s.patch", sha1_to_hex(sha1)); 106 patchname = fmt("%s.patch", sha1_to_hex(sha1));
107 ctx.page.mimetype = "text/plain"; 107 ctx.page.mimetype = "text/plain";
108 ctx.page.filename = patchname; 108 ctx.page.filename = patchname;
109 cgit_print_http_headers(&ctx); 109 cgit_print_http_headers(&ctx);
110 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1)); 110 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
111 htmlf("From: %s", info->author); 111 htmlf("From: %s", info->author);
112 if (!ctx.cfg.noplainemail) { 112 if (!ctx.cfg.noplainemail) {
113 htmlf(" %s", info->author_email); 113 htmlf(" %s", info->author_email);
114 } 114 }
115 html("\n"); 115 html("\n");
116 html("Date: "); 116 html("Date: ");
117 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n", ctx.cfg.local_time); 117 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n", ctx.cfg.local_time);
118 htmlf("Subject: %s\n\n", info->subject); 118 htmlf("Subject: %s\n\n", info->subject);
119 if (info->msg && *info->msg) { 119 if (info->msg && *info->msg) {
120 htmlf("%s", info->msg); 120 htmlf("%s", info->msg);
121 if (info->msg[strlen(info->msg) - 1] != '\n') 121 if (info->msg[strlen(info->msg) - 1] != '\n')
122 html("\n"); 122 html("\n");
123 } 123 }
124 html("---\n"); 124 html("---\n");
125 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);
126 html("--\n"); 128 html("--\n");
127 htmlf("cgit %s\n", CGIT_VERSION); 129 htmlf("cgit %s\n", CGIT_VERSION);
128 cgit_free_commitinfo(info); 130 cgit_free_commitinfo(info);
129} 131}
diff --git a/ui-patch.h b/ui-patch.h
index 9f68212..1641cea 100644
--- a/ui-patch.h
+++ b/ui-patch.h
@@ -1,6 +1,6 @@
1#ifndef UI_PATCH_H 1#ifndef UI_PATCH_H
2#define UI_PATCH_H 2#define UI_PATCH_H
3 3
4extern void cgit_print_patch(char *hex); 4extern void cgit_print_patch(char *hex, const char *prefix);
5 5
6#endif /* UI_PATCH_H */ 6#endif /* UI_PATCH_H */
diff --git a/ui-shared.c b/ui-shared.c
index 4fa506f..d5c4c10 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -7,771 +7,771 @@
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cmd.h" 10#include "cmd.h"
11#include "html.h" 11#include "html.h"
12 12
13const char cgit_doctype[] = 13const char cgit_doctype[] =
14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" 14"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; 15" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
16 16
17static char *http_date(time_t t) 17static char *http_date(time_t t)
18{ 18{
19 static char day[][4] = 19 static char day[][4] =
20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 20 {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
21 static char month[][4] = 21 static char month[][4] =
22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun", 22 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
23 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; 23 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
24 struct tm *tm = gmtime(&t); 24 struct tm *tm = gmtime(&t);
25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], 25 return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, 26 tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year,
27 tm->tm_hour, tm->tm_min, tm->tm_sec); 27 tm->tm_hour, tm->tm_min, tm->tm_sec);
28} 28}
29 29
30void cgit_print_error(const char *msg) 30void cgit_print_error(const char *msg)
31{ 31{
32 html("<div class='error'>"); 32 html("<div class='error'>");
33 html_txt(msg); 33 html_txt(msg);
34 html("</div>\n"); 34 html("</div>\n");
35} 35}
36 36
37char *cgit_httpscheme() 37char *cgit_httpscheme()
38{ 38{
39 if (ctx.env.https && !strcmp(ctx.env.https, "on")) 39 if (ctx.env.https && !strcmp(ctx.env.https, "on"))
40 return "https://"; 40 return "https://";
41 else 41 else
42 return "http://"; 42 return "http://";
43} 43}
44 44
45char *cgit_hosturl() 45char *cgit_hosturl()
46{ 46{
47 if (ctx.env.http_host) 47 if (ctx.env.http_host)
48 return ctx.env.http_host; 48 return ctx.env.http_host;
49 if (!ctx.env.server_name) 49 if (!ctx.env.server_name)
50 return NULL; 50 return NULL;
51 if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80) 51 if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
52 return ctx.env.server_name; 52 return ctx.env.server_name;
53 return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port)); 53 return xstrdup(fmt("%s:%s", ctx.env.server_name, ctx.env.server_port));
54} 54}
55 55
56char *cgit_rooturl() 56char *cgit_rooturl()
57{ 57{
58 if (ctx.cfg.virtual_root) 58 if (ctx.cfg.virtual_root)
59 return fmt("%s/", ctx.cfg.virtual_root); 59 return fmt("%s/", ctx.cfg.virtual_root);
60 else 60 else
61 return ctx.cfg.script_name; 61 return ctx.cfg.script_name;
62} 62}
63 63
64char *cgit_repourl(const char *reponame) 64char *cgit_repourl(const char *reponame)
65{ 65{
66 if (ctx.cfg.virtual_root) { 66 if (ctx.cfg.virtual_root) {
67 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); 67 return fmt("%s/%s/", ctx.cfg.virtual_root, reponame);
68 } else { 68 } else {
69 return fmt("?r=%s", reponame); 69 return fmt("?r=%s", reponame);
70 } 70 }
71} 71}
72 72
73char *cgit_fileurl(const char *reponame, const char *pagename, 73char *cgit_fileurl(const char *reponame, const char *pagename,
74 const char *filename, const char *query) 74 const char *filename, const char *query)
75{ 75{
76 char *tmp; 76 char *tmp;
77 char *delim; 77 char *delim;
78 78
79 if (ctx.cfg.virtual_root) { 79 if (ctx.cfg.virtual_root) {
80 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, 80 tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame,
81 pagename, (filename ? filename:"")); 81 pagename, (filename ? filename:""));
82 delim = "?"; 82 delim = "?";
83 } else { 83 } else {
84 tmp = fmt("?url=%s/%s/%s", reponame, pagename, 84 tmp = fmt("?url=%s/%s/%s", reponame, pagename,
85 (filename ? filename : "")); 85 (filename ? filename : ""));
86 delim = "&"; 86 delim = "&";
87 } 87 }
88 if (query) 88 if (query)
89 tmp = fmt("%s%s%s", tmp, delim, query); 89 tmp = fmt("%s%s%s", tmp, delim, query);
90 return tmp; 90 return tmp;
91} 91}
92 92
93char *cgit_pageurl(const char *reponame, const char *pagename, 93char *cgit_pageurl(const char *reponame, const char *pagename,
94 const char *query) 94 const char *query)
95{ 95{
96 return cgit_fileurl(reponame,pagename,0,query); 96 return cgit_fileurl(reponame,pagename,0,query);
97} 97}
98 98
99const char *cgit_repobasename(const char *reponame) 99const char *cgit_repobasename(const char *reponame)
100{ 100{
101 /* I assume we don't need to store more than one repo basename */ 101 /* I assume we don't need to store more than one repo basename */
102 static char rvbuf[1024]; 102 static char rvbuf[1024];
103 int p; 103 int p;
104 const char *rv; 104 const char *rv;
105 strncpy(rvbuf,reponame,sizeof(rvbuf)); 105 strncpy(rvbuf,reponame,sizeof(rvbuf));
106 if(rvbuf[sizeof(rvbuf)-1]) 106 if(rvbuf[sizeof(rvbuf)-1])
107 die("cgit_repobasename: truncated repository name '%s'", reponame); 107 die("cgit_repobasename: truncated repository name '%s'", reponame);
108 p = strlen(rvbuf)-1; 108 p = strlen(rvbuf)-1;
109 /* strip trailing slashes */ 109 /* strip trailing slashes */
110 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 110 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
111 /* strip trailing .git */ 111 /* strip trailing .git */
112 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 112 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
113 p -= 3; rvbuf[p--] = 0; 113 p -= 3; rvbuf[p--] = 0;
114 } 114 }
115 /* strip more trailing slashes if any */ 115 /* strip more trailing slashes if any */
116 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 116 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
117 /* find last slash in the remaining string */ 117 /* find last slash in the remaining string */
118 rv = strrchr(rvbuf,'/'); 118 rv = strrchr(rvbuf,'/');
119 if(rv) 119 if(rv)
120 return ++rv; 120 return ++rv;
121 return rvbuf; 121 return rvbuf;
122} 122}
123 123
124char *cgit_currurl() 124char *cgit_currurl()
125{ 125{
126 if (!ctx.cfg.virtual_root) 126 if (!ctx.cfg.virtual_root)
127 return ctx.cfg.script_name; 127 return ctx.cfg.script_name;
128 else if (ctx.qry.page) 128 else if (ctx.qry.page)
129 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); 129 return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page);
130 else if (ctx.qry.repo) 130 else if (ctx.qry.repo)
131 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); 131 return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo);
132 else 132 else
133 return fmt("%s/", ctx.cfg.virtual_root); 133 return fmt("%s/", ctx.cfg.virtual_root);
134} 134}
135 135
136static void site_url(const char *page, const char *search, int ofs) 136static void site_url(const char *page, const char *search, int ofs)
137{ 137{
138 char *delim = "?"; 138 char *delim = "?";
139 139
140 if (ctx.cfg.virtual_root) { 140 if (ctx.cfg.virtual_root) {
141 html_attr(ctx.cfg.virtual_root); 141 html_attr(ctx.cfg.virtual_root);
142 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 142 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
143 html("/"); 143 html("/");
144 } else 144 } else
145 html(ctx.cfg.script_name); 145 html(ctx.cfg.script_name);
146 146
147 if (page) { 147 if (page) {
148 htmlf("?p=%s", page); 148 htmlf("?p=%s", page);
149 delim = "&"; 149 delim = "&";
150 } 150 }
151 if (search) { 151 if (search) {
152 html(delim); 152 html(delim);
153 html("q="); 153 html("q=");
154 html_attr(search); 154 html_attr(search);
155 delim = "&"; 155 delim = "&";
156 } 156 }
157 if (ofs) { 157 if (ofs) {
158 html(delim); 158 html(delim);
159 htmlf("ofs=%d", ofs); 159 htmlf("ofs=%d", ofs);
160 } 160 }
161} 161}
162 162
163static void site_link(const char *page, const char *name, const char *title, 163static void site_link(const char *page, const char *name, const char *title,
164 const char *class, const char *search, int ofs) 164 const char *class, const char *search, int ofs)
165{ 165{
166 html("<a"); 166 html("<a");
167 if (title) { 167 if (title) {
168 html(" title='"); 168 html(" title='");
169 html_attr(title); 169 html_attr(title);
170 html("'"); 170 html("'");
171 } 171 }
172 if (class) { 172 if (class) {
173 html(" class='"); 173 html(" class='");
174 html_attr(class); 174 html_attr(class);
175 html("'"); 175 html("'");
176 } 176 }
177 html(" href='"); 177 html(" href='");
178 site_url(page, search, ofs); 178 site_url(page, search, ofs);
179 html("'>"); 179 html("'>");
180 html_txt(name); 180 html_txt(name);
181 html("</a>"); 181 html("</a>");
182} 182}
183 183
184void cgit_index_link(const char *name, const char *title, const char *class, 184void cgit_index_link(const char *name, const char *title, const char *class,
185 const char *pattern, int ofs) 185 const char *pattern, int ofs)
186{ 186{
187 site_link(NULL, name, title, class, pattern, ofs); 187 site_link(NULL, name, title, class, pattern, ofs);
188} 188}
189 189
190static char *repolink(const char *title, const char *class, const char *page, 190static char *repolink(const char *title, const char *class, const char *page,
191 const char *head, const char *path) 191 const char *head, const char *path)
192{ 192{
193 char *delim = "?"; 193 char *delim = "?";
194 194
195 html("<a"); 195 html("<a");
196 if (title) { 196 if (title) {
197 html(" title='"); 197 html(" title='");
198 html_attr(title); 198 html_attr(title);
199 html("'"); 199 html("'");
200 } 200 }
201 if (class) { 201 if (class) {
202 html(" class='"); 202 html(" class='");
203 html_attr(class); 203 html_attr(class);
204 html("'"); 204 html("'");
205 } 205 }
206 html(" href='"); 206 html(" href='");
207 if (ctx.cfg.virtual_root) { 207 if (ctx.cfg.virtual_root) {
208 html_url_path(ctx.cfg.virtual_root); 208 html_url_path(ctx.cfg.virtual_root);
209 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') 209 if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/')
210 html("/"); 210 html("/");
211 html_url_path(ctx.repo->url); 211 html_url_path(ctx.repo->url);
212 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 212 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
213 html("/"); 213 html("/");
214 if (page) { 214 if (page) {
215 html_url_path(page); 215 html_url_path(page);
216 html("/"); 216 html("/");
217 if (path) 217 if (path)
218 html_url_path(path); 218 html_url_path(path);
219 } 219 }
220 } else { 220 } else {
221 html(ctx.cfg.script_name); 221 html(ctx.cfg.script_name);
222 html("?url="); 222 html("?url=");
223 html_url_arg(ctx.repo->url); 223 html_url_arg(ctx.repo->url);
224 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') 224 if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
225 html("/"); 225 html("/");
226 if (page) { 226 if (page) {
227 html_url_arg(page); 227 html_url_arg(page);
228 html("/"); 228 html("/");
229 if (path) 229 if (path)
230 html_url_arg(path); 230 html_url_arg(path);
231 } 231 }
232 delim = "&amp;"; 232 delim = "&amp;";
233 } 233 }
234 if (head && strcmp(head, ctx.repo->defbranch)) { 234 if (head && strcmp(head, ctx.repo->defbranch)) {
235 html(delim); 235 html(delim);
236 html("h="); 236 html("h=");
237 html_url_arg(head); 237 html_url_arg(head);
238 delim = "&amp;"; 238 delim = "&amp;";
239 } 239 }
240 return fmt("%s", delim); 240 return fmt("%s", delim);
241} 241}
242 242
243static void reporevlink(const char *page, const char *name, const char *title, 243static void reporevlink(const char *page, const char *name, const char *title,
244 const char *class, const char *head, const char *rev, 244 const char *class, const char *head, const char *rev,
245 const char *path) 245 const char *path)
246{ 246{
247 char *delim; 247 char *delim;
248 248
249 delim = repolink(title, class, page, head, path); 249 delim = repolink(title, class, page, head, path);
250 if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) { 250 if (rev && ctx.qry.head != NULL && strcmp(rev, ctx.qry.head)) {
251 html(delim); 251 html(delim);
252 html("id="); 252 html("id=");
253 html_url_arg(rev); 253 html_url_arg(rev);
254 } 254 }
255 html("'>"); 255 html("'>");
256 html_txt(name); 256 html_txt(name);
257 html("</a>"); 257 html("</a>");
258} 258}
259 259
260void cgit_summary_link(const char *name, const char *title, const char *class, 260void cgit_summary_link(const char *name, const char *title, const char *class,
261 const char *head) 261 const char *head)
262{ 262{
263 reporevlink(NULL, name, title, class, head, NULL, NULL); 263 reporevlink(NULL, name, title, class, head, NULL, NULL);
264} 264}
265 265
266void cgit_tag_link(const char *name, const char *title, const char *class, 266void cgit_tag_link(const char *name, const char *title, const char *class,
267 const char *head, const char *rev) 267 const char *head, const char *rev)
268{ 268{
269 reporevlink("tag", name, title, class, head, rev, NULL); 269 reporevlink("tag", name, title, class, head, rev, NULL);
270} 270}
271 271
272void cgit_tree_link(const char *name, const char *title, const char *class, 272void cgit_tree_link(const char *name, const char *title, const char *class,
273 const char *head, const char *rev, const char *path) 273 const char *head, const char *rev, const char *path)
274{ 274{
275 reporevlink("tree", name, title, class, head, rev, path); 275 reporevlink("tree", name, title, class, head, rev, path);
276} 276}
277 277
278void cgit_plain_link(const char *name, const char *title, const char *class, 278void cgit_plain_link(const char *name, const char *title, const char *class,
279 const char *head, const char *rev, const char *path) 279 const char *head, const char *rev, const char *path)
280{ 280{
281 reporevlink("plain", name, title, class, head, rev, path); 281 reporevlink("plain", name, title, class, head, rev, path);
282} 282}
283 283
284void cgit_log_link(const char *name, const char *title, const char *class, 284void cgit_log_link(const char *name, const char *title, const char *class,
285 const char *head, const char *rev, const char *path, 285 const char *head, const char *rev, const char *path,
286 int ofs, const char *grep, const char *pattern, int showmsg) 286 int ofs, const char *grep, const char *pattern, int showmsg)
287{ 287{
288 char *delim; 288 char *delim;
289 289
290 delim = repolink(title, class, "log", head, path); 290 delim = repolink(title, class, "log", head, path);
291 if (rev && strcmp(rev, ctx.qry.head)) { 291 if (rev && strcmp(rev, ctx.qry.head)) {
292 html(delim); 292 html(delim);
293 html("id="); 293 html("id=");
294 html_url_arg(rev); 294 html_url_arg(rev);
295 delim = "&"; 295 delim = "&";
296 } 296 }
297 if (grep && pattern) { 297 if (grep && pattern) {
298 html(delim); 298 html(delim);
299 html("qt="); 299 html("qt=");
300 html_url_arg(grep); 300 html_url_arg(grep);
301 delim = "&"; 301 delim = "&";
302 html(delim); 302 html(delim);
303 html("q="); 303 html("q=");
304 html_url_arg(pattern); 304 html_url_arg(pattern);
305 } 305 }
306 if (ofs > 0) { 306 if (ofs > 0) {
307 html(delim); 307 html(delim);
308 html("ofs="); 308 html("ofs=");
309 htmlf("%d", ofs); 309 htmlf("%d", ofs);
310 delim = "&"; 310 delim = "&";
311 } 311 }
312 if (showmsg) { 312 if (showmsg) {
313 html(delim); 313 html(delim);
314 html("showmsg=1"); 314 html("showmsg=1");
315 } 315 }
316 html("'>"); 316 html("'>");
317 html_txt(name); 317 html_txt(name);
318 html("</a>"); 318 html("</a>");
319} 319}
320 320
321void cgit_commit_link(char *name, const char *title, const char *class, 321void cgit_commit_link(char *name, const char *title, const char *class,
322 const char *head, const char *rev, int toggle_ssdiff) 322 const char *head, const char *rev, int toggle_ssdiff)
323{ 323{
324 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { 324 if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
325 name[ctx.cfg.max_msg_len] = '\0'; 325 name[ctx.cfg.max_msg_len] = '\0';
326 name[ctx.cfg.max_msg_len - 1] = '.'; 326 name[ctx.cfg.max_msg_len - 1] = '.';
327 name[ctx.cfg.max_msg_len - 2] = '.'; 327 name[ctx.cfg.max_msg_len - 2] = '.';
328 name[ctx.cfg.max_msg_len - 3] = '.'; 328 name[ctx.cfg.max_msg_len - 3] = '.';
329 } 329 }
330 330
331 char *delim; 331 char *delim;
332 332
333 delim = repolink(title, class, "commit", head, NULL); 333 delim = repolink(title, class, "commit", head, NULL);
334 if (rev && strcmp(rev, ctx.qry.head)) { 334 if (rev && strcmp(rev, ctx.qry.head)) {
335 html(delim); 335 html(delim);
336 html("id="); 336 html("id=");
337 html_url_arg(rev); 337 html_url_arg(rev);
338 delim = "&amp;"; 338 delim = "&amp;";
339 } 339 }
340 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { 340 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
341 html(delim); 341 html(delim);
342 html("ss=1"); 342 html("ss=1");
343 } 343 }
344 html("'>"); 344 html("'>");
345 html_txt(name); 345 html_txt(name);
346 html("</a>"); 346 html("</a>");
347} 347}
348 348
349void cgit_refs_link(const char *name, const char *title, const char *class, 349void cgit_refs_link(const char *name, const char *title, const char *class,
350 const char *head, const char *rev, const char *path) 350 const char *head, const char *rev, const char *path)
351{ 351{
352 reporevlink("refs", name, title, class, head, rev, path); 352 reporevlink("refs", name, title, class, head, rev, path);
353} 353}
354 354
355void cgit_snapshot_link(const char *name, const char *title, const char *class, 355void cgit_snapshot_link(const char *name, const char *title, const char *class,
356 const char *head, const char *rev, 356 const char *head, const char *rev,
357 const char *archivename) 357 const char *archivename)
358{ 358{
359 reporevlink("snapshot", name, title, class, head, rev, archivename); 359 reporevlink("snapshot", name, title, class, head, rev, archivename);
360} 360}
361 361
362void cgit_diff_link(const char *name, const char *title, const char *class, 362void cgit_diff_link(const char *name, const char *title, const char *class,
363 const char *head, const char *new_rev, const char *old_rev, 363 const char *head, const char *new_rev, const char *old_rev,
364 const char *path, int toggle_ssdiff) 364 const char *path, int toggle_ssdiff)
365{ 365{
366 char *delim; 366 char *delim;
367 367
368 delim = repolink(title, class, "diff", head, path); 368 delim = repolink(title, class, "diff", head, path);
369 if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) { 369 if (new_rev && ctx.qry.head != NULL && strcmp(new_rev, ctx.qry.head)) {
370 html(delim); 370 html(delim);
371 html("id="); 371 html("id=");
372 html_url_arg(new_rev); 372 html_url_arg(new_rev);
373 delim = "&amp;"; 373 delim = "&amp;";
374 } 374 }
375 if (old_rev) { 375 if (old_rev) {
376 html(delim); 376 html(delim);
377 html("id2="); 377 html("id2=");
378 html_url_arg(old_rev); 378 html_url_arg(old_rev);
379 delim = "&amp;"; 379 delim = "&amp;";
380 } 380 }
381 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) { 381 if ((ctx.qry.ssdiff && !toggle_ssdiff) || (!ctx.qry.ssdiff && toggle_ssdiff)) {
382 html(delim); 382 html(delim);
383 html("ss=1"); 383 html("ss=1");
384 } 384 }
385 html("'>"); 385 html("'>");
386 html_txt(name); 386 html_txt(name);
387 html("</a>"); 387 html("</a>");
388} 388}
389 389
390void cgit_patch_link(const char *name, const char *title, const char *class, 390void cgit_patch_link(const char *name, const char *title, const char *class,
391 const char *head, const char *rev) 391 const char *head, const char *rev, const char *path)
392{ 392{
393 reporevlink("patch", name, title, class, head, rev, NULL); 393 reporevlink("patch", name, title, class, head, rev, path);
394} 394}
395 395
396void cgit_stats_link(const char *name, const char *title, const char *class, 396void cgit_stats_link(const char *name, const char *title, const char *class,
397 const char *head, const char *path) 397 const char *head, const char *path)
398{ 398{
399 reporevlink("stats", name, title, class, head, NULL, path); 399 reporevlink("stats", name, title, class, head, NULL, path);
400} 400}
401 401
402void cgit_self_link(char *name, const char *title, const char *class, 402void cgit_self_link(char *name, const char *title, const char *class,
403 struct cgit_context *ctx) 403 struct cgit_context *ctx)
404{ 404{
405 if (!strcmp(ctx->qry.page, "repolist")) 405 if (!strcmp(ctx->qry.page, "repolist"))
406 return cgit_index_link(name, title, class, ctx->qry.search, 406 return cgit_index_link(name, title, class, ctx->qry.search,
407 ctx->qry.ofs); 407 ctx->qry.ofs);
408 else if (!strcmp(ctx->qry.page, "summary")) 408 else if (!strcmp(ctx->qry.page, "summary"))
409 return cgit_summary_link(name, title, class, ctx->qry.head); 409 return cgit_summary_link(name, title, class, ctx->qry.head);
410 else if (!strcmp(ctx->qry.page, "tag")) 410 else if (!strcmp(ctx->qry.page, "tag"))
411 return cgit_tag_link(name, title, class, ctx->qry.head, 411 return cgit_tag_link(name, title, class, ctx->qry.head,
412 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL); 412 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL);
413 else if (!strcmp(ctx->qry.page, "tree")) 413 else if (!strcmp(ctx->qry.page, "tree"))
414 return cgit_tree_link(name, title, class, ctx->qry.head, 414 return cgit_tree_link(name, title, class, ctx->qry.head,
415 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 415 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
416 ctx->qry.path); 416 ctx->qry.path);
417 else if (!strcmp(ctx->qry.page, "plain")) 417 else if (!strcmp(ctx->qry.page, "plain"))
418 return cgit_plain_link(name, title, class, ctx->qry.head, 418 return cgit_plain_link(name, title, class, ctx->qry.head,
419 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 419 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
420 ctx->qry.path); 420 ctx->qry.path);
421 else if (!strcmp(ctx->qry.page, "log")) 421 else if (!strcmp(ctx->qry.page, "log"))
422 return cgit_log_link(name, title, class, ctx->qry.head, 422 return cgit_log_link(name, title, class, ctx->qry.head,
423 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 423 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
424 ctx->qry.path, ctx->qry.ofs, 424 ctx->qry.path, ctx->qry.ofs,
425 ctx->qry.grep, ctx->qry.search, 425 ctx->qry.grep, ctx->qry.search,
426 ctx->qry.showmsg); 426 ctx->qry.showmsg);
427 else if (!strcmp(ctx->qry.page, "commit")) 427 else if (!strcmp(ctx->qry.page, "commit"))
428 return cgit_commit_link(name, title, class, ctx->qry.head, 428 return cgit_commit_link(name, title, class, ctx->qry.head,
429 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 429 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
430 ctx->qry.path, 0); 430 ctx->qry.path, 0);
431 else if (!strcmp(ctx->qry.page, "patch")) 431 else if (!strcmp(ctx->qry.page, "patch"))
432 return cgit_patch_link(name, title, class, ctx->qry.head, 432 return cgit_patch_link(name, title, class, ctx->qry.head,
433 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 433 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
434 ctx->qry.path); 434 ctx->qry.path);
435 else if (!strcmp(ctx->qry.page, "refs")) 435 else if (!strcmp(ctx->qry.page, "refs"))
436 return cgit_refs_link(name, title, class, ctx->qry.head, 436 return cgit_refs_link(name, title, class, ctx->qry.head,
437 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 437 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
438 ctx->qry.path); 438 ctx->qry.path);
439 else if (!strcmp(ctx->qry.page, "snapshot")) 439 else if (!strcmp(ctx->qry.page, "snapshot"))
440 return cgit_snapshot_link(name, title, class, ctx->qry.head, 440 return cgit_snapshot_link(name, title, class, ctx->qry.head,
441 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL, 441 ctx->qry.has_sha1 ? ctx->qry.sha1 : NULL,
442 ctx->qry.path); 442 ctx->qry.path);
443 else if (!strcmp(ctx->qry.page, "diff")) 443 else if (!strcmp(ctx->qry.page, "diff"))
444 return cgit_diff_link(name, title, class, ctx->qry.head, 444 return cgit_diff_link(name, title, class, ctx->qry.head,
445 ctx->qry.sha1, ctx->qry.sha2, 445 ctx->qry.sha1, ctx->qry.sha2,
446 ctx->qry.path, 0); 446 ctx->qry.path, 0);
447 else if (!strcmp(ctx->qry.page, "stats")) 447 else if (!strcmp(ctx->qry.page, "stats"))
448 return cgit_stats_link(name, title, class, ctx->qry.head, 448 return cgit_stats_link(name, title, class, ctx->qry.head,
449 ctx->qry.path); 449 ctx->qry.path);
450 450
451 /* Don't known how to make link for this page */ 451 /* Don't known how to make link for this page */
452 repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path); 452 repolink(title, class, ctx->qry.page, ctx->qry.head, ctx->qry.path);
453 html("><!-- cgit_self_link() doesn't know how to make link for page '"); 453 html("><!-- cgit_self_link() doesn't know how to make link for page '");
454 html_txt(ctx->qry.page); 454 html_txt(ctx->qry.page);
455 html("' -->"); 455 html("' -->");
456 html_txt(name); 456 html_txt(name);
457 html("</a>"); 457 html("</a>");
458} 458}
459 459
460void cgit_object_link(struct object *obj) 460void cgit_object_link(struct object *obj)
461{ 461{
462 char *page, *shortrev, *fullrev, *name; 462 char *page, *shortrev, *fullrev, *name;
463 463
464 fullrev = sha1_to_hex(obj->sha1); 464 fullrev = sha1_to_hex(obj->sha1);
465 shortrev = xstrdup(fullrev); 465 shortrev = xstrdup(fullrev);
466 shortrev[10] = '\0'; 466 shortrev[10] = '\0';
467 if (obj->type == OBJ_COMMIT) { 467 if (obj->type == OBJ_COMMIT) {
468 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL, 468 cgit_commit_link(fmt("commit %s...", shortrev), NULL, NULL,
469 ctx.qry.head, fullrev, 0); 469 ctx.qry.head, fullrev, 0);
470 return; 470 return;
471 } else if (obj->type == OBJ_TREE) 471 } else if (obj->type == OBJ_TREE)
472 page = "tree"; 472 page = "tree";
473 else if (obj->type == OBJ_TAG) 473 else if (obj->type == OBJ_TAG)
474 page = "tag"; 474 page = "tag";
475 else 475 else
476 page = "blob"; 476 page = "blob";
477 name = fmt("%s %s...", typename(obj->type), shortrev); 477 name = fmt("%s %s...", typename(obj->type), shortrev);
478 reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL); 478 reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
479} 479}
480 480
481void cgit_print_date(time_t secs, const char *format, int local_time) 481void cgit_print_date(time_t secs, const char *format, int local_time)
482{ 482{
483 char buf[64]; 483 char buf[64];
484 struct tm *time; 484 struct tm *time;
485 485
486 if (!secs) 486 if (!secs)
487 return; 487 return;
488 if(local_time) 488 if(local_time)
489 time = localtime(&secs); 489 time = localtime(&secs);
490 else 490 else
491 time = gmtime(&secs); 491 time = gmtime(&secs);
492 strftime(buf, sizeof(buf)-1, format, time); 492 strftime(buf, sizeof(buf)-1, format, time);
493 html_txt(buf); 493 html_txt(buf);
494} 494}
495 495
496void cgit_print_age(time_t t, time_t max_relative, const char *format) 496void cgit_print_age(time_t t, time_t max_relative, const char *format)
497{ 497{
498 time_t now, secs; 498 time_t now, secs;
499 499
500 if (!t) 500 if (!t)
501 return; 501 return;
502 time(&now); 502 time(&now);
503 secs = now - t; 503 secs = now - t;
504 504
505 if (secs > max_relative && max_relative >= 0) { 505 if (secs > max_relative && max_relative >= 0) {
506 cgit_print_date(t, format, ctx.cfg.local_time); 506 cgit_print_date(t, format, ctx.cfg.local_time);
507 return; 507 return;
508 } 508 }
509 509
510 if (secs < TM_HOUR * 2) { 510 if (secs < TM_HOUR * 2) {
511 htmlf("<span class='age-mins'>%.0f min.</span>", 511 htmlf("<span class='age-mins'>%.0f min.</span>",
512 secs * 1.0 / TM_MIN); 512 secs * 1.0 / TM_MIN);
513 return; 513 return;
514 } 514 }
515 if (secs < TM_DAY * 2) { 515 if (secs < TM_DAY * 2) {
516 htmlf("<span class='age-hours'>%.0f hours</span>", 516 htmlf("<span class='age-hours'>%.0f hours</span>",
517 secs * 1.0 / TM_HOUR); 517 secs * 1.0 / TM_HOUR);
518 return; 518 return;
519 } 519 }
520 if (secs < TM_WEEK * 2) { 520 if (secs < TM_WEEK * 2) {
521 htmlf("<span class='age-days'>%.0f days</span>", 521 htmlf("<span class='age-days'>%.0f days</span>",
522 secs * 1.0 / TM_DAY); 522 secs * 1.0 / TM_DAY);
523 return; 523 return;
524 } 524 }
525 if (secs < TM_MONTH * 2) { 525 if (secs < TM_MONTH * 2) {
526 htmlf("<span class='age-weeks'>%.0f weeks</span>", 526 htmlf("<span class='age-weeks'>%.0f weeks</span>",
527 secs * 1.0 / TM_WEEK); 527 secs * 1.0 / TM_WEEK);
528 return; 528 return;
529 } 529 }
530 if (secs < TM_YEAR * 2) { 530 if (secs < TM_YEAR * 2) {
531 htmlf("<span class='age-months'>%.0f months</span>", 531 htmlf("<span class='age-months'>%.0f months</span>",
532 secs * 1.0 / TM_MONTH); 532 secs * 1.0 / TM_MONTH);
533 return; 533 return;
534 } 534 }
535 htmlf("<span class='age-years'>%.0f years</span>", 535 htmlf("<span class='age-years'>%.0f years</span>",
536 secs * 1.0 / TM_YEAR); 536 secs * 1.0 / TM_YEAR);
537} 537}
538 538
539void cgit_print_http_headers(struct cgit_context *ctx) 539void cgit_print_http_headers(struct cgit_context *ctx)
540{ 540{
541 if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1")) 541 if (ctx->env.no_http && !strcmp(ctx->env.no_http, "1"))
542 return; 542 return;
543 543
544 if (ctx->page.status) 544 if (ctx->page.status)
545 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg); 545 htmlf("Status: %d %s\n", ctx->page.status, ctx->page.statusmsg);
546 if (ctx->page.mimetype && ctx->page.charset) 546 if (ctx->page.mimetype && ctx->page.charset)
547 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, 547 htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype,
548 ctx->page.charset); 548 ctx->page.charset);
549 else if (ctx->page.mimetype) 549 else if (ctx->page.mimetype)
550 htmlf("Content-Type: %s\n", ctx->page.mimetype); 550 htmlf("Content-Type: %s\n", ctx->page.mimetype);
551 if (ctx->page.size) 551 if (ctx->page.size)
552 htmlf("Content-Length: %ld\n", ctx->page.size); 552 htmlf("Content-Length: %ld\n", ctx->page.size);
553 if (ctx->page.filename) 553 if (ctx->page.filename)
554 htmlf("Content-Disposition: inline; filename=\"%s\"\n", 554 htmlf("Content-Disposition: inline; filename=\"%s\"\n",
555 ctx->page.filename); 555 ctx->page.filename);
556 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); 556 htmlf("Last-Modified: %s\n", http_date(ctx->page.modified));
557 htmlf("Expires: %s\n", http_date(ctx->page.expires)); 557 htmlf("Expires: %s\n", http_date(ctx->page.expires));
558 if (ctx->page.etag) 558 if (ctx->page.etag)
559 htmlf("ETag: \"%s\"\n", ctx->page.etag); 559 htmlf("ETag: \"%s\"\n", ctx->page.etag);
560 html("\n"); 560 html("\n");
561 if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD")) 561 if (ctx->env.request_method && !strcmp(ctx->env.request_method, "HEAD"))
562 exit(0); 562 exit(0);
563} 563}
564 564
565void cgit_print_docstart(struct cgit_context *ctx) 565void cgit_print_docstart(struct cgit_context *ctx)
566{ 566{
567 if (ctx->cfg.embedded) { 567 if (ctx->cfg.embedded) {
568 if (ctx->cfg.header) 568 if (ctx->cfg.header)
569 html_include(ctx->cfg.header); 569 html_include(ctx->cfg.header);
570 return; 570 return;
571 } 571 }
572 572
573 char *host = cgit_hosturl(); 573 char *host = cgit_hosturl();
574 html(cgit_doctype); 574 html(cgit_doctype);
575 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); 575 html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
576 html("<head>\n"); 576 html("<head>\n");
577 html("<title>"); 577 html("<title>");
578 html_txt(ctx->page.title); 578 html_txt(ctx->page.title);
579 html("</title>\n"); 579 html("</title>\n");
580 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 580 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
581 if (ctx->cfg.robots && *ctx->cfg.robots) 581 if (ctx->cfg.robots && *ctx->cfg.robots)
582 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); 582 htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots);
583 html("<link rel='stylesheet' type='text/css' href='"); 583 html("<link rel='stylesheet' type='text/css' href='");
584 html_attr(ctx->cfg.css); 584 html_attr(ctx->cfg.css);
585 html("'/>\n"); 585 html("'/>\n");
586 if (ctx->cfg.favicon) { 586 if (ctx->cfg.favicon) {
587 html("<link rel='shortcut icon' href='"); 587 html("<link rel='shortcut icon' href='");
588 html_attr(ctx->cfg.favicon); 588 html_attr(ctx->cfg.favicon);
589 html("'/>\n"); 589 html("'/>\n");
590 } 590 }
591 if (host && ctx->repo) { 591 if (host && ctx->repo) {
592 html("<link rel='alternate' title='Atom feed' href='"); 592 html("<link rel='alternate' title='Atom feed' href='");
593 html(cgit_httpscheme()); 593 html(cgit_httpscheme());
594 html_attr(cgit_hosturl()); 594 html_attr(cgit_hosturl());
595 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath, 595 html_attr(cgit_fileurl(ctx->repo->url, "atom", ctx->qry.vpath,
596 fmt("h=%s", ctx->qry.head))); 596 fmt("h=%s", ctx->qry.head)));
597 html("' type='application/atom+xml'/>\n"); 597 html("' type='application/atom+xml'/>\n");
598 } 598 }
599 if (ctx->cfg.head_include) 599 if (ctx->cfg.head_include)
600 html_include(ctx->cfg.head_include); 600 html_include(ctx->cfg.head_include);
601 html("</head>\n"); 601 html("</head>\n");
602 html("<body>\n"); 602 html("<body>\n");
603 if (ctx->cfg.header) 603 if (ctx->cfg.header)
604 html_include(ctx->cfg.header); 604 html_include(ctx->cfg.header);
605} 605}
606 606
607void cgit_print_docend() 607void cgit_print_docend()
608{ 608{
609 html("</div> <!-- class=content -->\n"); 609 html("</div> <!-- class=content -->\n");
610 if (ctx.cfg.embedded) { 610 if (ctx.cfg.embedded) {
611 html("</div> <!-- id=cgit -->\n"); 611 html("</div> <!-- id=cgit -->\n");
612 if (ctx.cfg.footer) 612 if (ctx.cfg.footer)
613 html_include(ctx.cfg.footer); 613 html_include(ctx.cfg.footer);
614 return; 614 return;
615 } 615 }
616 if (ctx.cfg.footer) 616 if (ctx.cfg.footer)
617 html_include(ctx.cfg.footer); 617 html_include(ctx.cfg.footer);
618 else { 618 else {
619 htmlf("<div class='footer'>generated by cgit %s at ", 619 htmlf("<div class='footer'>generated by cgit %s at ",
620 cgit_version); 620 cgit_version);
621 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); 621 cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
622 html("</div>\n"); 622 html("</div>\n");
623 } 623 }
624 html("</div> <!-- id=cgit -->\n"); 624 html("</div> <!-- id=cgit -->\n");
625 html("</body>\n</html>\n"); 625 html("</body>\n</html>\n");
626} 626}
627 627
628int print_branch_option(const char *refname, const unsigned char *sha1, 628int print_branch_option(const char *refname, const unsigned char *sha1,
629 int flags, void *cb_data) 629 int flags, void *cb_data)
630{ 630{
631 char *name = (char *)refname; 631 char *name = (char *)refname;
632 html_option(name, name, ctx.qry.head); 632 html_option(name, name, ctx.qry.head);
633 return 0; 633 return 0;
634} 634}
635 635
636int print_archive_ref(const char *refname, const unsigned char *sha1, 636int print_archive_ref(const char *refname, const unsigned char *sha1,
637 int flags, void *cb_data) 637 int flags, void *cb_data)
638{ 638{
639 struct tag *tag; 639 struct tag *tag;
640 struct taginfo *info; 640 struct taginfo *info;
641 struct object *obj; 641 struct object *obj;
642 char buf[256], *url; 642 char buf[256], *url;
643 unsigned char fileid[20]; 643 unsigned char fileid[20];
644 int *header = (int *)cb_data; 644 int *header = (int *)cb_data;
645 645
646 if (prefixcmp(refname, "refs/archives")) 646 if (prefixcmp(refname, "refs/archives"))
647 return 0; 647 return 0;
648 strncpy(buf, refname+14, sizeof(buf)); 648 strncpy(buf, refname+14, sizeof(buf));
649 obj = parse_object(sha1); 649 obj = parse_object(sha1);
650 if (!obj) 650 if (!obj)
651 return 1; 651 return 1;
652 if (obj->type == OBJ_TAG) { 652 if (obj->type == OBJ_TAG) {
653 tag = lookup_tag(sha1); 653 tag = lookup_tag(sha1);
654 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 654 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
655 return 0; 655 return 0;
656 hashcpy(fileid, tag->tagged->sha1); 656 hashcpy(fileid, tag->tagged->sha1);
657 } else if (obj->type != OBJ_BLOB) { 657 } else if (obj->type != OBJ_BLOB) {
658 return 0; 658 return 0;
659 } else { 659 } else {
660 hashcpy(fileid, sha1); 660 hashcpy(fileid, sha1);
661 } 661 }
662 if (!*header) { 662 if (!*header) {
663 html("<h1>download</h1>\n"); 663 html("<h1>download</h1>\n");
664 *header = 1; 664 *header = 1;
665 } 665 }
666 url = cgit_pageurl(ctx.qry.repo, "blob", 666 url = cgit_pageurl(ctx.qry.repo, "blob",
667 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 667 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
668 buf)); 668 buf));
669 html_link_open(url, NULL, "menu"); 669 html_link_open(url, NULL, "menu");
670 html_txt(strlpart(buf, 20)); 670 html_txt(strlpart(buf, 20));
671 html_link_close(); 671 html_link_close();
672 return 0; 672 return 0;
673} 673}
674 674
675void cgit_add_hidden_formfields(int incl_head, int incl_search, 675void cgit_add_hidden_formfields(int incl_head, int incl_search,
676 const char *page) 676 const char *page)
677{ 677{
678 char *url; 678 char *url;
679 679
680 if (!ctx.cfg.virtual_root) { 680 if (!ctx.cfg.virtual_root) {
681 url = fmt("%s/%s", ctx.qry.repo, page); 681 url = fmt("%s/%s", ctx.qry.repo, page);
682 if (ctx.qry.vpath) 682 if (ctx.qry.vpath)
683 url = fmt("%s/%s", url, ctx.qry.vpath); 683 url = fmt("%s/%s", url, ctx.qry.vpath);
684 html_hidden("url", url); 684 html_hidden("url", url);
685 } 685 }
686 686
687 if (incl_head && ctx.qry.head && ctx.repo->defbranch && 687 if (incl_head && ctx.qry.head && ctx.repo->defbranch &&
688 strcmp(ctx.qry.head, ctx.repo->defbranch)) 688 strcmp(ctx.qry.head, ctx.repo->defbranch))
689 html_hidden("h", ctx.qry.head); 689 html_hidden("h", ctx.qry.head);
690 690
691 if (ctx.qry.sha1) 691 if (ctx.qry.sha1)
692 html_hidden("id", ctx.qry.sha1); 692 html_hidden("id", ctx.qry.sha1);
693 if (ctx.qry.sha2) 693 if (ctx.qry.sha2)
694 html_hidden("id2", ctx.qry.sha2); 694 html_hidden("id2", ctx.qry.sha2);
695 if (ctx.qry.showmsg) 695 if (ctx.qry.showmsg)
696 html_hidden("showmsg", "1"); 696 html_hidden("showmsg", "1");
697 697
698 if (incl_search) { 698 if (incl_search) {
699 if (ctx.qry.grep) 699 if (ctx.qry.grep)
700 html_hidden("qt", ctx.qry.grep); 700 html_hidden("qt", ctx.qry.grep);
701 if (ctx.qry.search) 701 if (ctx.qry.search)
702 html_hidden("q", ctx.qry.search); 702 html_hidden("q", ctx.qry.search);
703 } 703 }
704} 704}
705 705
706static const char *hc(struct cgit_context *ctx, const char *page) 706static const char *hc(struct cgit_context *ctx, const char *page)
707{ 707{
708 return strcmp(ctx->qry.page, page) ? NULL : "active"; 708 return strcmp(ctx->qry.page, page) ? NULL : "active";
709} 709}
710 710
711static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path) 711static void cgit_print_path_crumbs(struct cgit_context *ctx, char *path)
712{ 712{
713 char *old_path = ctx->qry.path; 713 char *old_path = ctx->qry.path;
714 char *p = path, *q, *end = path + strlen(path); 714 char *p = path, *q, *end = path + strlen(path);
715 715
716 ctx->qry.path = NULL; 716 ctx->qry.path = NULL;
717 cgit_self_link("root", NULL, NULL, ctx); 717 cgit_self_link("root", NULL, NULL, ctx);
718 ctx->qry.path = p = path; 718 ctx->qry.path = p = path;
719 while (p < end) { 719 while (p < end) {
720 if (!(q = strchr(p, '/'))) 720 if (!(q = strchr(p, '/')))
721 q = end; 721 q = end;
722 *q = '\0'; 722 *q = '\0';
723 html_txt("/"); 723 html_txt("/");
724 cgit_self_link(p, NULL, NULL, ctx); 724 cgit_self_link(p, NULL, NULL, ctx);
725 if (q < end) 725 if (q < end)
726 *q = '/'; 726 *q = '/';
727 p = q + 1; 727 p = q + 1;
728 } 728 }
729 ctx->qry.path = old_path; 729 ctx->qry.path = old_path;
730} 730}
731 731
732static void print_header(struct cgit_context *ctx) 732static void print_header(struct cgit_context *ctx)
733{ 733{
734 html("<table id='header'>\n"); 734 html("<table id='header'>\n");
735 html("<tr>\n"); 735 html("<tr>\n");
736 736
737 if (ctx->cfg.logo && ctx->cfg.logo[0] != 0) { 737 if (ctx->cfg.logo && ctx->cfg.logo[0] != 0) {
738 html("<td class='logo' rowspan='2'><a href='"); 738 html("<td class='logo' rowspan='2'><a href='");
739 if (ctx->cfg.logo_link) 739 if (ctx->cfg.logo_link)
740 html_attr(ctx->cfg.logo_link); 740 html_attr(ctx->cfg.logo_link);
741 else 741 else
742 html_attr(cgit_rooturl()); 742 html_attr(cgit_rooturl());
743 html("'><img src='"); 743 html("'><img src='");
744 html_attr(ctx->cfg.logo); 744 html_attr(ctx->cfg.logo);
745 html("' alt='cgit logo'/></a></td>\n"); 745 html("' alt='cgit logo'/></a></td>\n");
746 } 746 }
747 747
748 html("<td class='main'>"); 748 html("<td class='main'>");
749 if (ctx->repo) { 749 if (ctx->repo) {
750 cgit_index_link("index", NULL, NULL, NULL, 0); 750 cgit_index_link("index", NULL, NULL, NULL, 0);
751 html(" : "); 751 html(" : ");
752 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL); 752 cgit_summary_link(ctx->repo->name, ctx->repo->name, NULL, NULL);
753 html("</td><td class='form'>"); 753 html("</td><td class='form'>");
754 html("<form method='get' action=''>\n"); 754 html("<form method='get' action=''>\n");
755 cgit_add_hidden_formfields(0, 1, ctx->qry.page); 755 cgit_add_hidden_formfields(0, 1, ctx->qry.page);
756 html("<select name='h' onchange='this.form.submit();'>\n"); 756 html("<select name='h' onchange='this.form.submit();'>\n");
757 for_each_branch_ref(print_branch_option, ctx->qry.head); 757 for_each_branch_ref(print_branch_option, ctx->qry.head);
758 html("</select> "); 758 html("</select> ");
759 html("<input type='submit' name='' value='switch'/>"); 759 html("<input type='submit' name='' value='switch'/>");
760 html("</form>"); 760 html("</form>");
761 } else 761 } else
762 html_txt(ctx->cfg.root_title); 762 html_txt(ctx->cfg.root_title);
763 html("</td></tr>\n"); 763 html("</td></tr>\n");
764 764
765 html("<tr><td class='sub'>"); 765 html("<tr><td class='sub'>");
766 if (ctx->repo) { 766 if (ctx->repo) {
767 html_txt(ctx->repo->desc); 767 html_txt(ctx->repo->desc);
768 html("</td><td class='sub right'>"); 768 html("</td><td class='sub right'>");
769 html_txt(ctx->repo->owner); 769 html_txt(ctx->repo->owner);
770 } else { 770 } else {
771 if (ctx->cfg.root_desc) 771 if (ctx->cfg.root_desc)
772 html_txt(ctx->cfg.root_desc); 772 html_txt(ctx->cfg.root_desc);
773 else if (ctx->cfg.index_info) 773 else if (ctx->cfg.index_info)
774 html_include(ctx->cfg.index_info); 774 html_include(ctx->cfg.index_info);
775 } 775 }
776 html("</td></tr></table>\n"); 776 html("</td></tr></table>\n");
777} 777}
diff --git a/ui-shared.h b/ui-shared.h
index 3df5464..c0e5c55 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -1,65 +1,65 @@
1#ifndef UI_SHARED_H 1#ifndef UI_SHARED_H
2#define UI_SHARED_H 2#define UI_SHARED_H
3 3
4extern char *cgit_httpscheme(); 4extern char *cgit_httpscheme();
5extern char *cgit_hosturl(); 5extern char *cgit_hosturl();
6extern char *cgit_rooturl(); 6extern char *cgit_rooturl();
7extern char *cgit_repourl(const char *reponame); 7extern char *cgit_repourl(const char *reponame);
8extern char *cgit_fileurl(const char *reponame, const char *pagename, 8extern char *cgit_fileurl(const char *reponame, const char *pagename,
9 const char *filename, const char *query); 9 const char *filename, const char *query);
10extern char *cgit_pageurl(const char *reponame, const char *pagename, 10extern char *cgit_pageurl(const char *reponame, const char *pagename,
11 const char *query); 11 const char *query);
12 12
13extern void cgit_index_link(const char *name, const char *title, 13extern void cgit_index_link(const char *name, const char *title,
14 const char *class, const char *pattern, int ofs); 14 const char *class, const char *pattern, int ofs);
15extern void cgit_summary_link(const char *name, const char *title, 15extern void cgit_summary_link(const char *name, const char *title,
16 const char *class, const char *head); 16 const char *class, const char *head);
17extern void cgit_tag_link(const char *name, const char *title, 17extern void cgit_tag_link(const char *name, const char *title,
18 const char *class, const char *head, 18 const char *class, const char *head,
19 const char *rev); 19 const char *rev);
20extern void cgit_tree_link(const char *name, const char *title, 20extern void cgit_tree_link(const char *name, const char *title,
21 const char *class, const char *head, 21 const char *class, const char *head,
22 const char *rev, const char *path); 22 const char *rev, const char *path);
23extern void cgit_plain_link(const char *name, const char *title, 23extern void cgit_plain_link(const char *name, const char *title,
24 const char *class, const char *head, 24 const char *class, const char *head,
25 const char *rev, const char *path); 25 const char *rev, const char *path);
26extern void cgit_log_link(const char *name, const char *title, 26extern void cgit_log_link(const char *name, const char *title,
27 const char *class, const char *head, const char *rev, 27 const char *class, const char *head, const char *rev,
28 const char *path, int ofs, const char *grep, 28 const char *path, int ofs, const char *grep,
29 const char *pattern, int showmsg); 29 const char *pattern, int showmsg);
30extern void cgit_commit_link(char *name, const char *title, 30extern void cgit_commit_link(char *name, const char *title,
31 const char *class, const char *head, 31 const char *class, const char *head,
32 const char *rev, int toggle_ssdiff); 32 const char *rev, int toggle_ssdiff);
33extern void cgit_patch_link(const char *name, const char *title, 33extern void cgit_patch_link(const char *name, const char *title,
34 const char *class, const char *head, 34 const char *class, const char *head,
35 const char *rev); 35 const char *rev, const char *path);
36extern void cgit_refs_link(const char *name, const char *title, 36extern void cgit_refs_link(const char *name, const char *title,
37 const char *class, const char *head, 37 const char *class, const char *head,
38 const char *rev, const char *path); 38 const char *rev, const char *path);
39extern void cgit_snapshot_link(const char *name, const char *title, 39extern void cgit_snapshot_link(const char *name, const char *title,
40 const char *class, const char *head, 40 const char *class, const char *head,
41 const char *rev, const char *archivename); 41 const char *rev, const char *archivename);
42extern void cgit_diff_link(const char *name, const char *title, 42extern void cgit_diff_link(const char *name, const char *title,
43 const char *class, const char *head, 43 const char *class, const char *head,
44 const char *new_rev, const char *old_rev, 44 const char *new_rev, const char *old_rev,
45 const char *path, int toggle_ssdiff); 45 const char *path, int toggle_ssdiff);
46extern void cgit_stats_link(const char *name, const char *title, 46extern void cgit_stats_link(const char *name, const char *title,
47 const char *class, const char *head, 47 const char *class, const char *head,
48 const char *path); 48 const char *path);
49extern void cgit_self_link(char *name, const char *title, 49extern void cgit_self_link(char *name, const char *title,
50 const char *class, struct cgit_context *ctx); 50 const char *class, struct cgit_context *ctx);
51extern void cgit_object_link(struct object *obj); 51extern void cgit_object_link(struct object *obj);
52 52
53extern void cgit_print_error(const char *msg); 53extern void cgit_print_error(const char *msg);
54extern void cgit_print_date(time_t secs, const char *format, int local_time); 54extern void cgit_print_date(time_t secs, const char *format, int local_time);
55extern void cgit_print_age(time_t t, time_t max_relative, const char *format); 55extern void cgit_print_age(time_t t, time_t max_relative, const char *format);
56extern void cgit_print_http_headers(struct cgit_context *ctx); 56extern void cgit_print_http_headers(struct cgit_context *ctx);
57extern void cgit_print_docstart(struct cgit_context *ctx); 57extern void cgit_print_docstart(struct cgit_context *ctx);
58extern void cgit_print_docend(); 58extern void cgit_print_docend();
59extern void cgit_print_pageheader(struct cgit_context *ctx); 59extern void cgit_print_pageheader(struct cgit_context *ctx);
60extern void cgit_print_filemode(unsigned short mode); 60extern void cgit_print_filemode(unsigned short mode);
61extern void cgit_print_snapshot_links(const char *repo, const char *head, 61extern void cgit_print_snapshot_links(const char *repo, const char *head,
62 const char *hex, int snapshots); 62 const char *hex, int snapshots);
63extern void cgit_add_hidden_formfields(int incl_head, int incl_search, 63extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
64 const char *page); 64 const char *page);
65#endif /* UI_SHARED_H */ 65#endif /* UI_SHARED_H */