summaryrefslogtreecommitdiffabout
path: root/ui-repolist.c
authorLars Hjemli <hjemli@gmail.com>2008-11-29 15:46:37 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-11-29 15:46:37 (UTC)
commit8813170390f3c3a0f4743afbc92ede42953fa3b0 (patch) (unidiff)
tree39305350baee1eb564aae00294634bbe544983d3 /ui-repolist.c
parent54272e60965ec6a98b49cbf67d72a4b1f5adc55b (diff)
downloadcgit-8813170390f3c3a0f4743afbc92ede42953fa3b0.zip
cgit-8813170390f3c3a0f4743afbc92ede42953fa3b0.tar.gz
cgit-8813170390f3c3a0f4743afbc92ede42953fa3b0.tar.bz2
ui-repolist: implement lazy caching of repo->mtime
When sorting the list of repositories by their last modification time, cgit would (in the worst case) invoke fstat(3) four times and open(3) twice for each callback from qsort(3). This obviously scales very badly. Now, the calculated modtime for each repo is saved in repo->mtime, thus keeping the number of stat/open invocations identical for sorted and unsorted repo-listings. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
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}