summaryrefslogtreecommitdiffabout
path: root/ui-repolist.c
Unidiff
Diffstat (limited to 'ui-repolist.c') (more/less context) (ignore whitespace changes)
-rw-r--r--ui-repolist.c16
1 files changed, 12 insertions, 4 deletions
diff --git a/ui-repolist.c b/ui-repolist.c
index cf27cb3..aa743bf 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -1,266 +1,274 @@
1/* ui-repolist.c: functions for generating the repolist page 1/* ui-repolist.c: functions for generating the repolist 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 <time.h> 9#include <time.h>
10 10
11#include "cgit.h" 11#include "cgit.h"
12#include "html.h" 12#include "html.h"
13#include "ui-shared.h" 13#include "ui-shared.h"
14 14
15time_t read_agefile(char *path) 15time_t read_agefile(char *path)
16{ 16{
17 FILE *f; 17 FILE *f;
18 static char buf[64], buf2[64]; 18 static char buf[64], buf2[64];
19 19
20 if (!(f = fopen(path, "r"))) 20 if (!(f = fopen(path, "r")))
21 return -1; 21 return -1;
22 if (fgets(buf, sizeof(buf), f) == NULL) 22 if (fgets(buf, sizeof(buf), f) == NULL)
23 return -1; 23 return -1;
24 fclose(f); 24 fclose(f);
25 if (parse_date(buf, buf2, sizeof(buf2))) 25 if (parse_date(buf, buf2, sizeof(buf2)))
26 return strtoul(buf2, NULL, 10); 26 return strtoul(buf2, NULL, 10);
27 else 27 else
28 return 0; 28 return 0;
29} 29}
30 30
31static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) 31static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
32{ 32{
33 char *path; 33 char *path;
34 struct stat s; 34 struct stat s;
35 struct cgit_repo *r = (struct cgit_repo *)repo;
35 36
37 if (repo->mtime != -1) {
38 *mtime = repo->mtime;
39 return 1;
40 }
36 path = fmt("%s/%s", repo->path, ctx.cfg.agefile); 41 path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
37 if (stat(path, &s) == 0) { 42 if (stat(path, &s) == 0) {
38 *mtime = read_agefile(path); 43 *mtime = read_agefile(path);
44 r->mtime = *mtime;
39 return 1; 45 return 1;
40 } 46 }
41 47
42 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch); 48 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
43 if (stat(path, &s) == 0) { 49 if (stat(path, &s) == 0)
44 *mtime = s.st_mtime; 50 *mtime = s.st_mtime;
45 return 1; 51 else
46 } 52 *mtime = 0;
47 return 0; 53
54 r->mtime = *mtime;
55 return (r->mtime != 0);
48} 56}
49 57
50static void print_modtime(struct cgit_repo *repo) 58static void print_modtime(struct cgit_repo *repo)
51{ 59{
52 time_t t; 60 time_t t;
53 if (get_repo_modtime(repo, &t)) 61 if (get_repo_modtime(repo, &t))
54 cgit_print_age(t, -1, NULL); 62 cgit_print_age(t, -1, NULL);
55} 63}
56 64
57int is_match(struct cgit_repo *repo) 65int is_match(struct cgit_repo *repo)
58{ 66{
59 if (!ctx.qry.search) 67 if (!ctx.qry.search)
60 return 1; 68 return 1;
61 if (repo->url && strcasestr(repo->url, ctx.qry.search)) 69 if (repo->url && strcasestr(repo->url, ctx.qry.search))
62 return 1; 70 return 1;
63 if (repo->name && strcasestr(repo->name, ctx.qry.search)) 71 if (repo->name && strcasestr(repo->name, ctx.qry.search))
64 return 1; 72 return 1;
65 if (repo->desc && strcasestr(repo->desc, ctx.qry.search)) 73 if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
66 return 1; 74 return 1;
67 if (repo->owner && strcasestr(repo->owner, ctx.qry.search)) 75 if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
68 return 1; 76 return 1;
69 return 0; 77 return 0;
70} 78}
71 79
72int is_in_url(struct cgit_repo *repo) 80int is_in_url(struct cgit_repo *repo)
73{ 81{
74 if (!ctx.qry.url) 82 if (!ctx.qry.url)
75 return 1; 83 return 1;
76 if (repo->url && !prefixcmp(repo->url, ctx.qry.url)) 84 if (repo->url && !prefixcmp(repo->url, ctx.qry.url))
77 return 1; 85 return 1;
78 return 0; 86 return 0;
79} 87}
80 88
81void print_sort_header(const char *title, const char *sort) 89void print_sort_header(const char *title, const char *sort)
82{ 90{
83 htmlf("<th class='left'><a href='./?s=%s", sort); 91 htmlf("<th class='left'><a href='./?s=%s", sort);
84 if (ctx.qry.search) { 92 if (ctx.qry.search) {
85 html("&q="); 93 html("&q=");
86 html_url_arg(ctx.qry.search); 94 html_url_arg(ctx.qry.search);
87 } 95 }
88 htmlf("'>%s</a></th>", title); 96 htmlf("'>%s</a></th>", title);
89} 97}
90 98
91void print_header(int columns) 99void print_header(int columns)
92{ 100{
93 html("<tr class='nohover'>"); 101 html("<tr class='nohover'>");
94 print_sort_header("Name", "name"); 102 print_sort_header("Name", "name");
95 print_sort_header("Description", "desc"); 103 print_sort_header("Description", "desc");
96 print_sort_header("Owner", "owner"); 104 print_sort_header("Owner", "owner");
97 print_sort_header("Idle", "idle"); 105 print_sort_header("Idle", "idle");
98 if (ctx.cfg.enable_index_links) 106 if (ctx.cfg.enable_index_links)
99 html("<th class='left'>Links</th>"); 107 html("<th class='left'>Links</th>");
100 html("</tr>\n"); 108 html("</tr>\n");
101} 109}
102 110
103 111
104void print_pager(int items, int pagelen, char *search) 112void print_pager(int items, int pagelen, char *search)
105{ 113{
106 int i; 114 int i;
107 html("<div class='pager'>"); 115 html("<div class='pager'>");
108 for(i = 0; i * pagelen < items; i++) 116 for(i = 0; i * pagelen < items; i++)
109 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL, 117 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL,
110 search, i * pagelen); 118 search, i * pagelen);
111 html("</div>"); 119 html("</div>");
112} 120}
113 121
114static int cmp(const char *s1, const char *s2) 122static int cmp(const char *s1, const char *s2)
115{ 123{
116 if (s1 && s2) 124 if (s1 && s2)
117 return strcmp(s1, s2); 125 return strcmp(s1, s2);
118 if (s1 && !s2) 126 if (s1 && !s2)
119 return -1; 127 return -1;
120 if (s2 && !s1) 128 if (s2 && !s1)
121 return 1; 129 return 1;
122 return 0; 130 return 0;
123} 131}
124 132
125static int sort_name(const void *a, const void *b) 133static int sort_name(const void *a, const void *b)
126{ 134{
127 const struct cgit_repo *r1 = a; 135 const struct cgit_repo *r1 = a;
128 const struct cgit_repo *r2 = b; 136 const struct cgit_repo *r2 = b;
129 137
130 return cmp(r1->name, r2->name); 138 return cmp(r1->name, r2->name);
131} 139}
132 140
133static int sort_desc(const void *a, const void *b) 141static int sort_desc(const void *a, const void *b)
134{ 142{
135 const struct cgit_repo *r1 = a; 143 const struct cgit_repo *r1 = a;
136 const struct cgit_repo *r2 = b; 144 const struct cgit_repo *r2 = b;
137 145
138 return cmp(r1->desc, r2->desc); 146 return cmp(r1->desc, r2->desc);
139} 147}
140 148
141static int sort_owner(const void *a, const void *b) 149static int sort_owner(const void *a, const void *b)
142{ 150{
143 const struct cgit_repo *r1 = a; 151 const struct cgit_repo *r1 = a;
144 const struct cgit_repo *r2 = b; 152 const struct cgit_repo *r2 = b;
145 153
146 return cmp(r1->owner, r2->owner); 154 return cmp(r1->owner, r2->owner);
147} 155}
148 156
149static int sort_idle(const void *a, const void *b) 157static int sort_idle(const void *a, const void *b)
150{ 158{
151 const struct cgit_repo *r1 = a; 159 const struct cgit_repo *r1 = a;
152 const struct cgit_repo *r2 = b; 160 const struct cgit_repo *r2 = b;
153 time_t t1, t2; 161 time_t t1, t2;
154 162
155 t1 = t2 = 0; 163 t1 = t2 = 0;
156 get_repo_modtime(r1, &t1); 164 get_repo_modtime(r1, &t1);
157 get_repo_modtime(r2, &t2); 165 get_repo_modtime(r2, &t2);
158 return t2 - t1; 166 return t2 - t1;
159} 167}
160 168
161struct sortcolumn { 169struct sortcolumn {
162 const char *name; 170 const char *name;
163 int (*fn)(const void *a, const void *b); 171 int (*fn)(const void *a, const void *b);
164}; 172};
165 173
166struct sortcolumn sortcolumn[] = { 174struct sortcolumn sortcolumn[] = {
167 {"name", sort_name}, 175 {"name", sort_name},
168 {"desc", sort_desc}, 176 {"desc", sort_desc},
169 {"owner", sort_owner}, 177 {"owner", sort_owner},
170 {"idle", sort_idle}, 178 {"idle", sort_idle},
171 {NULL, NULL} 179 {NULL, NULL}
172}; 180};
173 181
174int sort_repolist(char *field) 182int sort_repolist(char *field)
175{ 183{
176 struct sortcolumn *column; 184 struct sortcolumn *column;
177 185
178 for (column = &sortcolumn[0]; column->name; column++) { 186 for (column = &sortcolumn[0]; column->name; column++) {
179 if (strcmp(field, column->name)) 187 if (strcmp(field, column->name))
180 continue; 188 continue;
181 qsort(cgit_repolist.repos, cgit_repolist.count, 189 qsort(cgit_repolist.repos, cgit_repolist.count,
182 sizeof(struct cgit_repo), column->fn); 190 sizeof(struct cgit_repo), column->fn);
183 return 1; 191 return 1;
184 } 192 }
185 return 0; 193 return 0;
186} 194}
187 195
188 196
189void cgit_print_repolist() 197void cgit_print_repolist()
190{ 198{
191 int i, columns = 4, hits = 0, header = 0; 199 int i, columns = 4, hits = 0, header = 0;
192 char *last_group = NULL; 200 char *last_group = NULL;
193 int sorted = 0; 201 int sorted = 0;
194 202
195 if (ctx.cfg.enable_index_links) 203 if (ctx.cfg.enable_index_links)
196 columns++; 204 columns++;
197 205
198 ctx.page.title = ctx.cfg.root_title; 206 ctx.page.title = ctx.cfg.root_title;
199 cgit_print_http_headers(&ctx); 207 cgit_print_http_headers(&ctx);
200 cgit_print_docstart(&ctx); 208 cgit_print_docstart(&ctx);
201 cgit_print_pageheader(&ctx); 209 cgit_print_pageheader(&ctx);
202 210
203 if (ctx.cfg.index_header) 211 if (ctx.cfg.index_header)
204 html_include(ctx.cfg.index_header); 212 html_include(ctx.cfg.index_header);
205 213
206 if(ctx.qry.sort) 214 if(ctx.qry.sort)
207 sorted = sort_repolist(ctx.qry.sort); 215 sorted = sort_repolist(ctx.qry.sort);
208 216
209 html("<table summary='repository list' class='list nowrap'>"); 217 html("<table summary='repository list' class='list nowrap'>");
210 for (i=0; i<cgit_repolist.count; i++) { 218 for (i=0; i<cgit_repolist.count; i++) {
211 ctx.repo = &cgit_repolist.repos[i]; 219 ctx.repo = &cgit_repolist.repos[i];
212 if (!(is_match(ctx.repo) && is_in_url(ctx.repo))) 220 if (!(is_match(ctx.repo) && is_in_url(ctx.repo)))
213 continue; 221 continue;
214 hits++; 222 hits++;
215 if (hits <= ctx.qry.ofs) 223 if (hits <= ctx.qry.ofs)
216 continue; 224 continue;
217 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count) 225 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
218 continue; 226 continue;
219 if (!header++) 227 if (!header++)
220 print_header(columns); 228 print_header(columns);
221 if (!sorted && 229 if (!sorted &&
222 ((last_group == NULL && ctx.repo->group != NULL) || 230 ((last_group == NULL && ctx.repo->group != NULL) ||
223 (last_group != NULL && ctx.repo->group == NULL) || 231 (last_group != NULL && ctx.repo->group == NULL) ||
224 (last_group != NULL && ctx.repo->group != NULL && 232 (last_group != NULL && ctx.repo->group != NULL &&
225 strcmp(ctx.repo->group, last_group)))) { 233 strcmp(ctx.repo->group, last_group)))) {
226 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", 234 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
227 columns); 235 columns);
228 html_txt(ctx.repo->group); 236 html_txt(ctx.repo->group);
229 html("</td></tr>"); 237 html("</td></tr>");
230 last_group = ctx.repo->group; 238 last_group = ctx.repo->group;
231 } 239 }
232 htmlf("<tr><td class='%s'>", 240 htmlf("<tr><td class='%s'>",
233 !sorted && ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); 241 !sorted && ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
234 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); 242 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
235 html("</td><td>"); 243 html("</td><td>");
236 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); 244 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
237 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); 245 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
238 html_link_close(); 246 html_link_close();
239 html("</td><td>"); 247 html("</td><td>");
240 html_txt(ctx.repo->owner); 248 html_txt(ctx.repo->owner);
241 html("</td><td>"); 249 html("</td><td>");
242 print_modtime(ctx.repo); 250 print_modtime(ctx.repo);
243 html("</td>"); 251 html("</td>");
244 if (ctx.cfg.enable_index_links) { 252 if (ctx.cfg.enable_index_links) {
245 html("<td>"); 253 html("<td>");
246 cgit_summary_link("summary", NULL, "button", NULL); 254 cgit_summary_link("summary", NULL, "button", NULL);
247 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 255 cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
248 0, NULL, NULL); 256 0, NULL, NULL);
249 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); 257 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
250 html("</td>"); 258 html("</td>");
251 } 259 }
252 html("</tr>\n"); 260 html("</tr>\n");
253 } 261 }
254 html("</table>"); 262 html("</table>");
255 if (!hits) 263 if (!hits)
256 cgit_print_error("No repositories found"); 264 cgit_print_error("No repositories found");
257 else if (hits > ctx.cfg.max_repo_count) 265 else if (hits > ctx.cfg.max_repo_count)
258 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search); 266 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search);
259 cgit_print_docend(); 267 cgit_print_docend();
260} 268}
261 269
262void cgit_print_site_readme() 270void cgit_print_site_readme()
263{ 271{
264 if (ctx.cfg.root_readme) 272 if (ctx.cfg.root_readme)
265 html_include(ctx.cfg.root_readme); 273 html_include(ctx.cfg.root_readme);
266} 274}