summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2007-10-28 14:23:00 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2007-10-28 14:23:00 (UTC)
commit68ca032dbe7379f78775fb03ef34a9ad2abc409f (patch) (unidiff)
tree2209ae312eb932bc61b41ae9c774f6cfcc3dd372
parent6ec5f36f279a85f59db2851ab476d9acd0015770 (diff)
downloadcgit-68ca032dbe7379f78775fb03ef34a9ad2abc409f.zip
cgit-68ca032dbe7379f78775fb03ef34a9ad2abc409f.tar.gz
cgit-68ca032dbe7379f78775fb03ef34a9ad2abc409f.tar.bz2
Teach log search about --grep, --author and --committer
This makes the log searching more explicit, using a dropdown box to specify the commit field to match against. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.css19
-rw-r--r--cgit.h4
-rw-r--r--shared.c3
-rw-r--r--ui-log.c9
-rw-r--r--ui-shared.c9
-rw-r--r--ui-summary.c2
7 files changed, 39 insertions, 9 deletions
diff --git a/cgit.c b/cgit.c
index cc18ed4..142e416 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,259 +1,259 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
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 10
11static int cgit_prepare_cache(struct cacheitem *item) 11static int cgit_prepare_cache(struct cacheitem *item)
12{ 12{
13 if (!cgit_repo && cgit_query_repo) { 13 if (!cgit_repo && cgit_query_repo) {
14 char *title = fmt("%s - %s", cgit_root_title, "Bad request"); 14 char *title = fmt("%s - %s", cgit_root_title, "Bad request");
15 cgit_print_docstart(title, item); 15 cgit_print_docstart(title, item);
16 cgit_print_pageheader(title, 0); 16 cgit_print_pageheader(title, 0);
17 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo)); 17 cgit_print_error(fmt("Unknown repo: %s", cgit_query_repo));
18 cgit_print_docend(); 18 cgit_print_docend();
19 return 0; 19 return 0;
20 } 20 }
21 21
22 if (!cgit_repo) { 22 if (!cgit_repo) {
23 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root)); 23 item->name = xstrdup(fmt("%s/index.html", cgit_cache_root));
24 item->ttl = cgit_cache_root_ttl; 24 item->ttl = cgit_cache_root_ttl;
25 return 1; 25 return 1;
26 } 26 }
27 27
28 if (!cgit_cmd) { 28 if (!cgit_cmd) {
29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root, 29 item->name = xstrdup(fmt("%s/%s/index.%s.html", cgit_cache_root,
30 cache_safe_filename(cgit_repo->url), 30 cache_safe_filename(cgit_repo->url),
31 cache_safe_filename(cgit_querystring))); 31 cache_safe_filename(cgit_querystring)));
32 item->ttl = cgit_cache_repo_ttl; 32 item->ttl = cgit_cache_repo_ttl;
33 } else { 33 } else {
34 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root, 34 item->name = xstrdup(fmt("%s/%s/%s/%s.html", cgit_cache_root,
35 cache_safe_filename(cgit_repo->url), 35 cache_safe_filename(cgit_repo->url),
36 cgit_query_page, 36 cgit_query_page,
37 cache_safe_filename(cgit_querystring))); 37 cache_safe_filename(cgit_querystring)));
38 if (cgit_query_has_symref) 38 if (cgit_query_has_symref)
39 item->ttl = cgit_cache_dynamic_ttl; 39 item->ttl = cgit_cache_dynamic_ttl;
40 else if (cgit_query_has_sha1) 40 else if (cgit_query_has_sha1)
41 item->ttl = cgit_cache_static_ttl; 41 item->ttl = cgit_cache_static_ttl;
42 else 42 else
43 item->ttl = cgit_cache_repo_ttl; 43 item->ttl = cgit_cache_repo_ttl;
44 } 44 }
45 return 1; 45 return 1;
46} 46}
47 47
48static void cgit_print_repo_page(struct cacheitem *item) 48static void cgit_print_repo_page(struct cacheitem *item)
49{ 49{
50 char *title; 50 char *title;
51 int show_search; 51 int show_search;
52 52
53 if (!cgit_query_head) 53 if (!cgit_query_head)
54 cgit_query_head = cgit_repo->defbranch; 54 cgit_query_head = cgit_repo->defbranch;
55 55
56 if (chdir(cgit_repo->path)) { 56 if (chdir(cgit_repo->path)) {
57 title = fmt("%s - %s", cgit_root_title, "Bad request"); 57 title = fmt("%s - %s", cgit_root_title, "Bad request");
58 cgit_print_docstart(title, item); 58 cgit_print_docstart(title, item);
59 cgit_print_pageheader(title, 0); 59 cgit_print_pageheader(title, 0);
60 cgit_print_error(fmt("Unable to scan repository: %s", 60 cgit_print_error(fmt("Unable to scan repository: %s",
61 strerror(errno))); 61 strerror(errno)));
62 cgit_print_docend(); 62 cgit_print_docend();
63 return; 63 return;
64 } 64 }
65 65
66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc); 66 title = fmt("%s - %s", cgit_repo->name, cgit_repo->desc);
67 show_search = 0; 67 show_search = 0;
68 setenv("GIT_DIR", cgit_repo->path, 1); 68 setenv("GIT_DIR", cgit_repo->path, 1);
69 69
70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) { 70 if ((cgit_cmd == CMD_SNAPSHOT) && cgit_repo->snapshots) {
71 cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1, 71 cgit_print_snapshot(item, cgit_query_head, cgit_query_sha1,
72 cgit_repobasename(cgit_repo->url), 72 cgit_repobasename(cgit_repo->url),
73 cgit_query_path, 73 cgit_query_path,
74 cgit_repo->snapshots ); 74 cgit_repo->snapshots );
75 return; 75 return;
76 } 76 }
77 77
78 if (cgit_cmd == CMD_BLOB) { 78 if (cgit_cmd == CMD_BLOB) {
79 cgit_print_blob(item, cgit_query_sha1, cgit_query_path); 79 cgit_print_blob(item, cgit_query_sha1, cgit_query_path);
80 return; 80 return;
81 } 81 }
82 82
83 show_search = (cgit_cmd == CMD_LOG); 83 show_search = (cgit_cmd == CMD_LOG);
84 cgit_print_docstart(title, item); 84 cgit_print_docstart(title, item);
85 if (!cgit_cmd) { 85 if (!cgit_cmd) {
86 cgit_print_pageheader("summary", show_search); 86 cgit_print_pageheader("summary", show_search);
87 cgit_print_summary(); 87 cgit_print_summary();
88 cgit_print_docend(); 88 cgit_print_docend();
89 return; 89 return;
90 } 90 }
91 91
92 cgit_print_pageheader(cgit_query_page, show_search); 92 cgit_print_pageheader(cgit_query_page, show_search);
93 93
94 switch(cgit_cmd) { 94 switch(cgit_cmd) {
95 case CMD_LOG: 95 case CMD_LOG:
96 cgit_print_log(cgit_query_sha1, cgit_query_ofs, 96 cgit_print_log(cgit_query_sha1, cgit_query_ofs,
97 cgit_max_commit_count, cgit_query_search, 97 cgit_max_commit_count, cgit_query_grep, cgit_query_search,
98 cgit_query_path, 1); 98 cgit_query_path, 1);
99 break; 99 break;
100 case CMD_TREE: 100 case CMD_TREE:
101 cgit_print_tree(cgit_query_sha1, cgit_query_path); 101 cgit_print_tree(cgit_query_sha1, cgit_query_path);
102 break; 102 break;
103 case CMD_COMMIT: 103 case CMD_COMMIT:
104 cgit_print_commit(cgit_query_sha1); 104 cgit_print_commit(cgit_query_sha1);
105 break; 105 break;
106 case CMD_REFS: 106 case CMD_REFS:
107 cgit_print_refs(); 107 cgit_print_refs();
108 break; 108 break;
109 case CMD_TAG: 109 case CMD_TAG:
110 cgit_print_tag(cgit_query_sha1); 110 cgit_print_tag(cgit_query_sha1);
111 break; 111 break;
112 case CMD_DIFF: 112 case CMD_DIFF:
113 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path); 113 cgit_print_diff(cgit_query_sha1, cgit_query_sha2, cgit_query_path);
114 break; 114 break;
115 default: 115 default:
116 cgit_print_error("Invalid request"); 116 cgit_print_error("Invalid request");
117 } 117 }
118 cgit_print_docend(); 118 cgit_print_docend();
119} 119}
120 120
121static void cgit_fill_cache(struct cacheitem *item, int use_cache) 121static void cgit_fill_cache(struct cacheitem *item, int use_cache)
122{ 122{
123 static char buf[PATH_MAX]; 123 static char buf[PATH_MAX];
124 int stdout2; 124 int stdout2;
125 125
126 getcwd(buf, sizeof(buf)); 126 getcwd(buf, sizeof(buf));
127 item->st.st_mtime = time(NULL); 127 item->st.st_mtime = time(NULL);
128 128
129 if (use_cache) { 129 if (use_cache) {
130 stdout2 = chk_positive(dup(STDOUT_FILENO), 130 stdout2 = chk_positive(dup(STDOUT_FILENO),
131 "Preserving STDOUT"); 131 "Preserving STDOUT");
132 chk_zero(close(STDOUT_FILENO), "Closing STDOUT"); 132 chk_zero(close(STDOUT_FILENO), "Closing STDOUT");
133 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)"); 133 chk_positive(dup2(item->fd, STDOUT_FILENO), "Dup2(cachefile)");
134 } 134 }
135 135
136 if (cgit_repo) 136 if (cgit_repo)
137 cgit_print_repo_page(item); 137 cgit_print_repo_page(item);
138 else 138 else
139 cgit_print_repolist(item); 139 cgit_print_repolist(item);
140 140
141 if (use_cache) { 141 if (use_cache) {
142 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT"); 142 chk_zero(close(STDOUT_FILENO), "Close redirected STDOUT");
143 chk_positive(dup2(stdout2, STDOUT_FILENO), 143 chk_positive(dup2(stdout2, STDOUT_FILENO),
144 "Restoring original STDOUT"); 144 "Restoring original STDOUT");
145 chk_zero(close(stdout2), "Closing temporary STDOUT"); 145 chk_zero(close(stdout2), "Closing temporary STDOUT");
146 } 146 }
147 147
148 chdir(buf); 148 chdir(buf);
149} 149}
150 150
151static void cgit_check_cache(struct cacheitem *item) 151static void cgit_check_cache(struct cacheitem *item)
152{ 152{
153 int i = 0; 153 int i = 0;
154 154
155 top: 155 top:
156 if (++i > cgit_max_lock_attempts) { 156 if (++i > cgit_max_lock_attempts) {
157 die("cgit_refresh_cache: unable to lock %s: %s", 157 die("cgit_refresh_cache: unable to lock %s: %s",
158 item->name, strerror(errno)); 158 item->name, strerror(errno));
159 } 159 }
160 if (!cache_exist(item)) { 160 if (!cache_exist(item)) {
161 if (!cache_lock(item)) { 161 if (!cache_lock(item)) {
162 sleep(1); 162 sleep(1);
163 goto top; 163 goto top;
164 } 164 }
165 if (!cache_exist(item)) { 165 if (!cache_exist(item)) {
166 cgit_fill_cache(item, 1); 166 cgit_fill_cache(item, 1);
167 cache_unlock(item); 167 cache_unlock(item);
168 } else { 168 } else {
169 cache_cancel_lock(item); 169 cache_cancel_lock(item);
170 } 170 }
171 } else if (cache_expired(item) && cache_lock(item)) { 171 } else if (cache_expired(item) && cache_lock(item)) {
172 if (cache_expired(item)) { 172 if (cache_expired(item)) {
173 cgit_fill_cache(item, 1); 173 cgit_fill_cache(item, 1);
174 cache_unlock(item); 174 cache_unlock(item);
175 } else { 175 } else {
176 cache_cancel_lock(item); 176 cache_cancel_lock(item);
177 } 177 }
178 } 178 }
179} 179}
180 180
181static void cgit_print_cache(struct cacheitem *item) 181static void cgit_print_cache(struct cacheitem *item)
182{ 182{
183 static char buf[4096]; 183 static char buf[4096];
184 ssize_t i; 184 ssize_t i;
185 185
186 int fd = open(item->name, O_RDONLY); 186 int fd = open(item->name, O_RDONLY);
187 if (fd<0) 187 if (fd<0)
188 die("Unable to open cached file %s", item->name); 188 die("Unable to open cached file %s", item->name);
189 189
190 while((i=read(fd, buf, sizeof(buf))) > 0) 190 while((i=read(fd, buf, sizeof(buf))) > 0)
191 write(STDOUT_FILENO, buf, i); 191 write(STDOUT_FILENO, buf, i);
192 192
193 close(fd); 193 close(fd);
194} 194}
195 195
196static void cgit_parse_args(int argc, const char **argv) 196static void cgit_parse_args(int argc, const char **argv)
197{ 197{
198 int i; 198 int i;
199 199
200 for (i = 1; i < argc; i++) { 200 for (i = 1; i < argc; i++) {
201 if (!strncmp(argv[i], "--cache=", 8)) { 201 if (!strncmp(argv[i], "--cache=", 8)) {
202 cgit_cache_root = xstrdup(argv[i]+8); 202 cgit_cache_root = xstrdup(argv[i]+8);
203 } 203 }
204 if (!strcmp(argv[i], "--nocache")) { 204 if (!strcmp(argv[i], "--nocache")) {
205 cgit_nocache = 1; 205 cgit_nocache = 1;
206 } 206 }
207 if (!strncmp(argv[i], "--query=", 8)) { 207 if (!strncmp(argv[i], "--query=", 8)) {
208 cgit_querystring = xstrdup(argv[i]+8); 208 cgit_querystring = xstrdup(argv[i]+8);
209 } 209 }
210 if (!strncmp(argv[i], "--repo=", 7)) { 210 if (!strncmp(argv[i], "--repo=", 7)) {
211 cgit_query_repo = xstrdup(argv[i]+7); 211 cgit_query_repo = xstrdup(argv[i]+7);
212 } 212 }
213 if (!strncmp(argv[i], "--page=", 7)) { 213 if (!strncmp(argv[i], "--page=", 7)) {
214 cgit_query_page = xstrdup(argv[i]+7); 214 cgit_query_page = xstrdup(argv[i]+7);
215 } 215 }
216 if (!strncmp(argv[i], "--head=", 7)) { 216 if (!strncmp(argv[i], "--head=", 7)) {
217 cgit_query_head = xstrdup(argv[i]+7); 217 cgit_query_head = xstrdup(argv[i]+7);
218 cgit_query_has_symref = 1; 218 cgit_query_has_symref = 1;
219 } 219 }
220 if (!strncmp(argv[i], "--sha1=", 7)) { 220 if (!strncmp(argv[i], "--sha1=", 7)) {
221 cgit_query_sha1 = xstrdup(argv[i]+7); 221 cgit_query_sha1 = xstrdup(argv[i]+7);
222 cgit_query_has_sha1 = 1; 222 cgit_query_has_sha1 = 1;
223 } 223 }
224 if (!strncmp(argv[i], "--ofs=", 6)) { 224 if (!strncmp(argv[i], "--ofs=", 6)) {
225 cgit_query_ofs = atoi(argv[i]+6); 225 cgit_query_ofs = atoi(argv[i]+6);
226 } 226 }
227 } 227 }
228} 228}
229 229
230int main(int argc, const char **argv) 230int main(int argc, const char **argv)
231{ 231{
232 struct cacheitem item; 232 struct cacheitem item;
233 const char *cgit_config_env = getenv("CGIT_CONFIG"); 233 const char *cgit_config_env = getenv("CGIT_CONFIG");
234 234
235 htmlfd = STDOUT_FILENO; 235 htmlfd = STDOUT_FILENO;
236 item.st.st_mtime = time(NULL); 236 item.st.st_mtime = time(NULL);
237 cgit_repolist.length = 0; 237 cgit_repolist.length = 0;
238 cgit_repolist.count = 0; 238 cgit_repolist.count = 0;
239 cgit_repolist.repos = NULL; 239 cgit_repolist.repos = NULL;
240 240
241 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG, 241 cgit_read_config(cgit_config_env ? cgit_config_env : CGIT_CONFIG,
242 cgit_global_config_cb); 242 cgit_global_config_cb);
243 cgit_repo = NULL; 243 cgit_repo = NULL;
244 if (getenv("SCRIPT_NAME")) 244 if (getenv("SCRIPT_NAME"))
245 cgit_script_name = xstrdup(getenv("SCRIPT_NAME")); 245 cgit_script_name = xstrdup(getenv("SCRIPT_NAME"));
246 if (getenv("QUERY_STRING")) 246 if (getenv("QUERY_STRING"))
247 cgit_querystring = xstrdup(getenv("QUERY_STRING")); 247 cgit_querystring = xstrdup(getenv("QUERY_STRING"));
248 cgit_parse_args(argc, argv); 248 cgit_parse_args(argc, argv);
249 cgit_parse_query(cgit_querystring, cgit_querystring_cb); 249 cgit_parse_query(cgit_querystring, cgit_querystring_cb);
250 if (!cgit_prepare_cache(&item)) 250 if (!cgit_prepare_cache(&item))
251 return 0; 251 return 0;
252 if (cgit_nocache) { 252 if (cgit_nocache) {
253 cgit_fill_cache(&item, 0); 253 cgit_fill_cache(&item, 0);
254 } else { 254 } else {
255 cgit_check_cache(&item); 255 cgit_check_cache(&item);
256 cgit_print_cache(&item); 256 cgit_print_cache(&item);
257 } 257 }
258 return 0; 258 return 0;
259} 259}
diff --git a/cgit.css b/cgit.css
index b8c3d81..5d47099 100644
--- a/cgit.css
+++ b/cgit.css
@@ -1,437 +1,454 @@
1body { 1body {
2 font-family: arial, sans-serif; 2 font-family: arial, sans-serif;
3 font-size: 11pt; 3 font-size: 11pt;
4 color: black; 4 color: black;
5 background: white; 5 background: white;
6} 6}
7 7
8body, table { 8body, table {
9 padding: 0em; 9 padding: 0em;
10 margin: 0em; 10 margin: 0em;
11} 11}
12 12
13table { 13table {
14 border-collapse: collapse; 14 border-collapse: collapse;
15} 15}
16 16
17h2 { 17h2 {
18 font-size: 120%; 18 font-size: 120%;
19 font-weight: bold; 19 font-weight: bold;
20 margin-top: 0em; 20 margin-top: 0em;
21 margin-bottom: 0.25em; 21 margin-bottom: 0.25em;
22} 22}
23 23
24h3 { 24h3 {
25 margin-top: 0em; 25 margin-top: 0em;
26 font-size: 100%; 26 font-size: 100%;
27 font-weight: normal; 27 font-weight: normal;
28} 28}
29 29
30h4 { 30h4 {
31 margin-top: 1.5em; 31 margin-top: 1.5em;
32 margin-bottom: 0.1em; 32 margin-bottom: 0.1em;
33 font-size: 100%; 33 font-size: 100%;
34 font-weight: bold; 34 font-weight: bold;
35} 35}
36 36
37a { 37a {
38 color: blue; 38 color: blue;
39 text-decoration: none; 39 text-decoration: none;
40} 40}
41 41
42a:hover { 42a:hover {
43 text-decoration: underline; 43 text-decoration: underline;
44} 44}
45 45
46table.list { 46table.list {
47 border: none; 47 border: none;
48 border-collapse: collapse; 48 border-collapse: collapse;
49} 49}
50 50
51table.list tr { 51table.list tr {
52 background: white; 52 background: white;
53} 53}
54 54
55table.list tr:hover { 55table.list tr:hover {
56 background: #eee; 56 background: #eee;
57} 57}
58 58
59table.list tr.nohover:hover { 59table.list tr.nohover:hover {
60 background: white; 60 background: white;
61} 61}
62 62
63table.list th { 63table.list th {
64 font-weight: bold; 64 font-weight: bold;
65 border-bottom: solid 1px #777; 65 border-bottom: solid 1px #777;
66 padding: 0.1em 0.5em 0.1em 0.5em; 66 padding: 0.1em 0.5em 0.1em 0.5em;
67 vertical-align: baseline; 67 vertical-align: baseline;
68} 68}
69 69
70table.list td { 70table.list td {
71 border: none; 71 border: none;
72 padding: 0.1em 0.5em 0.1em 0.5em; 72 padding: 0.1em 0.5em 0.1em 0.5em;
73} 73}
74 74
75img { 75img {
76 border: none; 76 border: none;
77} 77}
78 78
79table#layout { 79table#layout {
80 width: 100%; 80 width: 100%;
81 border-collapse: collapse; 81 border-collapse: collapse;
82 margin: 0px; 82 margin: 0px;
83} 83}
84 84
85td#header, td#logo { 85td#header, td#logo {
86 color: #666; 86 color: #666;
87 background-color: #ddd; 87 background-color: #ddd;
88 border-bottom: solid 1px #000; 88 border-bottom: solid 1px #000;
89} 89}
90 90
91td#header { 91td#header {
92 font-size: 150%; 92 font-size: 150%;
93 font-weight: bold; 93 font-weight: bold;
94 padding: 0.2em 0.5em; 94 padding: 0.2em 0.5em;
95 vertical-align: text-bottom; 95 vertical-align: text-bottom;
96} 96}
97 97
98td#header a { 98td#header a {
99 color: #666; 99 color: #666;
100} 100}
101 101
102td#header a:hover { 102td#header a:hover {
103 text-decoration: underline; 103 text-decoration: underline;
104} 104}
105 105
106td#logo { 106td#logo {
107 text-align: right; 107 text-align: right;
108 vertical-align: middle; 108 vertical-align: middle;
109 padding-right: 0.5em; 109 padding-right: 0.5em;
110} 110}
111 111
112td#crumb, td#search { 112td#crumb, td#search {
113 color: #ccc; 113 color: #ccc;
114 border-top: solid 3px #555; 114 border-top: solid 3px #555;
115 background-color: #666; 115 background-color: #666;
116 border-bottom: solid 1px #333; 116 border-bottom: solid 1px #333;
117 padding: 2px 1em; 117 padding: 2px 1em;
118} 118}
119 119
120td#crumb { 120td#crumb {
121 font-weight: bold; 121 font-weight: bold;
122} 122}
123 123
124td#crumb a { 124td#crumb a {
125 color: #ccc; 125 color: #ccc;
126 background-color: #666; 126 background-color: #666;
127 padding: 0em 0.5em 0em 0.5em; 127 padding: 0em 0.5em 0em 0.5em;
128} 128}
129 129
130td#crumb a:hover { 130td#crumb a:hover {
131 color: #666; 131 color: #666;
132 background-color: #ccc; 132 background-color: #ccc;
133 text-decoration: none; 133 text-decoration: none;
134} 134}
135 135
136td#search { 136td#search {
137 text-align: right; 137 text-align: right;
138 vertical-align: middle; 138 vertical-align: middle;
139 padding-right: 0.5em; 139 padding-right: 0.5em;
140} 140}
141 141
142td#search form { 142td#search form {
143 margin: 0px; 143 margin: 0px;
144 padding: 0px; 144 padding: 0px;
145} 145}
146 146
147td#search select {
148 font-size: 9pt;
149 padding: 0px;
150 border: solid 1px #333;
151 color: #333;
152 background-color: #fff;
153}
154
147td#search input { 155td#search input {
148 font-size: 9pt; 156 font-size: 9pt;
149 padding: 0px; 157 padding: 0px;
150 width: 10em; 158}
159
160td#search input.txt {
161 width: 8em;
151 border: solid 1px #333; 162 border: solid 1px #333;
152 color: #333; 163 color: #333;
153 background-color: #fff; 164 background-color: #fff;
154} 165}
155 166
167td#search input.btn {
168 border: solid 1px #333;
169 color: #333;
170 background-color: #ccc;
171}
172
156div#summary { 173div#summary {
157 vertical-align: top; 174 vertical-align: top;
158 margin-bottom: 1em; 175 margin-bottom: 1em;
159} 176}
160 177
161table#downloads { 178table#downloads {
162 float: right; 179 float: right;
163 border-collapse: collapse; 180 border-collapse: collapse;
164 border: solid 1px #777; 181 border: solid 1px #777;
165 margin-left: 0.5em; 182 margin-left: 0.5em;
166 margin-bottom: 0.5em; 183 margin-bottom: 0.5em;
167} 184}
168 185
169table#downloads th { 186table#downloads th {
170 background-color: #ccc; 187 background-color: #ccc;
171} 188}
172 189
173td#content { 190td#content {
174 padding: 1em 0.5em; 191 padding: 1em 0.5em;
175} 192}
176 193
177div#blob { 194div#blob {
178 border: solid 1px black; 195 border: solid 1px black;
179} 196}
180 197
181div.error { 198div.error {
182 color: red; 199 color: red;
183 font-weight: bold; 200 font-weight: bold;
184 margin: 1em 2em; 201 margin: 1em 2em;
185} 202}
186 203
187a.ls-blob, a.ls-dir, a.ls-mod { 204a.ls-blob, a.ls-dir, a.ls-mod {
188 font-family: monospace; 205 font-family: monospace;
189} 206}
190 207
191td.ls-size { 208td.ls-size {
192 text-align: right; 209 text-align: right;
193} 210}
194 211
195td.ls-size { 212td.ls-size {
196 font-family: monospace; 213 font-family: monospace;
197} 214}
198 215
199td.ls-mode { 216td.ls-mode {
200 font-family: monospace; 217 font-family: monospace;
201} 218}
202 219
203table.blob { 220table.blob {
204 margin-top: 0.5em; 221 margin-top: 0.5em;
205 border-top: solid 1px black; 222 border-top: solid 1px black;
206} 223}
207 224
208table.blob td.no { 225table.blob td.no {
209 border-right: solid 1px black; 226 border-right: solid 1px black;
210 color: black; 227 color: black;
211 background-color: #eee; 228 background-color: #eee;
212 text-align: right; 229 text-align: right;
213} 230}
214 231
215table.blob td.no a { 232table.blob td.no a {
216 color: black; 233 color: black;
217} 234}
218 235
219table.blob td.no a:hover { 236table.blob td.no a:hover {
220 color: black; 237 color: black;
221 text-decoration: none; 238 text-decoration: none;
222} 239}
223 240
224table.blob td.txt { 241table.blob td.txt {
225 white-space: pre; 242 white-space: pre;
226 font-family: monospace; 243 font-family: monospace;
227 padding-left: 0.5em; 244 padding-left: 0.5em;
228} 245}
229 246
230table.nowrap td { 247table.nowrap td {
231 white-space: nowrap; 248 white-space: nowrap;
232} 249}
233 250
234table.commit-info { 251table.commit-info {
235 border-collapse: collapse; 252 border-collapse: collapse;
236 margin-top: 1.5em; 253 margin-top: 1.5em;
237} 254}
238 255
239table.commit-info th { 256table.commit-info th {
240 text-align: left; 257 text-align: left;
241 font-weight: normal; 258 font-weight: normal;
242 padding: 0.1em 1em 0.1em 0.1em; 259 padding: 0.1em 1em 0.1em 0.1em;
243 vertical-align: top; 260 vertical-align: top;
244} 261}
245 262
246table.commit-info td { 263table.commit-info td {
247 font-weight: normal; 264 font-weight: normal;
248 padding: 0.1em 1em 0.1em 0.1em; 265 padding: 0.1em 1em 0.1em 0.1em;
249} 266}
250 267
251div.commit-subject { 268div.commit-subject {
252 font-weight: bold; 269 font-weight: bold;
253 font-size: 125%; 270 font-size: 125%;
254 margin: 1.5em 0em 0.5em 0em; 271 margin: 1.5em 0em 0.5em 0em;
255 padding: 0em; 272 padding: 0em;
256} 273}
257 274
258div.commit-msg { 275div.commit-msg {
259 white-space: pre; 276 white-space: pre;
260 font-family: monospace; 277 font-family: monospace;
261} 278}
262 279
263div.diffstat-header { 280div.diffstat-header {
264 font-weight: bold; 281 font-weight: bold;
265 padding-top: 1.5em; 282 padding-top: 1.5em;
266} 283}
267 284
268table.diffstat { 285table.diffstat {
269 border-collapse: collapse; 286 border-collapse: collapse;
270 width: 100%; 287 width: 100%;
271 border: solid 1px #aaa; 288 border: solid 1px #aaa;
272 background-color: #eee; 289 background-color: #eee;
273} 290}
274 291
275table.diffstat th { 292table.diffstat th {
276 font-weight: normal; 293 font-weight: normal;
277 text-align: left; 294 text-align: left;
278 text-decoration: underline; 295 text-decoration: underline;
279 padding: 0.1em 1em 0.1em 0.1em; 296 padding: 0.1em 1em 0.1em 0.1em;
280 font-size: 100%; 297 font-size: 100%;
281} 298}
282 299
283table.diffstat td { 300table.diffstat td {
284 padding: 0.2em 0.2em 0.1em 0.1em; 301 padding: 0.2em 0.2em 0.1em 0.1em;
285 font-size: 100%; 302 font-size: 100%;
286 border: none; 303 border: none;
287} 304}
288 305
289table.diffstat td.mode { 306table.diffstat td.mode {
290 white-space: nowrap; 307 white-space: nowrap;
291} 308}
292 309
293table.diffstat td span.modechange { 310table.diffstat td span.modechange {
294 padding-left: 1em; 311 padding-left: 1em;
295 color: red; 312 color: red;
296} 313}
297 314
298table.diffstat td.add a { 315table.diffstat td.add a {
299 color: green; 316 color: green;
300} 317}
301 318
302table.diffstat td.del a { 319table.diffstat td.del a {
303 color: red; 320 color: red;
304} 321}
305 322
306table.diffstat td.upd a { 323table.diffstat td.upd a {
307 color: blue; 324 color: blue;
308} 325}
309 326
310table.diffstat td.graph { 327table.diffstat td.graph {
311 width: 75%; 328 width: 75%;
312 vertical-align: middle; 329 vertical-align: middle;
313} 330}
314 331
315table.diffstat td.graph table { 332table.diffstat td.graph table {
316 border: none; 333 border: none;
317} 334}
318 335
319table.diffstat td.graph td { 336table.diffstat td.graph td {
320 padding: 0px; 337 padding: 0px;
321 border: 0px; 338 border: 0px;
322 height: 7pt; 339 height: 7pt;
323} 340}
324 341
325table.diffstat td.graph td.add { 342table.diffstat td.graph td.add {
326 background-color: #5c5; 343 background-color: #5c5;
327} 344}
328 345
329table.diffstat td.graph td.rem { 346table.diffstat td.graph td.rem {
330 background-color: #c55; 347 background-color: #c55;
331} 348}
332 349
333div.diffstat-summary { 350div.diffstat-summary {
334 color: #888; 351 color: #888;
335 padding-top: 0.5em; 352 padding-top: 0.5em;
336} 353}
337 354
338table.diff { 355table.diff {
339 width: 100%; 356 width: 100%;
340} 357}
341 358
342table.diff td { 359table.diff td {
343 font-family: monospace; 360 font-family: monospace;
344 white-space: pre; 361 white-space: pre;
345} 362}
346 363
347table.diff td div.head { 364table.diff td div.head {
348 font-weight: bold; 365 font-weight: bold;
349 margin-top: 1em; 366 margin-top: 1em;
350 background-color: #eee; 367 background-color: #eee;
351} 368}
352 369
353table.diff td div.hunk { 370table.diff td div.hunk {
354 color: #009; 371 color: #009;
355} 372}
356 373
357table.diff td div.add { 374table.diff td div.add {
358 color: green; 375 color: green;
359} 376}
360 377
361table.diff td div.del { 378table.diff td div.del {
362 color: red; 379 color: red;
363} 380}
364 381
365.sha1 { 382.sha1 {
366 font-family: monospace; 383 font-family: monospace;
367 font-size: 90%; 384 font-size: 90%;
368} 385}
369 386
370.left { 387.left {
371 text-align: left; 388 text-align: left;
372} 389}
373 390
374.right { 391.right {
375 text-align: right; 392 text-align: right;
376} 393}
377 394
378table.list td.repogroup { 395table.list td.repogroup {
379 font-style: italic; 396 font-style: italic;
380 color: #888; 397 color: #888;
381} 398}
382 399
383a.button { 400a.button {
384 font-size: 80%; 401 font-size: 80%;
385 color: #aaa; 402 color: #aaa;
386 background-color: #eee; 403 background-color: #eee;
387 border: solid 1px #aaa; 404 border: solid 1px #aaa;
388 padding: 0em 0.5em; 405 padding: 0em 0.5em;
389 margin: 0.1em 0.25em; 406 margin: 0.1em 0.25em;
390} 407}
391 408
392a.button:hover { 409a.button:hover {
393 text-decoration: none; 410 text-decoration: none;
394 color: #333; 411 color: #333;
395 background-color: #ccc; 412 background-color: #ccc;
396} 413}
397 414
398a.primary { 415a.primary {
399 font-size: 100%; 416 font-size: 100%;
400} 417}
401 418
402a.secondary { 419a.secondary {
403 font-size: 90%; 420 font-size: 90%;
404} 421}
405 422
406td.toplevel-repo { 423td.toplevel-repo {
407 424
408} 425}
409 426
410table.list td.sublevel-repo { 427table.list td.sublevel-repo {
411 padding-left: 1.5em; 428 padding-left: 1.5em;
412} 429}
413 430
414span.age-mins { 431span.age-mins {
415 font-weight: bold; 432 font-weight: bold;
416 color: #080; 433 color: #080;
417} 434}
418 435
419span.age-hours { 436span.age-hours {
420 color: #080; 437 color: #080;
421} 438}
422 439
423span.age-days { 440span.age-days {
424 color: #040; 441 color: #040;
425} 442}
426 443
427span.age-weeks { 444span.age-weeks {
428 color: #444; 445 color: #444;
429} 446}
430 447
431span.age-months { 448span.age-months {
432 color: #888; 449 color: #888;
433} 450}
434 451
435span.age-years { 452span.age-years {
436 color: #bbb; 453 color: #bbb;
437} 454}
diff --git a/cgit.h b/cgit.h
index 0baa679..dd83f70 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,277 +1,279 @@
1#ifndef CGIT_H 1#ifndef CGIT_H
2#define CGIT_H 2#define CGIT_H
3 3
4 4
5#include <git-compat-util.h> 5#include <git-compat-util.h>
6#include <cache.h> 6#include <cache.h>
7#include <grep.h> 7#include <grep.h>
8#include <object.h> 8#include <object.h>
9#include <tree.h> 9#include <tree.h>
10#include <commit.h> 10#include <commit.h>
11#include <tag.h> 11#include <tag.h>
12#include <diff.h> 12#include <diff.h>
13#include <diffcore.h> 13#include <diffcore.h>
14#include <refs.h> 14#include <refs.h>
15#include <revision.h> 15#include <revision.h>
16#include <log-tree.h> 16#include <log-tree.h>
17#include <archive.h> 17#include <archive.h>
18#include <xdiff/xdiff.h> 18#include <xdiff/xdiff.h>
19 19
20 20
21/* 21/*
22 * The valid cgit repo-commands 22 * The valid cgit repo-commands
23 */ 23 */
24#define CMD_LOG 1 24#define CMD_LOG 1
25#define CMD_COMMIT 2 25#define CMD_COMMIT 2
26#define CMD_DIFF 3 26#define CMD_DIFF 3
27#define CMD_TREE 4 27#define CMD_TREE 4
28#define CMD_BLOB 5 28#define CMD_BLOB 5
29#define CMD_SNAPSHOT 6 29#define CMD_SNAPSHOT 6
30#define CMD_TAG 7 30#define CMD_TAG 7
31#define CMD_REFS 8 31#define CMD_REFS 8
32 32
33/* 33/*
34 * Dateformats used on misc. pages 34 * Dateformats used on misc. pages
35 */ 35 */
36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S" 36#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S"
37#define FMT_SHORTDATE "%Y-%m-%d" 37#define FMT_SHORTDATE "%Y-%m-%d"
38 38
39 39
40/* 40/*
41 * Limits used for relative dates 41 * Limits used for relative dates
42 */ 42 */
43#define TM_MIN 60 43#define TM_MIN 60
44#define TM_HOUR (TM_MIN * 60) 44#define TM_HOUR (TM_MIN * 60)
45#define TM_DAY (TM_HOUR * 24) 45#define TM_DAY (TM_HOUR * 24)
46#define TM_WEEK (TM_DAY * 7) 46#define TM_WEEK (TM_DAY * 7)
47#define TM_YEAR (TM_DAY * 365) 47#define TM_YEAR (TM_DAY * 365)
48#define TM_MONTH (TM_YEAR / 12.0) 48#define TM_MONTH (TM_YEAR / 12.0)
49 49
50 50
51typedef void (*configfn)(const char *name, const char *value); 51typedef void (*configfn)(const char *name, const char *value);
52typedef void (*filepair_fn)(struct diff_filepair *pair); 52typedef void (*filepair_fn)(struct diff_filepair *pair);
53typedef void (*linediff_fn)(char *line, int len); 53typedef void (*linediff_fn)(char *line, int len);
54 54
55struct cacheitem { 55struct cacheitem {
56 char *name; 56 char *name;
57 struct stat st; 57 struct stat st;
58 int ttl; 58 int ttl;
59 int fd; 59 int fd;
60}; 60};
61 61
62struct repoinfo { 62struct repoinfo {
63 char *url; 63 char *url;
64 char *name; 64 char *name;
65 char *path; 65 char *path;
66 char *desc; 66 char *desc;
67 char *owner; 67 char *owner;
68 char *defbranch; 68 char *defbranch;
69 char *group; 69 char *group;
70 char *module_link; 70 char *module_link;
71 char *readme; 71 char *readme;
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}; 75};
76 76
77struct repolist { 77struct repolist {
78 int length; 78 int length;
79 int count; 79 int count;
80 struct repoinfo *repos; 80 struct repoinfo *repos;
81}; 81};
82 82
83struct commitinfo { 83struct commitinfo {
84 struct commit *commit; 84 struct commit *commit;
85 char *author; 85 char *author;
86 char *author_email; 86 char *author_email;
87 unsigned long author_date; 87 unsigned long author_date;
88 char *committer; 88 char *committer;
89 char *committer_email; 89 char *committer_email;
90 unsigned long committer_date; 90 unsigned long committer_date;
91 char *subject; 91 char *subject;
92 char *msg; 92 char *msg;
93}; 93};
94 94
95struct taginfo { 95struct taginfo {
96 char *tagger; 96 char *tagger;
97 char *tagger_email; 97 char *tagger_email;
98 int tagger_date; 98 int tagger_date;
99 char *msg; 99 char *msg;
100}; 100};
101 101
102struct refinfo { 102struct refinfo {
103 const char *refname; 103 const char *refname;
104 struct object *object; 104 struct object *object;
105 union { 105 union {
106 struct taginfo *tag; 106 struct taginfo *tag;
107 struct commitinfo *commit; 107 struct commitinfo *commit;
108 }; 108 };
109}; 109};
110 110
111struct reflist { 111struct reflist {
112 struct refinfo **refs; 112 struct refinfo **refs;
113 int alloc; 113 int alloc;
114 int count; 114 int count;
115}; 115};
116 116
117extern const char *cgit_version; 117extern const char *cgit_version;
118 118
119extern struct repolist cgit_repolist; 119extern struct repolist cgit_repolist;
120extern struct repoinfo *cgit_repo; 120extern struct repoinfo *cgit_repo;
121extern int cgit_cmd; 121extern int cgit_cmd;
122 122
123extern char *cgit_root_title; 123extern char *cgit_root_title;
124extern char *cgit_css; 124extern char *cgit_css;
125extern char *cgit_logo; 125extern char *cgit_logo;
126extern char *cgit_index_header; 126extern char *cgit_index_header;
127extern char *cgit_logo_link; 127extern char *cgit_logo_link;
128extern char *cgit_module_link; 128extern char *cgit_module_link;
129extern char *cgit_agefile; 129extern char *cgit_agefile;
130extern char *cgit_virtual_root; 130extern char *cgit_virtual_root;
131extern char *cgit_script_name; 131extern char *cgit_script_name;
132extern char *cgit_cache_root; 132extern char *cgit_cache_root;
133extern char *cgit_repo_group; 133extern char *cgit_repo_group;
134 134
135extern int cgit_nocache; 135extern int cgit_nocache;
136extern int cgit_snapshots; 136extern int cgit_snapshots;
137extern int cgit_enable_index_links; 137extern int cgit_enable_index_links;
138extern int cgit_enable_log_filecount; 138extern int cgit_enable_log_filecount;
139extern int cgit_enable_log_linecount; 139extern int cgit_enable_log_linecount;
140extern int cgit_max_lock_attempts; 140extern int cgit_max_lock_attempts;
141extern int cgit_cache_root_ttl; 141extern int cgit_cache_root_ttl;
142extern int cgit_cache_repo_ttl; 142extern int cgit_cache_repo_ttl;
143extern int cgit_cache_dynamic_ttl; 143extern int cgit_cache_dynamic_ttl;
144extern int cgit_cache_static_ttl; 144extern int cgit_cache_static_ttl;
145extern int cgit_cache_max_create_time; 145extern int cgit_cache_max_create_time;
146extern int cgit_summary_log; 146extern int cgit_summary_log;
147extern int cgit_summary_tags; 147extern int cgit_summary_tags;
148extern int cgit_summary_branches; 148extern int cgit_summary_branches;
149 149
150extern int cgit_max_msg_len; 150extern int cgit_max_msg_len;
151extern int cgit_max_repodesc_len; 151extern int cgit_max_repodesc_len;
152extern int cgit_max_commit_count; 152extern int cgit_max_commit_count;
153 153
154extern int cgit_query_has_symref; 154extern int cgit_query_has_symref;
155extern int cgit_query_has_sha1; 155extern int cgit_query_has_sha1;
156 156
157extern char *cgit_querystring; 157extern char *cgit_querystring;
158extern char *cgit_query_repo; 158extern char *cgit_query_repo;
159extern char *cgit_query_page; 159extern char *cgit_query_page;
160extern char *cgit_query_search; 160extern char *cgit_query_search;
161extern char *cgit_query_grep;
161extern char *cgit_query_head; 162extern char *cgit_query_head;
162extern char *cgit_query_sha1; 163extern char *cgit_query_sha1;
163extern char *cgit_query_sha2; 164extern char *cgit_query_sha2;
164extern char *cgit_query_path; 165extern char *cgit_query_path;
165extern char *cgit_query_name; 166extern char *cgit_query_name;
166extern int cgit_query_ofs; 167extern int cgit_query_ofs;
167 168
168extern int htmlfd; 169extern int htmlfd;
169 170
170extern int cgit_get_cmd_index(const char *cmd); 171extern int cgit_get_cmd_index(const char *cmd);
171extern struct repoinfo *cgit_get_repoinfo(const char *url); 172extern struct repoinfo *cgit_get_repoinfo(const char *url);
172extern void cgit_global_config_cb(const char *name, const char *value); 173extern void cgit_global_config_cb(const char *name, const char *value);
173extern void cgit_repo_config_cb(const char *name, const char *value); 174extern void cgit_repo_config_cb(const char *name, const char *value);
174extern void cgit_querystring_cb(const char *name, const char *value); 175extern void cgit_querystring_cb(const char *name, const char *value);
175 176
176extern int chk_zero(int result, char *msg); 177extern int chk_zero(int result, char *msg);
177extern int chk_positive(int result, char *msg); 178extern int chk_positive(int result, char *msg);
178extern int chk_non_negative(int result, char *msg); 179extern int chk_non_negative(int result, char *msg);
179 180
180extern int hextoint(char c); 181extern int hextoint(char c);
181extern char *trim_end(const char *str, char c); 182extern char *trim_end(const char *str, char c);
182 183
183extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 184extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
184extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 185extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
185 int flags, void *cb_data); 186 int flags, void *cb_data);
186 187
187extern void *cgit_free_commitinfo(struct commitinfo *info); 188extern void *cgit_free_commitinfo(struct commitinfo *info);
188 189
189extern int cgit_diff_files(const unsigned char *old_sha1, 190extern int cgit_diff_files(const unsigned char *old_sha1,
190 const unsigned char *new_sha1, 191 const unsigned char *new_sha1,
191 linediff_fn fn); 192 linediff_fn fn);
192 193
193extern void cgit_diff_tree(const unsigned char *old_sha1, 194extern void cgit_diff_tree(const unsigned char *old_sha1,
194 const unsigned char *new_sha1, 195 const unsigned char *new_sha1,
195 filepair_fn fn, const char *prefix); 196 filepair_fn fn, const char *prefix);
196 197
197extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 198extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
198 199
199extern char *fmt(const char *format,...); 200extern char *fmt(const char *format,...);
200 201
201extern void html(const char *txt); 202extern void html(const char *txt);
202extern void htmlf(const char *format,...); 203extern void htmlf(const char *format,...);
203extern void html_txt(char *txt); 204extern void html_txt(char *txt);
204extern void html_ntxt(int len, char *txt); 205extern void html_ntxt(int len, char *txt);
205extern void html_attr(char *txt); 206extern void html_attr(char *txt);
206extern void html_hidden(char *name, char *value); 207extern void html_hidden(char *name, char *value);
207extern void html_option(char *value, char *text, char *selected_value); 208extern void html_option(char *value, char *text, char *selected_value);
208extern void html_link_open(char *url, char *title, char *class); 209extern void html_link_open(char *url, char *title, char *class);
209extern void html_link_close(void); 210extern void html_link_close(void);
210extern void html_filemode(unsigned short mode); 211extern void html_filemode(unsigned short mode);
211extern int html_include(const char *filename); 212extern int html_include(const char *filename);
212 213
213extern int cgit_read_config(const char *filename, configfn fn); 214extern int cgit_read_config(const char *filename, configfn fn);
214extern int cgit_parse_query(char *txt, configfn fn); 215extern int cgit_parse_query(char *txt, configfn fn);
215extern struct commitinfo *cgit_parse_commit(struct commit *commit); 216extern struct commitinfo *cgit_parse_commit(struct commit *commit);
216extern struct taginfo *cgit_parse_tag(struct tag *tag); 217extern struct taginfo *cgit_parse_tag(struct tag *tag);
217extern void cgit_parse_url(const char *url); 218extern void cgit_parse_url(const char *url);
218 219
219extern char *cache_safe_filename(const char *unsafe); 220extern char *cache_safe_filename(const char *unsafe);
220extern int cache_lock(struct cacheitem *item); 221extern int cache_lock(struct cacheitem *item);
221extern int cache_unlock(struct cacheitem *item); 222extern int cache_unlock(struct cacheitem *item);
222extern int cache_cancel_lock(struct cacheitem *item); 223extern int cache_cancel_lock(struct cacheitem *item);
223extern int cache_exist(struct cacheitem *item); 224extern int cache_exist(struct cacheitem *item);
224extern int cache_expired(struct cacheitem *item); 225extern int cache_expired(struct cacheitem *item);
225 226
226extern char *cgit_repourl(const char *reponame); 227extern char *cgit_repourl(const char *reponame);
227extern char *cgit_fileurl(const char *reponame, const char *pagename, 228extern char *cgit_fileurl(const char *reponame, const char *pagename,
228 const char *filename, const char *query); 229 const char *filename, const char *query);
229extern char *cgit_pageurl(const char *reponame, const char *pagename, 230extern char *cgit_pageurl(const char *reponame, const char *pagename,
230 const char *query); 231 const char *query);
231 232
232extern const char *cgit_repobasename(const char *reponame); 233extern const char *cgit_repobasename(const char *reponame);
233 234
234extern void cgit_tree_link(char *name, char *title, char *class, char *head, 235extern void cgit_tree_link(char *name, char *title, char *class, char *head,
235 char *rev, char *path); 236 char *rev, char *path);
236extern void cgit_log_link(char *name, char *title, char *class, char *head, 237extern void cgit_log_link(char *name, char *title, char *class, char *head,
237 char *rev, char *path, int ofs); 238 char *rev, char *path, int ofs);
238extern void cgit_commit_link(char *name, char *title, char *class, char *head, 239extern void cgit_commit_link(char *name, char *title, char *class, char *head,
239 char *rev); 240 char *rev);
240extern void cgit_refs_link(char *name, char *title, char *class, char *head, 241extern void cgit_refs_link(char *name, char *title, char *class, char *head,
241 char *rev, char *path); 242 char *rev, char *path);
242extern void cgit_snapshot_link(char *name, char *title, char *class, 243extern void cgit_snapshot_link(char *name, char *title, char *class,
243 char *head, char *rev, char *archivename); 244 char *head, char *rev, char *archivename);
244extern void cgit_diff_link(char *name, char *title, char *class, char *head, 245extern void cgit_diff_link(char *name, char *title, char *class, char *head,
245 char *new_rev, char *old_rev, char *path); 246 char *new_rev, char *old_rev, char *path);
246 247
247extern void cgit_object_link(struct object *obj); 248extern void cgit_object_link(struct object *obj);
248 249
249extern void cgit_print_error(char *msg); 250extern void cgit_print_error(char *msg);
250extern void cgit_print_date(time_t secs, char *format); 251extern void cgit_print_date(time_t secs, char *format);
251extern void cgit_print_age(time_t t, time_t max_relative, char *format); 252extern void cgit_print_age(time_t t, time_t max_relative, char *format);
252extern void cgit_print_docstart(char *title, struct cacheitem *item); 253extern void cgit_print_docstart(char *title, struct cacheitem *item);
253extern void cgit_print_docend(); 254extern void cgit_print_docend();
254extern void cgit_print_pageheader(char *title, int show_search); 255extern void cgit_print_pageheader(char *title, int show_search);
255extern void cgit_print_snapshot_start(const char *mimetype, 256extern void cgit_print_snapshot_start(const char *mimetype,
256 const char *filename, 257 const char *filename,
257 struct cacheitem *item); 258 struct cacheitem *item);
258extern void cgit_print_branches(int maxcount); 259extern void cgit_print_branches(int maxcount);
259extern void cgit_print_tags(int maxcount); 260extern void cgit_print_tags(int maxcount);
260 261
261extern void cgit_print_repolist(struct cacheitem *item); 262extern void cgit_print_repolist(struct cacheitem *item);
262extern void cgit_print_summary(); 263extern void cgit_print_summary();
263extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager); 264extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
265 char *pattern, char *path, int pager);
264extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path); 266extern void cgit_print_blob(struct cacheitem *item, const char *hex, char *path);
265extern void cgit_print_tree(const char *rev, char *path); 267extern void cgit_print_tree(const char *rev, char *path);
266extern void cgit_print_commit(char *hex); 268extern void cgit_print_commit(char *hex);
267extern void cgit_print_refs(); 269extern void cgit_print_refs();
268extern void cgit_print_tag(char *revname); 270extern void cgit_print_tag(char *revname);
269extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix); 271extern void cgit_print_diff(const char *new_hex, const char *old_hex, const char *prefix);
270extern void cgit_print_snapshot(struct cacheitem *item, const char *head, 272extern void cgit_print_snapshot(struct cacheitem *item, const char *head,
271 const char *hex, const char *prefix, 273 const char *hex, const char *prefix,
272 const char *filename, int snapshot); 274 const char *filename, int snapshot);
273extern void cgit_print_snapshot_links(const char *repo, const char *head, 275extern void cgit_print_snapshot_links(const char *repo, const char *head,
274 const char *hex, int snapshots); 276 const char *hex, int snapshots);
275extern int cgit_parse_snapshots_mask(const char *str); 277extern int cgit_parse_snapshots_mask(const char *str);
276 278
277#endif /* CGIT_H */ 279#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index 7eb2b0e..4fab1c9 100644
--- a/shared.c
+++ b/shared.c
@@ -1,472 +1,475 @@
1/* shared.c: global vars + some callback functions 1/* shared.c: global vars + some callback functions
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 10
11struct repolist cgit_repolist; 11struct repolist cgit_repolist;
12struct repoinfo *cgit_repo; 12struct repoinfo *cgit_repo;
13int cgit_cmd; 13int cgit_cmd;
14 14
15const char *cgit_version = CGIT_VERSION; 15const char *cgit_version = CGIT_VERSION;
16 16
17char *cgit_root_title = "Git repository browser"; 17char *cgit_root_title = "Git repository browser";
18char *cgit_css = "/cgit.css"; 18char *cgit_css = "/cgit.css";
19char *cgit_logo = "/git-logo.png"; 19char *cgit_logo = "/git-logo.png";
20char *cgit_index_header = NULL; 20char *cgit_index_header = NULL;
21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/"; 21char *cgit_logo_link = "http://www.kernel.org/pub/software/scm/git/docs/";
22char *cgit_module_link = "./?repo=%s&page=commit&id=%s"; 22char *cgit_module_link = "./?repo=%s&page=commit&id=%s";
23char *cgit_agefile = "info/web/last-modified"; 23char *cgit_agefile = "info/web/last-modified";
24char *cgit_virtual_root = NULL; 24char *cgit_virtual_root = NULL;
25char *cgit_script_name = CGIT_SCRIPT_NAME; 25char *cgit_script_name = CGIT_SCRIPT_NAME;
26char *cgit_cache_root = CGIT_CACHE_ROOT; 26char *cgit_cache_root = CGIT_CACHE_ROOT;
27char *cgit_repo_group = NULL; 27char *cgit_repo_group = NULL;
28 28
29int cgit_nocache = 0; 29int cgit_nocache = 0;
30int cgit_snapshots = 0; 30int cgit_snapshots = 0;
31int cgit_enable_index_links = 0; 31int cgit_enable_index_links = 0;
32int cgit_enable_log_filecount = 0; 32int cgit_enable_log_filecount = 0;
33int cgit_enable_log_linecount = 0; 33int cgit_enable_log_linecount = 0;
34int cgit_max_lock_attempts = 5; 34int cgit_max_lock_attempts = 5;
35int cgit_cache_root_ttl = 5; 35int cgit_cache_root_ttl = 5;
36int cgit_cache_repo_ttl = 5; 36int cgit_cache_repo_ttl = 5;
37int cgit_cache_dynamic_ttl = 5; 37int cgit_cache_dynamic_ttl = 5;
38int cgit_cache_static_ttl = -1; 38int cgit_cache_static_ttl = -1;
39int cgit_cache_max_create_time = 5; 39int cgit_cache_max_create_time = 5;
40int cgit_summary_log = 0; 40int cgit_summary_log = 0;
41int cgit_summary_tags = 0; 41int cgit_summary_tags = 0;
42int cgit_summary_branches = 0; 42int cgit_summary_branches = 0;
43int cgit_renamelimit = -1; 43int cgit_renamelimit = -1;
44 44
45int cgit_max_msg_len = 60; 45int cgit_max_msg_len = 60;
46int cgit_max_repodesc_len = 60; 46int cgit_max_repodesc_len = 60;
47int cgit_max_commit_count = 50; 47int cgit_max_commit_count = 50;
48 48
49int cgit_query_has_symref = 0; 49int cgit_query_has_symref = 0;
50int cgit_query_has_sha1 = 0; 50int cgit_query_has_sha1 = 0;
51 51
52char *cgit_querystring = NULL; 52char *cgit_querystring = NULL;
53char *cgit_query_repo = NULL; 53char *cgit_query_repo = NULL;
54char *cgit_query_page = NULL; 54char *cgit_query_page = NULL;
55char *cgit_query_head = NULL; 55char *cgit_query_head = NULL;
56char *cgit_query_search = NULL; 56char *cgit_query_search = NULL;
57char *cgit_query_grep = NULL;
57char *cgit_query_sha1 = NULL; 58char *cgit_query_sha1 = NULL;
58char *cgit_query_sha2 = NULL; 59char *cgit_query_sha2 = NULL;
59char *cgit_query_path = NULL; 60char *cgit_query_path = NULL;
60char *cgit_query_name = NULL; 61char *cgit_query_name = NULL;
61int cgit_query_ofs = 0; 62int cgit_query_ofs = 0;
62 63
63int htmlfd = 0; 64int htmlfd = 0;
64 65
65 66
66int cgit_get_cmd_index(const char *cmd) 67int cgit_get_cmd_index(const char *cmd)
67{ 68{
68 static char *cmds[] = {"log", "commit", "diff", "tree", "blob", 69 static char *cmds[] = {"log", "commit", "diff", "tree", "blob",
69 "snapshot", "tag", "refs", NULL}; 70 "snapshot", "tag", "refs", NULL};
70 int i; 71 int i;
71 72
72 for(i = 0; cmds[i]; i++) 73 for(i = 0; cmds[i]; i++)
73 if (!strcmp(cmd, cmds[i])) 74 if (!strcmp(cmd, cmds[i]))
74 return i + 1; 75 return i + 1;
75 return 0; 76 return 0;
76} 77}
77 78
78int chk_zero(int result, char *msg) 79int chk_zero(int result, char *msg)
79{ 80{
80 if (result != 0) 81 if (result != 0)
81 die("%s: %s", msg, strerror(errno)); 82 die("%s: %s", msg, strerror(errno));
82 return result; 83 return result;
83} 84}
84 85
85int chk_positive(int result, char *msg) 86int chk_positive(int result, char *msg)
86{ 87{
87 if (result <= 0) 88 if (result <= 0)
88 die("%s: %s", msg, strerror(errno)); 89 die("%s: %s", msg, strerror(errno));
89 return result; 90 return result;
90} 91}
91 92
92int chk_non_negative(int result, char *msg) 93int chk_non_negative(int result, char *msg)
93{ 94{
94 if (result < 0) 95 if (result < 0)
95 die("%s: %s",msg, strerror(errno)); 96 die("%s: %s",msg, strerror(errno));
96 return result; 97 return result;
97} 98}
98 99
99struct repoinfo *add_repo(const char *url) 100struct repoinfo *add_repo(const char *url)
100{ 101{
101 struct repoinfo *ret; 102 struct repoinfo *ret;
102 103
103 if (++cgit_repolist.count > cgit_repolist.length) { 104 if (++cgit_repolist.count > cgit_repolist.length) {
104 if (cgit_repolist.length == 0) 105 if (cgit_repolist.length == 0)
105 cgit_repolist.length = 8; 106 cgit_repolist.length = 8;
106 else 107 else
107 cgit_repolist.length *= 2; 108 cgit_repolist.length *= 2;
108 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 109 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
109 cgit_repolist.length * 110 cgit_repolist.length *
110 sizeof(struct repoinfo)); 111 sizeof(struct repoinfo));
111 } 112 }
112 113
113 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 114 ret = &cgit_repolist.repos[cgit_repolist.count-1];
114 ret->url = trim_end(url, '/'); 115 ret->url = trim_end(url, '/');
115 ret->name = ret->url; 116 ret->name = ret->url;
116 ret->path = NULL; 117 ret->path = NULL;
117 ret->desc = NULL; 118 ret->desc = NULL;
118 ret->owner = NULL; 119 ret->owner = NULL;
119 ret->group = cgit_repo_group; 120 ret->group = cgit_repo_group;
120 ret->defbranch = "master"; 121 ret->defbranch = "master";
121 ret->snapshots = cgit_snapshots; 122 ret->snapshots = cgit_snapshots;
122 ret->enable_log_filecount = cgit_enable_log_filecount; 123 ret->enable_log_filecount = cgit_enable_log_filecount;
123 ret->enable_log_linecount = cgit_enable_log_linecount; 124 ret->enable_log_linecount = cgit_enable_log_linecount;
124 ret->module_link = cgit_module_link; 125 ret->module_link = cgit_module_link;
125 ret->readme = NULL; 126 ret->readme = NULL;
126 return ret; 127 return ret;
127} 128}
128 129
129struct repoinfo *cgit_get_repoinfo(const char *url) 130struct repoinfo *cgit_get_repoinfo(const char *url)
130{ 131{
131 int i; 132 int i;
132 struct repoinfo *repo; 133 struct repoinfo *repo;
133 134
134 for (i=0; i<cgit_repolist.count; i++) { 135 for (i=0; i<cgit_repolist.count; i++) {
135 repo = &cgit_repolist.repos[i]; 136 repo = &cgit_repolist.repos[i];
136 if (!strcmp(repo->url, url)) 137 if (!strcmp(repo->url, url))
137 return repo; 138 return repo;
138 } 139 }
139 return NULL; 140 return NULL;
140} 141}
141 142
142void cgit_global_config_cb(const char *name, const char *value) 143void cgit_global_config_cb(const char *name, const char *value)
143{ 144{
144 if (!strcmp(name, "root-title")) 145 if (!strcmp(name, "root-title"))
145 cgit_root_title = xstrdup(value); 146 cgit_root_title = xstrdup(value);
146 else if (!strcmp(name, "css")) 147 else if (!strcmp(name, "css"))
147 cgit_css = xstrdup(value); 148 cgit_css = xstrdup(value);
148 else if (!strcmp(name, "logo")) 149 else if (!strcmp(name, "logo"))
149 cgit_logo = xstrdup(value); 150 cgit_logo = xstrdup(value);
150 else if (!strcmp(name, "index-header")) 151 else if (!strcmp(name, "index-header"))
151 cgit_index_header = xstrdup(value); 152 cgit_index_header = xstrdup(value);
152 else if (!strcmp(name, "logo-link")) 153 else if (!strcmp(name, "logo-link"))
153 cgit_logo_link = xstrdup(value); 154 cgit_logo_link = xstrdup(value);
154 else if (!strcmp(name, "module-link")) 155 else if (!strcmp(name, "module-link"))
155 cgit_module_link = xstrdup(value); 156 cgit_module_link = xstrdup(value);
156 else if (!strcmp(name, "virtual-root")) 157 else if (!strcmp(name, "virtual-root"))
157 cgit_virtual_root = trim_end(value, '/'); 158 cgit_virtual_root = trim_end(value, '/');
158 else if (!strcmp(name, "nocache")) 159 else if (!strcmp(name, "nocache"))
159 cgit_nocache = atoi(value); 160 cgit_nocache = atoi(value);
160 else if (!strcmp(name, "snapshots")) 161 else if (!strcmp(name, "snapshots"))
161 cgit_snapshots = cgit_parse_snapshots_mask(value); 162 cgit_snapshots = cgit_parse_snapshots_mask(value);
162 else if (!strcmp(name, "enable-index-links")) 163 else if (!strcmp(name, "enable-index-links"))
163 cgit_enable_index_links = atoi(value); 164 cgit_enable_index_links = atoi(value);
164 else if (!strcmp(name, "enable-log-filecount")) 165 else if (!strcmp(name, "enable-log-filecount"))
165 cgit_enable_log_filecount = atoi(value); 166 cgit_enable_log_filecount = atoi(value);
166 else if (!strcmp(name, "enable-log-linecount")) 167 else if (!strcmp(name, "enable-log-linecount"))
167 cgit_enable_log_linecount = atoi(value); 168 cgit_enable_log_linecount = atoi(value);
168 else if (!strcmp(name, "cache-root")) 169 else if (!strcmp(name, "cache-root"))
169 cgit_cache_root = xstrdup(value); 170 cgit_cache_root = xstrdup(value);
170 else if (!strcmp(name, "cache-root-ttl")) 171 else if (!strcmp(name, "cache-root-ttl"))
171 cgit_cache_root_ttl = atoi(value); 172 cgit_cache_root_ttl = atoi(value);
172 else if (!strcmp(name, "cache-repo-ttl")) 173 else if (!strcmp(name, "cache-repo-ttl"))
173 cgit_cache_repo_ttl = atoi(value); 174 cgit_cache_repo_ttl = atoi(value);
174 else if (!strcmp(name, "cache-static-ttl")) 175 else if (!strcmp(name, "cache-static-ttl"))
175 cgit_cache_static_ttl = atoi(value); 176 cgit_cache_static_ttl = atoi(value);
176 else if (!strcmp(name, "cache-dynamic-ttl")) 177 else if (!strcmp(name, "cache-dynamic-ttl"))
177 cgit_cache_dynamic_ttl = atoi(value); 178 cgit_cache_dynamic_ttl = atoi(value);
178 else if (!strcmp(name, "max-message-length")) 179 else if (!strcmp(name, "max-message-length"))
179 cgit_max_msg_len = atoi(value); 180 cgit_max_msg_len = atoi(value);
180 else if (!strcmp(name, "max-repodesc-length")) 181 else if (!strcmp(name, "max-repodesc-length"))
181 cgit_max_repodesc_len = atoi(value); 182 cgit_max_repodesc_len = atoi(value);
182 else if (!strcmp(name, "max-commit-count")) 183 else if (!strcmp(name, "max-commit-count"))
183 cgit_max_commit_count = atoi(value); 184 cgit_max_commit_count = atoi(value);
184 else if (!strcmp(name, "summary-log")) 185 else if (!strcmp(name, "summary-log"))
185 cgit_summary_log = atoi(value); 186 cgit_summary_log = atoi(value);
186 else if (!strcmp(name, "summary-branches")) 187 else if (!strcmp(name, "summary-branches"))
187 cgit_summary_branches = atoi(value); 188 cgit_summary_branches = atoi(value);
188 else if (!strcmp(name, "summary-tags")) 189 else if (!strcmp(name, "summary-tags"))
189 cgit_summary_tags = atoi(value); 190 cgit_summary_tags = atoi(value);
190 else if (!strcmp(name, "agefile")) 191 else if (!strcmp(name, "agefile"))
191 cgit_agefile = xstrdup(value); 192 cgit_agefile = xstrdup(value);
192 else if (!strcmp(name, "renamelimit")) 193 else if (!strcmp(name, "renamelimit"))
193 cgit_renamelimit = atoi(value); 194 cgit_renamelimit = atoi(value);
194 else if (!strcmp(name, "repo.group")) 195 else if (!strcmp(name, "repo.group"))
195 cgit_repo_group = xstrdup(value); 196 cgit_repo_group = xstrdup(value);
196 else if (!strcmp(name, "repo.url")) 197 else if (!strcmp(name, "repo.url"))
197 cgit_repo = add_repo(value); 198 cgit_repo = add_repo(value);
198 else if (!strcmp(name, "repo.name")) 199 else if (!strcmp(name, "repo.name"))
199 cgit_repo->name = xstrdup(value); 200 cgit_repo->name = xstrdup(value);
200 else if (cgit_repo && !strcmp(name, "repo.path")) 201 else if (cgit_repo && !strcmp(name, "repo.path"))
201 cgit_repo->path = trim_end(value, '/'); 202 cgit_repo->path = trim_end(value, '/');
202 else if (cgit_repo && !strcmp(name, "repo.desc")) 203 else if (cgit_repo && !strcmp(name, "repo.desc"))
203 cgit_repo->desc = xstrdup(value); 204 cgit_repo->desc = xstrdup(value);
204 else if (cgit_repo && !strcmp(name, "repo.owner")) 205 else if (cgit_repo && !strcmp(name, "repo.owner"))
205 cgit_repo->owner = xstrdup(value); 206 cgit_repo->owner = xstrdup(value);
206 else if (cgit_repo && !strcmp(name, "repo.defbranch")) 207 else if (cgit_repo && !strcmp(name, "repo.defbranch"))
207 cgit_repo->defbranch = xstrdup(value); 208 cgit_repo->defbranch = xstrdup(value);
208 else if (cgit_repo && !strcmp(name, "repo.snapshots")) 209 else if (cgit_repo && !strcmp(name, "repo.snapshots"))
209 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 210 cgit_repo->snapshots = cgit_snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
210 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount")) 211 else if (cgit_repo && !strcmp(name, "repo.enable-log-filecount"))
211 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value); 212 cgit_repo->enable_log_filecount = cgit_enable_log_filecount * atoi(value);
212 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount")) 213 else if (cgit_repo && !strcmp(name, "repo.enable-log-linecount"))
213 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value); 214 cgit_repo->enable_log_linecount = cgit_enable_log_linecount * atoi(value);
214 else if (cgit_repo && !strcmp(name, "repo.module-link")) 215 else if (cgit_repo && !strcmp(name, "repo.module-link"))
215 cgit_repo->module_link= xstrdup(value); 216 cgit_repo->module_link= xstrdup(value);
216 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) { 217 else if (cgit_repo && !strcmp(name, "repo.readme") && value != NULL) {
217 if (*value == '/') 218 if (*value == '/')
218 cgit_repo->readme = xstrdup(value); 219 cgit_repo->readme = xstrdup(value);
219 else 220 else
220 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value)); 221 cgit_repo->readme = xstrdup(fmt("%s/%s", cgit_repo->path, value));
221 } else if (!strcmp(name, "include")) 222 } else if (!strcmp(name, "include"))
222 cgit_read_config(value, cgit_global_config_cb); 223 cgit_read_config(value, cgit_global_config_cb);
223} 224}
224 225
225void cgit_querystring_cb(const char *name, const char *value) 226void cgit_querystring_cb(const char *name, const char *value)
226{ 227{
227 if (!strcmp(name,"r")) { 228 if (!strcmp(name,"r")) {
228 cgit_query_repo = xstrdup(value); 229 cgit_query_repo = xstrdup(value);
229 cgit_repo = cgit_get_repoinfo(value); 230 cgit_repo = cgit_get_repoinfo(value);
230 } else if (!strcmp(name, "p")) { 231 } else if (!strcmp(name, "p")) {
231 cgit_query_page = xstrdup(value); 232 cgit_query_page = xstrdup(value);
232 cgit_cmd = cgit_get_cmd_index(value); 233 cgit_cmd = cgit_get_cmd_index(value);
233 } else if (!strcmp(name, "url")) { 234 } else if (!strcmp(name, "url")) {
234 cgit_parse_url(value); 235 cgit_parse_url(value);
236 } else if (!strcmp(name, "qt")) {
237 cgit_query_grep = xstrdup(value);
235 } else if (!strcmp(name, "q")) { 238 } else if (!strcmp(name, "q")) {
236 cgit_query_search = xstrdup(value); 239 cgit_query_search = xstrdup(value);
237 } else if (!strcmp(name, "h")) { 240 } else if (!strcmp(name, "h")) {
238 cgit_query_head = xstrdup(value); 241 cgit_query_head = xstrdup(value);
239 cgit_query_has_symref = 1; 242 cgit_query_has_symref = 1;
240 } else if (!strcmp(name, "id")) { 243 } else if (!strcmp(name, "id")) {
241 cgit_query_sha1 = xstrdup(value); 244 cgit_query_sha1 = xstrdup(value);
242 cgit_query_has_sha1 = 1; 245 cgit_query_has_sha1 = 1;
243 } else if (!strcmp(name, "id2")) { 246 } else if (!strcmp(name, "id2")) {
244 cgit_query_sha2 = xstrdup(value); 247 cgit_query_sha2 = xstrdup(value);
245 cgit_query_has_sha1 = 1; 248 cgit_query_has_sha1 = 1;
246 } else if (!strcmp(name, "ofs")) { 249 } else if (!strcmp(name, "ofs")) {
247 cgit_query_ofs = atoi(value); 250 cgit_query_ofs = atoi(value);
248 } else if (!strcmp(name, "path")) { 251 } else if (!strcmp(name, "path")) {
249 cgit_query_path = trim_end(value, '/'); 252 cgit_query_path = trim_end(value, '/');
250 } else if (!strcmp(name, "name")) { 253 } else if (!strcmp(name, "name")) {
251 cgit_query_name = xstrdup(value); 254 cgit_query_name = xstrdup(value);
252 } 255 }
253} 256}
254 257
255void *cgit_free_commitinfo(struct commitinfo *info) 258void *cgit_free_commitinfo(struct commitinfo *info)
256{ 259{
257 free(info->author); 260 free(info->author);
258 free(info->author_email); 261 free(info->author_email);
259 free(info->committer); 262 free(info->committer);
260 free(info->committer_email); 263 free(info->committer_email);
261 free(info->subject); 264 free(info->subject);
262 free(info); 265 free(info);
263 return NULL; 266 return NULL;
264} 267}
265 268
266int hextoint(char c) 269int hextoint(char c)
267{ 270{
268 if (c >= 'a' && c <= 'f') 271 if (c >= 'a' && c <= 'f')
269 return 10 + c - 'a'; 272 return 10 + c - 'a';
270 else if (c >= 'A' && c <= 'F') 273 else if (c >= 'A' && c <= 'F')
271 return 10 + c - 'A'; 274 return 10 + c - 'A';
272 else if (c >= '0' && c <= '9') 275 else if (c >= '0' && c <= '9')
273 return c - '0'; 276 return c - '0';
274 else 277 else
275 return -1; 278 return -1;
276} 279}
277 280
278char *trim_end(const char *str, char c) 281char *trim_end(const char *str, char c)
279{ 282{
280 int len; 283 int len;
281 char *s, *t; 284 char *s, *t;
282 285
283 if (str == NULL) 286 if (str == NULL)
284 return NULL; 287 return NULL;
285 t = (char *)str; 288 t = (char *)str;
286 len = strlen(t); 289 len = strlen(t);
287 while(len > 0 && t[len - 1] == c) 290 while(len > 0 && t[len - 1] == c)
288 len--; 291 len--;
289 292
290 if (len == 0) 293 if (len == 0)
291 return NULL; 294 return NULL;
292 295
293 c = t[len]; 296 c = t[len];
294 t[len] = '\0'; 297 t[len] = '\0';
295 s = xstrdup(t); 298 s = xstrdup(t);
296 t[len] = c; 299 t[len] = c;
297 return s; 300 return s;
298} 301}
299 302
300void cgit_add_ref(struct reflist *list, struct refinfo *ref) 303void cgit_add_ref(struct reflist *list, struct refinfo *ref)
301{ 304{
302 size_t size; 305 size_t size;
303 306
304 if (list->count >= list->alloc) { 307 if (list->count >= list->alloc) {
305 list->alloc += (list->alloc ? list->alloc : 4); 308 list->alloc += (list->alloc ? list->alloc : 4);
306 size = list->alloc * sizeof(struct refinfo *); 309 size = list->alloc * sizeof(struct refinfo *);
307 list->refs = xrealloc(list->refs, size); 310 list->refs = xrealloc(list->refs, size);
308 } 311 }
309 list->refs[list->count++] = ref; 312 list->refs[list->count++] = ref;
310} 313}
311 314
312struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 315struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
313{ 316{
314 struct refinfo *ref; 317 struct refinfo *ref;
315 318
316 ref = xmalloc(sizeof (struct refinfo)); 319 ref = xmalloc(sizeof (struct refinfo));
317 ref->refname = xstrdup(refname); 320 ref->refname = xstrdup(refname);
318 ref->object = parse_object(sha1); 321 ref->object = parse_object(sha1);
319 switch (ref->object->type) { 322 switch (ref->object->type) {
320 case OBJ_TAG: 323 case OBJ_TAG:
321 ref->tag = cgit_parse_tag((struct tag *)ref->object); 324 ref->tag = cgit_parse_tag((struct tag *)ref->object);
322 break; 325 break;
323 case OBJ_COMMIT: 326 case OBJ_COMMIT:
324 ref->commit = cgit_parse_commit((struct commit *)ref->object); 327 ref->commit = cgit_parse_commit((struct commit *)ref->object);
325 break; 328 break;
326 } 329 }
327 return ref; 330 return ref;
328} 331}
329 332
330int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, 333int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
331 void *cb_data) 334 void *cb_data)
332{ 335{
333 struct reflist *list = (struct reflist *)cb_data; 336 struct reflist *list = (struct reflist *)cb_data;
334 struct refinfo *info = cgit_mk_refinfo(refname, sha1); 337 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
335 338
336 if (info) 339 if (info)
337 cgit_add_ref(list, info); 340 cgit_add_ref(list, info);
338 return 0; 341 return 0;
339} 342}
340 343
341void cgit_diff_tree_cb(struct diff_queue_struct *q, 344void cgit_diff_tree_cb(struct diff_queue_struct *q,
342 struct diff_options *options, void *data) 345 struct diff_options *options, void *data)
343{ 346{
344 int i; 347 int i;
345 348
346 for (i = 0; i < q->nr; i++) { 349 for (i = 0; i < q->nr; i++) {
347 if (q->queue[i]->status == 'U') 350 if (q->queue[i]->status == 'U')
348 continue; 351 continue;
349 ((filepair_fn)data)(q->queue[i]); 352 ((filepair_fn)data)(q->queue[i]);
350 } 353 }
351} 354}
352 355
353static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 356static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
354{ 357{
355 enum object_type type; 358 enum object_type type;
356 359
357 if (is_null_sha1(sha1)) { 360 if (is_null_sha1(sha1)) {
358 file->ptr = (char *)""; 361 file->ptr = (char *)"";
359 file->size = 0; 362 file->size = 0;
360 } else { 363 } else {
361 file->ptr = read_sha1_file(sha1, &type, 364 file->ptr = read_sha1_file(sha1, &type,
362 (unsigned long *)&file->size); 365 (unsigned long *)&file->size);
363 } 366 }
364 return 1; 367 return 1;
365} 368}
366 369
367/* 370/*
368 * Receive diff-buffers from xdiff and concatenate them as 371 * Receive diff-buffers from xdiff and concatenate them as
369 * needed across multiple callbacks. 372 * needed across multiple callbacks.
370 * 373 *
371 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 374 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
372 * ripped from git and modified to use globals instead of 375 * ripped from git and modified to use globals instead of
373 * a special callback-struct. 376 * a special callback-struct.
374 */ 377 */
375char *diffbuf = NULL; 378char *diffbuf = NULL;
376int buflen = 0; 379int buflen = 0;
377 380
378int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 381int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
379{ 382{
380 int i; 383 int i;
381 384
382 for (i = 0; i < nbuf; i++) { 385 for (i = 0; i < nbuf; i++) {
383 if (mb[i].ptr[mb[i].size-1] != '\n') { 386 if (mb[i].ptr[mb[i].size-1] != '\n') {
384 /* Incomplete line */ 387 /* Incomplete line */
385 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 388 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
386 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 389 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
387 buflen += mb[i].size; 390 buflen += mb[i].size;
388 continue; 391 continue;
389 } 392 }
390 393
391 /* we have a complete line */ 394 /* we have a complete line */
392 if (!diffbuf) { 395 if (!diffbuf) {
393 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 396 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
394 continue; 397 continue;
395 } 398 }
396 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 399 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
397 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 400 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
398 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 401 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
399 free(diffbuf); 402 free(diffbuf);
400 diffbuf = NULL; 403 diffbuf = NULL;
401 buflen = 0; 404 buflen = 0;
402 } 405 }
403 if (diffbuf) { 406 if (diffbuf) {
404 ((linediff_fn)priv)(diffbuf, buflen); 407 ((linediff_fn)priv)(diffbuf, buflen);
405 free(diffbuf); 408 free(diffbuf);
406 diffbuf = NULL; 409 diffbuf = NULL;
407 buflen = 0; 410 buflen = 0;
408 } 411 }
409 return 0; 412 return 0;
410} 413}
411 414
412int cgit_diff_files(const unsigned char *old_sha1, 415int cgit_diff_files(const unsigned char *old_sha1,
413 const unsigned char *new_sha1, 416 const unsigned char *new_sha1,
414 linediff_fn fn) 417 linediff_fn fn)
415{ 418{
416 mmfile_t file1, file2; 419 mmfile_t file1, file2;
417 xpparam_t diff_params; 420 xpparam_t diff_params;
418 xdemitconf_t emit_params; 421 xdemitconf_t emit_params;
419 xdemitcb_t emit_cb; 422 xdemitcb_t emit_cb;
420 423
421 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 424 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
422 return 1; 425 return 1;
423 426
424 diff_params.flags = XDF_NEED_MINIMAL; 427 diff_params.flags = XDF_NEED_MINIMAL;
425 emit_params.ctxlen = 3; 428 emit_params.ctxlen = 3;
426 emit_params.flags = XDL_EMIT_FUNCNAMES; 429 emit_params.flags = XDL_EMIT_FUNCNAMES;
427 emit_params.find_func = NULL; 430 emit_params.find_func = NULL;
428 emit_cb.outf = filediff_cb; 431 emit_cb.outf = filediff_cb;
429 emit_cb.priv = fn; 432 emit_cb.priv = fn;
430 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 433 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
431 return 0; 434 return 0;
432} 435}
433 436
434void cgit_diff_tree(const unsigned char *old_sha1, 437void cgit_diff_tree(const unsigned char *old_sha1,
435 const unsigned char *new_sha1, 438 const unsigned char *new_sha1,
436 filepair_fn fn, const char *prefix) 439 filepair_fn fn, const char *prefix)
437{ 440{
438 struct diff_options opt; 441 struct diff_options opt;
439 int ret; 442 int ret;
440 int prefixlen; 443 int prefixlen;
441 444
442 diff_setup(&opt); 445 diff_setup(&opt);
443 opt.output_format = DIFF_FORMAT_CALLBACK; 446 opt.output_format = DIFF_FORMAT_CALLBACK;
444 opt.detect_rename = 1; 447 opt.detect_rename = 1;
445 opt.rename_limit = cgit_renamelimit; 448 opt.rename_limit = cgit_renamelimit;
446 opt.recursive = 1; 449 opt.recursive = 1;
447 opt.format_callback = cgit_diff_tree_cb; 450 opt.format_callback = cgit_diff_tree_cb;
448 opt.format_callback_data = fn; 451 opt.format_callback_data = fn;
449 if (prefix) { 452 if (prefix) {
450 opt.nr_paths = 1; 453 opt.nr_paths = 1;
451 opt.paths = &prefix; 454 opt.paths = &prefix;
452 prefixlen = strlen(prefix); 455 prefixlen = strlen(prefix);
453 opt.pathlens = &prefixlen; 456 opt.pathlens = &prefixlen;
454 } 457 }
455 diff_setup_done(&opt); 458 diff_setup_done(&opt);
456 459
457 if (old_sha1 && !is_null_sha1(old_sha1)) 460 if (old_sha1 && !is_null_sha1(old_sha1))
458 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 461 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
459 else 462 else
460 ret = diff_root_tree_sha1(new_sha1, "", &opt); 463 ret = diff_root_tree_sha1(new_sha1, "", &opt);
461 diffcore_std(&opt); 464 diffcore_std(&opt);
462 diff_flush(&opt); 465 diff_flush(&opt);
463} 466}
464 467
465void cgit_diff_commit(struct commit *commit, filepair_fn fn) 468void cgit_diff_commit(struct commit *commit, filepair_fn fn)
466{ 469{
467 unsigned char *old_sha1 = NULL; 470 unsigned char *old_sha1 = NULL;
468 471
469 if (commit->parents) 472 if (commit->parents)
470 old_sha1 = commit->parents->item->object.sha1; 473 old_sha1 = commit->parents->item->object.sha1;
471 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); 474 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
472} 475}
diff --git a/ui-log.c b/ui-log.c
index d38e40a..e7f7d6f 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,128 +1,131 @@
1/* ui-log.c: functions for log output 1/* ui-log.c: functions for log output
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 10
11int files, lines; 11int files, lines;
12 12
13void count_lines(char *line, int size) 13void count_lines(char *line, int size)
14{ 14{
15 if (size>0 && (line[0] == '+' || line[0] == '-')) 15 if (size>0 && (line[0] == '+' || line[0] == '-'))
16 lines++; 16 lines++;
17} 17}
18 18
19void inspect_files(struct diff_filepair *pair) 19void inspect_files(struct diff_filepair *pair)
20{ 20{
21 files++; 21 files++;
22 if (cgit_repo->enable_log_linecount) 22 if (cgit_repo->enable_log_linecount)
23 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines); 23 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines);
24} 24}
25 25
26void print_commit(struct commit *commit) 26void print_commit(struct commit *commit)
27{ 27{
28 struct commitinfo *info; 28 struct commitinfo *info;
29 29
30 info = cgit_parse_commit(commit); 30 info = cgit_parse_commit(commit);
31 html("<tr><td>"); 31 html("<tr><td>");
32 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 32 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
33 html("</td><td>"); 33 html("</td><td>");
34 cgit_commit_link(info->subject, NULL, NULL, cgit_query_head, 34 cgit_commit_link(info->subject, NULL, NULL, cgit_query_head,
35 sha1_to_hex(commit->object.sha1)); 35 sha1_to_hex(commit->object.sha1));
36 if (cgit_repo->enable_log_filecount) { 36 if (cgit_repo->enable_log_filecount) {
37 files = 0; 37 files = 0;
38 lines = 0; 38 lines = 0;
39 cgit_diff_commit(commit, inspect_files); 39 cgit_diff_commit(commit, inspect_files);
40 html("</td><td class='right'>"); 40 html("</td><td class='right'>");
41 htmlf("%d", files); 41 htmlf("%d", files);
42 if (cgit_repo->enable_log_linecount) { 42 if (cgit_repo->enable_log_linecount) {
43 html("</td><td class='right'>"); 43 html("</td><td class='right'>");
44 htmlf("%d", lines); 44 htmlf("%d", lines);
45 } 45 }
46 } 46 }
47 html("</td><td>"); 47 html("</td><td>");
48 html_txt(info->author); 48 html_txt(info->author);
49 html("</td></tr>\n"); 49 html("</td></tr>\n");
50 cgit_free_commitinfo(info); 50 cgit_free_commitinfo(info);
51} 51}
52 52
53 53
54void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *path, int pager) 54void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager)
55{ 55{
56 struct rev_info rev; 56 struct rev_info rev;
57 struct commit *commit; 57 struct commit *commit;
58 const char *argv[] = {NULL, tip, NULL, NULL, NULL}; 58 const char *argv[] = {NULL, tip, NULL, NULL, NULL};
59 int argc = 2; 59 int argc = 2;
60 int i; 60 int i;
61 61
62 if (!tip) 62 if (!tip)
63 argv[1] = cgit_query_head; 63 argv[1] = cgit_query_head;
64 64
65 if (grep) 65 if (grep && pattern && (!strcmp(grep, "grep") ||
66 argv[argc++] = fmt("--grep=%s", grep); 66 !strcmp(grep, "author") ||
67 !strcmp(grep, "committer")))
68 argv[argc++] = fmt("--%s=%s", grep, pattern);
69
67 if (path) { 70 if (path) {
68 argv[argc++] = "--"; 71 argv[argc++] = "--";
69 argv[argc++] = path; 72 argv[argc++] = path;
70 } 73 }
71 init_revisions(&rev, NULL); 74 init_revisions(&rev, NULL);
72 rev.abbrev = DEFAULT_ABBREV; 75 rev.abbrev = DEFAULT_ABBREV;
73 rev.commit_format = CMIT_FMT_DEFAULT; 76 rev.commit_format = CMIT_FMT_DEFAULT;
74 rev.verbose_header = 1; 77 rev.verbose_header = 1;
75 rev.show_root_diff = 0; 78 rev.show_root_diff = 0;
76 setup_revisions(argc, argv, &rev, NULL); 79 setup_revisions(argc, argv, &rev, NULL);
77 if (rev.grep_filter) { 80 if (rev.grep_filter) {
78 rev.grep_filter->regflags |= REG_ICASE; 81 rev.grep_filter->regflags |= REG_ICASE;
79 compile_grep_patterns(rev.grep_filter); 82 compile_grep_patterns(rev.grep_filter);
80 } 83 }
81 prepare_revision_walk(&rev); 84 prepare_revision_walk(&rev);
82 85
83 html("<table class='list nowrap'>"); 86 html("<table class='list nowrap'>");
84 html("<tr class='nohover'><th class='left'>Age</th>" 87 html("<tr class='nohover'><th class='left'>Age</th>"
85 "<th class='left'>Message</th>"); 88 "<th class='left'>Message</th>");
86 89
87 if (cgit_repo->enable_log_filecount) { 90 if (cgit_repo->enable_log_filecount) {
88 html("<th class='left'>Files</th>"); 91 html("<th class='left'>Files</th>");
89 if (cgit_repo->enable_log_linecount) 92 if (cgit_repo->enable_log_linecount)
90 html("<th class='left'>Lines</th>"); 93 html("<th class='left'>Lines</th>");
91 } 94 }
92 html("<th class='left'>Author</th></tr>\n"); 95 html("<th class='left'>Author</th></tr>\n");
93 96
94 if (ofs<0) 97 if (ofs<0)
95 ofs = 0; 98 ofs = 0;
96 99
97 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { 100 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
98 free(commit->buffer); 101 free(commit->buffer);
99 commit->buffer = NULL; 102 commit->buffer = NULL;
100 free_commit_list(commit->parents); 103 free_commit_list(commit->parents);
101 commit->parents = NULL; 104 commit->parents = NULL;
102 } 105 }
103 106
104 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { 107 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
105 print_commit(commit); 108 print_commit(commit);
106 free(commit->buffer); 109 free(commit->buffer);
107 commit->buffer = NULL; 110 commit->buffer = NULL;
108 free_commit_list(commit->parents); 111 free_commit_list(commit->parents);
109 commit->parents = NULL; 112 commit->parents = NULL;
110 } 113 }
111 html("</table>\n"); 114 html("</table>\n");
112 115
113 if (pager) { 116 if (pager) {
114 html("<div class='pager'>"); 117 html("<div class='pager'>");
115 if (ofs > 0) { 118 if (ofs > 0) {
116 cgit_log_link("[prev]", NULL, NULL, cgit_query_head, 119 cgit_log_link("[prev]", NULL, NULL, cgit_query_head,
117 cgit_query_sha1, cgit_query_path, 120 cgit_query_sha1, cgit_query_path,
118 ofs - cnt); 121 ofs - cnt);
119 html("&nbsp;"); 122 html("&nbsp;");
120 } 123 }
121 if ((commit = get_revision(&rev)) != NULL) { 124 if ((commit = get_revision(&rev)) != NULL) {
122 cgit_log_link("[next]", NULL, NULL, cgit_query_head, 125 cgit_log_link("[next]", NULL, NULL, cgit_query_head,
123 cgit_query_sha1, cgit_query_path, 126 cgit_query_sha1, cgit_query_path,
124 ofs + cnt); 127 ofs + cnt);
125 } 128 }
126 html("</div>"); 129 html("</div>");
127 } 130 }
128} 131}
diff --git a/ui-shared.c b/ui-shared.c
index e4bb98f..45105dc 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -36,404 +36,409 @@ static long ttl_seconds(long ttl)
36void cgit_print_error(char *msg) 36void cgit_print_error(char *msg)
37{ 37{
38 html("<div class='error'>"); 38 html("<div class='error'>");
39 html_txt(msg); 39 html_txt(msg);
40 html("</div>\n"); 40 html("</div>\n");
41} 41}
42 42
43char *cgit_rooturl() 43char *cgit_rooturl()
44{ 44{
45 if (cgit_virtual_root) 45 if (cgit_virtual_root)
46 return fmt("%s/", cgit_virtual_root); 46 return fmt("%s/", cgit_virtual_root);
47 else 47 else
48 return cgit_script_name; 48 return cgit_script_name;
49} 49}
50 50
51char *cgit_repourl(const char *reponame) 51char *cgit_repourl(const char *reponame)
52{ 52{
53 if (cgit_virtual_root) { 53 if (cgit_virtual_root) {
54 return fmt("%s/%s/", cgit_virtual_root, reponame); 54 return fmt("%s/%s/", cgit_virtual_root, reponame);
55 } else { 55 } else {
56 return fmt("?r=%s", reponame); 56 return fmt("?r=%s", reponame);
57 } 57 }
58} 58}
59 59
60char *cgit_fileurl(const char *reponame, const char *pagename, 60char *cgit_fileurl(const char *reponame, const char *pagename,
61 const char *filename, const char *query) 61 const char *filename, const char *query)
62{ 62{
63 if (cgit_virtual_root) { 63 if (cgit_virtual_root) {
64 if (query) 64 if (query)
65 return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame, 65 return fmt("%s/%s/%s/%s?%s", cgit_virtual_root, reponame,
66 pagename, filename?filename:"", query); 66 pagename, filename?filename:"", query);
67 else 67 else
68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame, 68 return fmt("%s/%s/%s/", cgit_virtual_root, reponame,
69 pagename); 69 pagename);
70 } else { 70 } else {
71 if (query) 71 if (query)
72 return fmt("?r=%s&amp;p=%s&amp;%s", reponame, pagename, query); 72 return fmt("?r=%s&amp;p=%s&amp;%s", reponame, pagename, query);
73 else 73 else
74 return fmt("?r=%s&amp;p=%s", reponame, pagename); 74 return fmt("?r=%s&amp;p=%s", reponame, pagename);
75 } 75 }
76} 76}
77 77
78char *cgit_pageurl(const char *reponame, const char *pagename, 78char *cgit_pageurl(const char *reponame, const char *pagename,
79 const char *query) 79 const char *query)
80{ 80{
81 return cgit_fileurl(reponame,pagename,0,query); 81 return cgit_fileurl(reponame,pagename,0,query);
82} 82}
83 83
84const char *cgit_repobasename(const char *reponame) 84const char *cgit_repobasename(const char *reponame)
85{ 85{
86 /* I assume we don't need to store more than one repo basename */ 86 /* I assume we don't need to store more than one repo basename */
87 static char rvbuf[1024]; 87 static char rvbuf[1024];
88 int p; 88 int p;
89 const char *rv; 89 const char *rv;
90 strncpy(rvbuf,reponame,sizeof(rvbuf)); 90 strncpy(rvbuf,reponame,sizeof(rvbuf));
91 if(rvbuf[sizeof(rvbuf)-1]) 91 if(rvbuf[sizeof(rvbuf)-1])
92 die("cgit_repobasename: truncated repository name '%s'", reponame); 92 die("cgit_repobasename: truncated repository name '%s'", reponame);
93 p = strlen(rvbuf)-1; 93 p = strlen(rvbuf)-1;
94 /* strip trailing slashes */ 94 /* strip trailing slashes */
95 while(p && rvbuf[p]=='/') rvbuf[p--]=0; 95 while(p && rvbuf[p]=='/') rvbuf[p--]=0;
96 /* strip trailing .git */ 96 /* strip trailing .git */
97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { 97 if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) {
98 p -= 3; rvbuf[p--] = 0; 98 p -= 3; rvbuf[p--] = 0;
99 } 99 }
100 /* strip more trailing slashes if any */ 100 /* strip more trailing slashes if any */
101 while( p && rvbuf[p]=='/') rvbuf[p--]=0; 101 while( p && rvbuf[p]=='/') rvbuf[p--]=0;
102 /* find last slash in the remaining string */ 102 /* find last slash in the remaining string */
103 rv = strrchr(rvbuf,'/'); 103 rv = strrchr(rvbuf,'/');
104 if(rv) 104 if(rv)
105 return ++rv; 105 return ++rv;
106 return rvbuf; 106 return rvbuf;
107} 107}
108 108
109char *cgit_currurl() 109char *cgit_currurl()
110{ 110{
111 if (!cgit_virtual_root) 111 if (!cgit_virtual_root)
112 return cgit_script_name; 112 return cgit_script_name;
113 else if (cgit_query_page) 113 else if (cgit_query_page)
114 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page); 114 return fmt("%s/%s/%s/", cgit_virtual_root, cgit_query_repo, cgit_query_page);
115 else if (cgit_query_repo) 115 else if (cgit_query_repo)
116 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo); 116 return fmt("%s/%s/", cgit_virtual_root, cgit_query_repo);
117 else 117 else
118 return fmt("%s/", cgit_virtual_root); 118 return fmt("%s/", cgit_virtual_root);
119} 119}
120 120
121static char *repolink(char *title, char *class, char *page, char *head, 121static char *repolink(char *title, char *class, char *page, char *head,
122 char *path) 122 char *path)
123{ 123{
124 char *delim = "?"; 124 char *delim = "?";
125 125
126 html("<a"); 126 html("<a");
127 if (title) { 127 if (title) {
128 html(" title='"); 128 html(" title='");
129 html_attr(title); 129 html_attr(title);
130 html("'"); 130 html("'");
131 } 131 }
132 if (class) { 132 if (class) {
133 html(" class='"); 133 html(" class='");
134 html_attr(class); 134 html_attr(class);
135 html("'"); 135 html("'");
136 } 136 }
137 html(" href='"); 137 html(" href='");
138 if (cgit_virtual_root) { 138 if (cgit_virtual_root) {
139 html_attr(cgit_virtual_root); 139 html_attr(cgit_virtual_root);
140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/') 140 if (cgit_virtual_root[strlen(cgit_virtual_root) - 1] != '/')
141 html("/"); 141 html("/");
142 html_attr(cgit_repo->url); 142 html_attr(cgit_repo->url);
143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 143 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
144 html("/"); 144 html("/");
145 if (page) { 145 if (page) {
146 html(page); 146 html(page);
147 html("/"); 147 html("/");
148 if (path) 148 if (path)
149 html_attr(path); 149 html_attr(path);
150 } 150 }
151 } else { 151 } else {
152 html(cgit_script_name); 152 html(cgit_script_name);
153 html("?url="); 153 html("?url=");
154 html_attr(cgit_repo->url); 154 html_attr(cgit_repo->url);
155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/') 155 if (cgit_repo->url[strlen(cgit_repo->url) - 1] != '/')
156 html("/"); 156 html("/");
157 if (page) { 157 if (page) {
158 html(page); 158 html(page);
159 html("/"); 159 html("/");
160 if (path) 160 if (path)
161 html_attr(path); 161 html_attr(path);
162 } 162 }
163 delim = "&amp;"; 163 delim = "&amp;";
164 } 164 }
165 if (head && strcmp(head, cgit_repo->defbranch)) { 165 if (head && strcmp(head, cgit_repo->defbranch)) {
166 html(delim); 166 html(delim);
167 html("h="); 167 html("h=");
168 html_attr(head); 168 html_attr(head);
169 delim = "&amp;"; 169 delim = "&amp;";
170 } 170 }
171 return fmt("%s", delim); 171 return fmt("%s", delim);
172} 172}
173 173
174static void reporevlink(char *page, char *name, char *title, char *class, 174static void reporevlink(char *page, char *name, char *title, char *class,
175 char *head, char *rev, char *path) 175 char *head, char *rev, char *path)
176{ 176{
177 char *delim; 177 char *delim;
178 178
179 delim = repolink(title, class, page, head, path); 179 delim = repolink(title, class, page, head, path);
180 if (rev && strcmp(rev, cgit_query_head)) { 180 if (rev && strcmp(rev, cgit_query_head)) {
181 html(delim); 181 html(delim);
182 html("id="); 182 html("id=");
183 html_attr(rev); 183 html_attr(rev);
184 } 184 }
185 html("'>"); 185 html("'>");
186 html_txt(name); 186 html_txt(name);
187 html("</a>"); 187 html("</a>");
188} 188}
189 189
190void cgit_tree_link(char *name, char *title, char *class, char *head, 190void cgit_tree_link(char *name, char *title, char *class, char *head,
191 char *rev, char *path) 191 char *rev, char *path)
192{ 192{
193 reporevlink("tree", name, title, class, head, rev, path); 193 reporevlink("tree", name, title, class, head, rev, path);
194} 194}
195 195
196void cgit_log_link(char *name, char *title, char *class, char *head, 196void cgit_log_link(char *name, char *title, char *class, char *head,
197 char *rev, char *path, int ofs) 197 char *rev, char *path, int ofs)
198{ 198{
199 char *delim; 199 char *delim;
200 200
201 delim = repolink(title, class, "log", head, path); 201 delim = repolink(title, class, "log", head, path);
202 if (rev && strcmp(rev, cgit_query_head)) { 202 if (rev && strcmp(rev, cgit_query_head)) {
203 html(delim); 203 html(delim);
204 html("id="); 204 html("id=");
205 html_attr(rev); 205 html_attr(rev);
206 delim = "&"; 206 delim = "&";
207 } 207 }
208 if (ofs > 0) { 208 if (ofs > 0) {
209 html(delim); 209 html(delim);
210 html("ofs="); 210 html("ofs=");
211 htmlf("%d", ofs); 211 htmlf("%d", ofs);
212 } 212 }
213 html("'>"); 213 html("'>");
214 html_txt(name); 214 html_txt(name);
215 html("</a>"); 215 html("</a>");
216} 216}
217 217
218void cgit_commit_link(char *name, char *title, char *class, char *head, 218void cgit_commit_link(char *name, char *title, char *class, char *head,
219 char *rev) 219 char *rev)
220{ 220{
221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) { 221 if (strlen(name) > cgit_max_msg_len && cgit_max_msg_len >= 15) {
222 name[cgit_max_msg_len] = '\0'; 222 name[cgit_max_msg_len] = '\0';
223 name[cgit_max_msg_len - 1] = '.'; 223 name[cgit_max_msg_len - 1] = '.';
224 name[cgit_max_msg_len - 2] = '.'; 224 name[cgit_max_msg_len - 2] = '.';
225 name[cgit_max_msg_len - 3] = '.'; 225 name[cgit_max_msg_len - 3] = '.';
226 } 226 }
227 reporevlink("commit", name, title, class, head, rev, NULL); 227 reporevlink("commit", name, title, class, head, rev, NULL);
228} 228}
229 229
230void cgit_refs_link(char *name, char *title, char *class, char *head, 230void cgit_refs_link(char *name, char *title, char *class, char *head,
231 char *rev, char *path) 231 char *rev, char *path)
232{ 232{
233 reporevlink("refs", name, title, class, head, rev, path); 233 reporevlink("refs", name, title, class, head, rev, path);
234} 234}
235 235
236void cgit_snapshot_link(char *name, char *title, char *class, char *head, 236void cgit_snapshot_link(char *name, char *title, char *class, char *head,
237 char *rev, char *archivename) 237 char *rev, char *archivename)
238{ 238{
239 reporevlink("snapshot", name, title, class, head, rev, archivename); 239 reporevlink("snapshot", name, title, class, head, rev, archivename);
240} 240}
241 241
242void cgit_diff_link(char *name, char *title, char *class, char *head, 242void cgit_diff_link(char *name, char *title, char *class, char *head,
243 char *new_rev, char *old_rev, char *path) 243 char *new_rev, char *old_rev, char *path)
244{ 244{
245 char *delim; 245 char *delim;
246 246
247 delim = repolink(title, class, "diff", head, path); 247 delim = repolink(title, class, "diff", head, path);
248 if (new_rev && strcmp(new_rev, cgit_query_head)) { 248 if (new_rev && strcmp(new_rev, cgit_query_head)) {
249 html(delim); 249 html(delim);
250 html("id="); 250 html("id=");
251 html_attr(new_rev); 251 html_attr(new_rev);
252 delim = "&amp;"; 252 delim = "&amp;";
253 } 253 }
254 if (old_rev) { 254 if (old_rev) {
255 html(delim); 255 html(delim);
256 html("id2="); 256 html("id2=");
257 html_attr(old_rev); 257 html_attr(old_rev);
258 } 258 }
259 html("'>"); 259 html("'>");
260 html_txt(name); 260 html_txt(name);
261 html("</a>"); 261 html("</a>");
262} 262}
263 263
264void cgit_object_link(struct object *obj) 264void cgit_object_link(struct object *obj)
265{ 265{
266 char *page, *arg, *url; 266 char *page, *arg, *url;
267 267
268 if (obj->type == OBJ_COMMIT) { 268 if (obj->type == OBJ_COMMIT) {
269 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, 269 cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL,
270 cgit_query_head, sha1_to_hex(obj->sha1)); 270 cgit_query_head, sha1_to_hex(obj->sha1));
271 return; 271 return;
272 } else if (obj->type == OBJ_TREE) { 272 } else if (obj->type == OBJ_TREE) {
273 page = "tree"; 273 page = "tree";
274 arg = "id"; 274 arg = "id";
275 } else { 275 } else {
276 page = "blob"; 276 page = "blob";
277 arg = "id"; 277 arg = "id";
278 } 278 }
279 279
280 url = cgit_pageurl(cgit_query_repo, page, 280 url = cgit_pageurl(cgit_query_repo, page,
281 fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); 281 fmt("%s=%s", arg, sha1_to_hex(obj->sha1)));
282 html_link_open(url, NULL, NULL); 282 html_link_open(url, NULL, NULL);
283 htmlf("%s %s", typename(obj->type), 283 htmlf("%s %s", typename(obj->type),
284 sha1_to_hex(obj->sha1)); 284 sha1_to_hex(obj->sha1));
285 html_link_close(); 285 html_link_close();
286} 286}
287 287
288void cgit_print_date(time_t secs, char *format) 288void cgit_print_date(time_t secs, char *format)
289{ 289{
290 char buf[64]; 290 char buf[64];
291 struct tm *time; 291 struct tm *time;
292 292
293 time = gmtime(&secs); 293 time = gmtime(&secs);
294 strftime(buf, sizeof(buf)-1, format, time); 294 strftime(buf, sizeof(buf)-1, format, time);
295 html_txt(buf); 295 html_txt(buf);
296} 296}
297 297
298void cgit_print_age(time_t t, time_t max_relative, char *format) 298void cgit_print_age(time_t t, time_t max_relative, char *format)
299{ 299{
300 time_t now, secs; 300 time_t now, secs;
301 301
302 time(&now); 302 time(&now);
303 secs = now - t; 303 secs = now - t;
304 304
305 if (secs > max_relative && max_relative >= 0) { 305 if (secs > max_relative && max_relative >= 0) {
306 cgit_print_date(t, format); 306 cgit_print_date(t, format);
307 return; 307 return;
308 } 308 }
309 309
310 if (secs < TM_HOUR * 2) { 310 if (secs < TM_HOUR * 2) {
311 htmlf("<span class='age-mins'>%.0f min.</span>", 311 htmlf("<span class='age-mins'>%.0f min.</span>",
312 secs * 1.0 / TM_MIN); 312 secs * 1.0 / TM_MIN);
313 return; 313 return;
314 } 314 }
315 if (secs < TM_DAY * 2) { 315 if (secs < TM_DAY * 2) {
316 htmlf("<span class='age-hours'>%.0f hours</span>", 316 htmlf("<span class='age-hours'>%.0f hours</span>",
317 secs * 1.0 / TM_HOUR); 317 secs * 1.0 / TM_HOUR);
318 return; 318 return;
319 } 319 }
320 if (secs < TM_WEEK * 2) { 320 if (secs < TM_WEEK * 2) {
321 htmlf("<span class='age-days'>%.0f days</span>", 321 htmlf("<span class='age-days'>%.0f days</span>",
322 secs * 1.0 / TM_DAY); 322 secs * 1.0 / TM_DAY);
323 return; 323 return;
324 } 324 }
325 if (secs < TM_MONTH * 2) { 325 if (secs < TM_MONTH * 2) {
326 htmlf("<span class='age-weeks'>%.0f weeks</span>", 326 htmlf("<span class='age-weeks'>%.0f weeks</span>",
327 secs * 1.0 / TM_WEEK); 327 secs * 1.0 / TM_WEEK);
328 return; 328 return;
329 } 329 }
330 if (secs < TM_YEAR * 2) { 330 if (secs < TM_YEAR * 2) {
331 htmlf("<span class='age-months'>%.0f months</span>", 331 htmlf("<span class='age-months'>%.0f months</span>",
332 secs * 1.0 / TM_MONTH); 332 secs * 1.0 / TM_MONTH);
333 return; 333 return;
334 } 334 }
335 htmlf("<span class='age-years'>%.0f years</span>", 335 htmlf("<span class='age-years'>%.0f years</span>",
336 secs * 1.0 / TM_YEAR); 336 secs * 1.0 / TM_YEAR);
337} 337}
338 338
339void cgit_print_docstart(char *title, struct cacheitem *item) 339void cgit_print_docstart(char *title, struct cacheitem *item)
340{ 340{
341 html("Content-Type: text/html; charset=utf-8\n"); 341 html("Content-Type: text/html; charset=utf-8\n");
342 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 342 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
343 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 343 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
344 ttl_seconds(item->ttl))); 344 ttl_seconds(item->ttl)));
345 html("\n"); 345 html("\n");
346 html(cgit_doctype); 346 html(cgit_doctype);
347 html("<html>\n"); 347 html("<html>\n");
348 html("<head>\n"); 348 html("<head>\n");
349 html("<title>"); 349 html("<title>");
350 html_txt(title); 350 html_txt(title);
351 html("</title>\n"); 351 html("</title>\n");
352 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); 352 htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
353 html("<link rel='stylesheet' type='text/css' href='"); 353 html("<link rel='stylesheet' type='text/css' href='");
354 html_attr(cgit_css); 354 html_attr(cgit_css);
355 html("'/>\n"); 355 html("'/>\n");
356 html("</head>\n"); 356 html("</head>\n");
357 html("<body>\n"); 357 html("<body>\n");
358} 358}
359 359
360void cgit_print_docend() 360void cgit_print_docend()
361{ 361{
362 html("</td></tr></table>"); 362 html("</td></tr></table>");
363 html("</body>\n</html>\n"); 363 html("</body>\n</html>\n");
364} 364}
365 365
366void cgit_print_pageheader(char *title, int show_search) 366void cgit_print_pageheader(char *title, int show_search)
367{ 367{
368 html("<table id='layout'>"); 368 html("<table id='layout'>");
369 html("<tr><td id='header'><a href='"); 369 html("<tr><td id='header'><a href='");
370 html_attr(cgit_rooturl()); 370 html_attr(cgit_rooturl());
371 html("'>"); 371 html("'>");
372 html_txt(cgit_root_title); 372 html_txt(cgit_root_title);
373 html("</a></td><td id='logo'>"); 373 html("</a></td><td id='logo'>");
374 html("<a href='"); 374 html("<a href='");
375 html_attr(cgit_logo_link); 375 html_attr(cgit_logo_link);
376 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo); 376 htmlf("'><img src='%s' alt='logo'/></a>", cgit_logo);
377 html("</td></tr>"); 377 html("</td></tr>");
378 html("<tr><td id='crumb'>"); 378 html("<tr><td id='crumb'>");
379 if (cgit_query_repo) { 379 if (cgit_query_repo) {
380 html_txt(cgit_repo->name); 380 html_txt(cgit_repo->name);
381 html(" ("); 381 html(" (");
382 html_txt(cgit_query_head); 382 html_txt(cgit_query_head);
383 html(") : &nbsp;"); 383 html(") : &nbsp;");
384 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head, 384 reporevlink(NULL, "summary", NULL, NULL, cgit_query_head,
385 NULL, NULL); 385 NULL, NULL);
386 html(" "); 386 html(" ");
387 cgit_log_link("log", NULL, NULL, cgit_query_head, 387 cgit_log_link("log", NULL, NULL, cgit_query_head,
388 cgit_query_sha1, cgit_query_path, 0); 388 cgit_query_sha1, cgit_query_path, 0);
389 html(" "); 389 html(" ");
390 cgit_tree_link("tree", NULL, NULL, cgit_query_head, 390 cgit_tree_link("tree", NULL, NULL, cgit_query_head,
391 cgit_query_sha1, NULL); 391 cgit_query_sha1, NULL);
392 html(" "); 392 html(" ");
393 cgit_commit_link("commit", NULL, NULL, cgit_query_head, 393 cgit_commit_link("commit", NULL, NULL, cgit_query_head,
394 cgit_query_sha1); 394 cgit_query_sha1);
395 html(" "); 395 html(" ");
396 cgit_diff_link("diff", NULL, NULL, cgit_query_head, 396 cgit_diff_link("diff", NULL, NULL, cgit_query_head,
397 cgit_query_sha1, cgit_query_sha2, 397 cgit_query_sha1, cgit_query_sha2,
398 cgit_query_path); 398 cgit_query_path);
399 } else { 399 } else {
400 html_txt("Index of repositories"); 400 html_txt("Index of repositories");
401 } 401 }
402 html("</td>"); 402 html("</td>");
403 html("<td id='search'>"); 403 html("<td id='search'>");
404 if (show_search) { 404 if (show_search) {
405 html("<form method='get' action='"); 405 html("<form method='get' action='");
406 html_attr(cgit_currurl()); 406 html_attr(cgit_currurl());
407 html("'>"); 407 html("'>");
408 if (!cgit_virtual_root) { 408 if (!cgit_virtual_root) {
409 if (cgit_query_repo) 409 if (cgit_query_repo)
410 html_hidden("r", cgit_query_repo); 410 html_hidden("r", cgit_query_repo);
411 if (cgit_query_page) 411 if (cgit_query_page)
412 html_hidden("p", cgit_query_page); 412 html_hidden("p", cgit_query_page);
413 } 413 }
414 if (cgit_query_head) 414 if (cgit_query_head)
415 html_hidden("h", cgit_query_head); 415 html_hidden("h", cgit_query_head);
416 if (cgit_query_sha1) 416 if (cgit_query_sha1)
417 html_hidden("id", cgit_query_sha1); 417 html_hidden("id", cgit_query_sha1);
418 if (cgit_query_sha2) 418 if (cgit_query_sha2)
419 html_hidden("id2", cgit_query_sha2); 419 html_hidden("id2", cgit_query_sha2);
420 html("<input type='text' name='q' value='"); 420 html("<select name='qt'>");
421 html_option("grep", "log msg", cgit_query_grep);
422 html_option("author", "author", cgit_query_grep);
423 html_option("committer", "committer", cgit_query_grep);
424 html("</select>");
425 html("<input class='txt' type='text' name='q' value='");
421 html_attr(cgit_query_search); 426 html_attr(cgit_query_search);
422 html("'/></form>"); 427 html("'/><input class='btn' type='submit' value='...'/></form>");
423 } 428 }
424 html("</td></tr>"); 429 html("</td></tr>");
425 html("<tr><td id='content' colspan='2'>"); 430 html("<tr><td id='content' colspan='2'>");
426} 431}
427 432
428void cgit_print_snapshot_start(const char *mimetype, const char *filename, 433void cgit_print_snapshot_start(const char *mimetype, const char *filename,
429 struct cacheitem *item) 434 struct cacheitem *item)
430{ 435{
431 htmlf("Content-Type: %s\n", mimetype); 436 htmlf("Content-Type: %s\n", mimetype);
432 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename); 437 htmlf("Content-Disposition: inline; filename=\"%s\"\n", filename);
433 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime)); 438 htmlf("Last-Modified: %s\n", http_date(item->st.st_mtime));
434 htmlf("Expires: %s\n", http_date(item->st.st_mtime + 439 htmlf("Expires: %s\n", http_date(item->st.st_mtime +
435 ttl_seconds(item->ttl))); 440 ttl_seconds(item->ttl)));
436 html("\n"); 441 html("\n");
437} 442}
438 443
439/* vim:set sw=8: */ 444/* vim:set sw=8: */
diff --git a/ui-summary.c b/ui-summary.c
index 178e959..04a466a 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -1,247 +1,247 @@
1/* ui-summary.c: functions for generating repo summary page 1/* ui-summary.c: functions for generating repo summary page
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 10
11static int header; 11static int header;
12 12
13static int cmp_age(int age1, int age2) 13static int cmp_age(int age1, int age2)
14{ 14{
15 if (age1 != 0 && age2 != 0) 15 if (age1 != 0 && age2 != 0)
16 return age2 - age1; 16 return age2 - age1;
17 17
18 if (age1 == 0 && age2 == 0) 18 if (age1 == 0 && age2 == 0)
19 return 0; 19 return 0;
20 20
21 if (age1 == 0) 21 if (age1 == 0)
22 return +1; 22 return +1;
23 23
24 return -1; 24 return -1;
25} 25}
26 26
27static int cmp_ref_name(const void *a, const void *b) 27static int cmp_ref_name(const void *a, const void *b)
28{ 28{
29 struct refinfo *r1 = *(struct refinfo **)a; 29 struct refinfo *r1 = *(struct refinfo **)a;
30 struct refinfo *r2 = *(struct refinfo **)b; 30 struct refinfo *r2 = *(struct refinfo **)b;
31 31
32 return strcmp(r1->refname, r2->refname); 32 return strcmp(r1->refname, r2->refname);
33} 33}
34 34
35static int cmp_branch_age(const void *a, const void *b) 35static int cmp_branch_age(const void *a, const void *b)
36{ 36{
37 struct refinfo *r1 = *(struct refinfo **)a; 37 struct refinfo *r1 = *(struct refinfo **)a;
38 struct refinfo *r2 = *(struct refinfo **)b; 38 struct refinfo *r2 = *(struct refinfo **)b;
39 39
40 return cmp_age(r1->commit->committer_date, r2->commit->committer_date); 40 return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
41} 41}
42 42
43static int cmp_tag_age(const void *a, const void *b) 43static int cmp_tag_age(const void *a, const void *b)
44{ 44{
45 struct refinfo *r1 = *(struct refinfo **)a; 45 struct refinfo *r1 = *(struct refinfo **)a;
46 struct refinfo *r2 = *(struct refinfo **)b; 46 struct refinfo *r2 = *(struct refinfo **)b;
47 47
48 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date); 48 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
49} 49}
50 50
51static int print_branch(struct refinfo *ref) 51static int print_branch(struct refinfo *ref)
52{ 52{
53 struct commitinfo *info = ref->commit; 53 struct commitinfo *info = ref->commit;
54 char *name = (char *)ref->refname; 54 char *name = (char *)ref->refname;
55 55
56 if (!info) 56 if (!info)
57 return 1; 57 return 1;
58 html("<tr><td>"); 58 html("<tr><td>");
59 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0); 59 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0);
60 html("</td><td>"); 60 html("</td><td>");
61 cgit_print_age(info->commit->date, -1, NULL); 61 cgit_print_age(info->commit->date, -1, NULL);
62 html("</td><td>"); 62 html("</td><td>");
63 html_txt(info->author); 63 html_txt(info->author);
64 html("</td><td>"); 64 html("</td><td>");
65 cgit_commit_link(info->subject, NULL, NULL, name, NULL); 65 cgit_commit_link(info->subject, NULL, NULL, name, NULL);
66 html("</td></tr>\n"); 66 html("</td></tr>\n");
67 return 0; 67 return 0;
68} 68}
69 69
70static void print_tag_header() 70static void print_tag_header()
71{ 71{
72 html("<tr class='nohover'><th class='left'>Tag</th>" 72 html("<tr class='nohover'><th class='left'>Tag</th>"
73 "<th class='left'>Age</th>" 73 "<th class='left'>Age</th>"
74 "<th class='left'>Author</th>" 74 "<th class='left'>Author</th>"
75 "<th class='left'>Reference</th></tr>\n"); 75 "<th class='left'>Reference</th></tr>\n");
76 header = 1; 76 header = 1;
77} 77}
78 78
79static int print_tag(struct refinfo *ref) 79static int print_tag(struct refinfo *ref)
80{ 80{
81 struct tag *tag; 81 struct tag *tag;
82 struct taginfo *info; 82 struct taginfo *info;
83 char *url, *name = (char *)ref->refname; 83 char *url, *name = (char *)ref->refname;
84 84
85 if (ref->object->type == OBJ_TAG) { 85 if (ref->object->type == OBJ_TAG) {
86 tag = (struct tag *)ref->object; 86 tag = (struct tag *)ref->object;
87 info = ref->tag; 87 info = ref->tag;
88 if (!tag || !info) 88 if (!tag || !info)
89 return 1; 89 return 1;
90 html("<tr><td>"); 90 html("<tr><td>");
91 url = cgit_pageurl(cgit_query_repo, "tag", 91 url = cgit_pageurl(cgit_query_repo, "tag",
92 fmt("id=%s", name)); 92 fmt("id=%s", name));
93 html_link_open(url, NULL, NULL); 93 html_link_open(url, NULL, NULL);
94 html_txt(name); 94 html_txt(name);
95 html_link_close(); 95 html_link_close();
96 html("</td><td>"); 96 html("</td><td>");
97 if (info->tagger_date > 0) 97 if (info->tagger_date > 0)
98 cgit_print_age(info->tagger_date, -1, NULL); 98 cgit_print_age(info->tagger_date, -1, NULL);
99 html("</td><td>"); 99 html("</td><td>");
100 if (info->tagger) 100 if (info->tagger)
101 html(info->tagger); 101 html(info->tagger);
102 html("</td><td>"); 102 html("</td><td>");
103 cgit_object_link(tag->tagged); 103 cgit_object_link(tag->tagged);
104 html("</td></tr>\n"); 104 html("</td></tr>\n");
105 } else { 105 } else {
106 if (!header) 106 if (!header)
107 print_tag_header(); 107 print_tag_header();
108 html("<tr><td>"); 108 html("<tr><td>");
109 html_txt(name); 109 html_txt(name);
110 html("</td><td colspan='2'/><td>"); 110 html("</td><td colspan='2'/><td>");
111 cgit_object_link(ref->object); 111 cgit_object_link(ref->object);
112 html("</td></tr>\n"); 112 html("</td></tr>\n");
113 } 113 }
114 return 0; 114 return 0;
115} 115}
116 116
117static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1, 117static int cgit_print_archive_cb(const char *refname, const unsigned char *sha1,
118 int flags, void *cb_data) 118 int flags, void *cb_data)
119{ 119{
120 struct tag *tag; 120 struct tag *tag;
121 struct taginfo *info; 121 struct taginfo *info;
122 struct object *obj; 122 struct object *obj;
123 char buf[256], *url; 123 char buf[256], *url;
124 unsigned char fileid[20]; 124 unsigned char fileid[20];
125 125
126 if (prefixcmp(refname, "refs/archives")) 126 if (prefixcmp(refname, "refs/archives"))
127 return 0; 127 return 0;
128 strncpy(buf, refname+14, sizeof(buf)); 128 strncpy(buf, refname+14, sizeof(buf));
129 obj = parse_object(sha1); 129 obj = parse_object(sha1);
130 if (!obj) 130 if (!obj)
131 return 1; 131 return 1;
132 if (obj->type == OBJ_TAG) { 132 if (obj->type == OBJ_TAG) {
133 tag = lookup_tag(sha1); 133 tag = lookup_tag(sha1);
134 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) 134 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag)))
135 return 0; 135 return 0;
136 hashcpy(fileid, tag->tagged->sha1); 136 hashcpy(fileid, tag->tagged->sha1);
137 } else if (obj->type != OBJ_BLOB) { 137 } else if (obj->type != OBJ_BLOB) {
138 return 0; 138 return 0;
139 } else { 139 } else {
140 hashcpy(fileid, sha1); 140 hashcpy(fileid, sha1);
141 } 141 }
142 if (!header) { 142 if (!header) {
143 html("<table id='downloads'>"); 143 html("<table id='downloads'>");
144 html("<tr><th>Downloads</th></tr>"); 144 html("<tr><th>Downloads</th></tr>");
145 header = 1; 145 header = 1;
146 } 146 }
147 html("<tr><td>"); 147 html("<tr><td>");
148 url = cgit_pageurl(cgit_query_repo, "blob", 148 url = cgit_pageurl(cgit_query_repo, "blob",
149 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid), 149 fmt("id=%s&amp;path=%s", sha1_to_hex(fileid),
150 buf)); 150 buf));
151 html_link_open(url, NULL, NULL); 151 html_link_open(url, NULL, NULL);
152 html_txt(buf); 152 html_txt(buf);
153 html_link_close(); 153 html_link_close();
154 html("</td></tr>"); 154 html("</td></tr>");
155 return 0; 155 return 0;
156} 156}
157 157
158static void print_refs_link(char *path) 158static void print_refs_link(char *path)
159{ 159{
160 html("<tr class='nohover'><td colspan='4'>"); 160 html("<tr class='nohover'><td colspan='4'>");
161 cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path); 161 cgit_refs_link("[...]", NULL, NULL, cgit_query_head, NULL, path);
162 html("</td></tr>"); 162 html("</td></tr>");
163} 163}
164 164
165void cgit_print_branches(int maxcount) 165void cgit_print_branches(int maxcount)
166{ 166{
167 struct reflist list; 167 struct reflist list;
168 int i; 168 int i;
169 169
170 html("<tr class='nohover'><th class='left'>Branch</th>" 170 html("<tr class='nohover'><th class='left'>Branch</th>"
171 "<th class='left'>Idle</th>" 171 "<th class='left'>Idle</th>"
172 "<th class='left'>Author</th>" 172 "<th class='left'>Author</th>"
173 "<th class='left'>Head commit</th></tr>\n"); 173 "<th class='left'>Head commit</th></tr>\n");
174 174
175 list.refs = NULL; 175 list.refs = NULL;
176 list.alloc = list.count = 0; 176 list.alloc = list.count = 0;
177 for_each_branch_ref(cgit_refs_cb, &list); 177 for_each_branch_ref(cgit_refs_cb, &list);
178 178
179 if (maxcount == 0 || maxcount > list.count) 179 if (maxcount == 0 || maxcount > list.count)
180 maxcount = list.count; 180 maxcount = list.count;
181 181
182 if (maxcount < list.count) { 182 if (maxcount < list.count) {
183 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age); 183 qsort(list.refs, list.count, sizeof(*list.refs), cmp_branch_age);
184 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name); 184 qsort(list.refs, maxcount, sizeof(*list.refs), cmp_ref_name);
185 } 185 }
186 186
187 for(i=0; i<maxcount; i++) 187 for(i=0; i<maxcount; i++)
188 print_branch(list.refs[i]); 188 print_branch(list.refs[i]);
189 189
190 if (maxcount < list.count) 190 if (maxcount < list.count)
191 print_refs_link("heads"); 191 print_refs_link("heads");
192} 192}
193 193
194void cgit_print_tags(int maxcount) 194void cgit_print_tags(int maxcount)
195{ 195{
196 struct reflist list; 196 struct reflist list;
197 int i; 197 int i;
198 198
199 header = 0; 199 header = 0;
200 list.refs = NULL; 200 list.refs = NULL;
201 list.alloc = list.count = 0; 201 list.alloc = list.count = 0;
202 for_each_tag_ref(cgit_refs_cb, &list); 202 for_each_tag_ref(cgit_refs_cb, &list);
203 if (list.count == 0) 203 if (list.count == 0)
204 return; 204 return;
205 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age); 205 qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
206 if (!maxcount) 206 if (!maxcount)
207 maxcount = list.count; 207 maxcount = list.count;
208 else if (maxcount > list.count) 208 else if (maxcount > list.count)
209 maxcount = list.count; 209 maxcount = list.count;
210 print_tag_header(); 210 print_tag_header();
211 for(i=0; i<maxcount; i++) 211 for(i=0; i<maxcount; i++)
212 print_tag(list.refs[i]); 212 print_tag(list.refs[i]);
213 213
214 if (maxcount < list.count) 214 if (maxcount < list.count)
215 print_refs_link("tags"); 215 print_refs_link("tags");
216} 216}
217 217
218static void cgit_print_archives() 218static void cgit_print_archives()
219{ 219{
220 header = 0; 220 header = 0;
221 for_each_ref(cgit_print_archive_cb, NULL); 221 for_each_ref(cgit_print_archive_cb, NULL);
222 if (header) 222 if (header)
223 html("</table>"); 223 html("</table>");
224} 224}
225 225
226void cgit_print_summary() 226void cgit_print_summary()
227{ 227{
228 html("<div id='summary'>"); 228 html("<div id='summary'>");
229 cgit_print_archives(); 229 cgit_print_archives();
230 html("<h2>"); 230 html("<h2>");
231 html_txt(cgit_repo->name); 231 html_txt(cgit_repo->name);
232 html(" - "); 232 html(" - ");
233 html_txt(cgit_repo->desc); 233 html_txt(cgit_repo->desc);
234 html("</h2>"); 234 html("</h2>");
235 if (cgit_repo->readme) 235 if (cgit_repo->readme)
236 html_include(cgit_repo->readme); 236 html_include(cgit_repo->readme);
237 html("</div>"); 237 html("</div>");
238 if (cgit_summary_log > 0) 238 if (cgit_summary_log > 0)
239 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, 0); 239 cgit_print_log(cgit_query_head, 0, cgit_summary_log, NULL, NULL, NULL, 0);
240 html("<table class='list nowrap'>"); 240 html("<table class='list nowrap'>");
241 if (cgit_summary_log > 0) 241 if (cgit_summary_log > 0)
242 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 242 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
243 cgit_print_branches(cgit_summary_branches); 243 cgit_print_branches(cgit_summary_branches);
244 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 244 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
245 cgit_print_tags(cgit_summary_tags); 245 cgit_print_tags(cgit_summary_tags);
246 html("</table>"); 246 html("</table>");
247} 247}