summaryrefslogtreecommitdiffabout
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
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 (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.h1
-rw-r--r--shared.c1
-rw-r--r--ui-repolist.c16
3 files changed, 14 insertions, 4 deletions
diff --git a/cgit.h b/cgit.h
index ea90833..c99d337 100644
--- a/cgit.h
+++ b/cgit.h
@@ -1,244 +1,245 @@
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#include <utf8.h> 19#include <utf8.h>
20 20
21 21
22/* 22/*
23 * Dateformats used on misc. pages 23 * Dateformats used on misc. pages
24 */ 24 */
25#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 25#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
26#define FMT_SHORTDATE "%Y-%m-%d" 26#define FMT_SHORTDATE "%Y-%m-%d"
27#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 27#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
28 28
29 29
30/* 30/*
31 * Limits used for relative dates 31 * Limits used for relative dates
32 */ 32 */
33#define TM_MIN 60 33#define TM_MIN 60
34#define TM_HOUR (TM_MIN * 60) 34#define TM_HOUR (TM_MIN * 60)
35#define TM_DAY (TM_HOUR * 24) 35#define TM_DAY (TM_HOUR * 24)
36#define TM_WEEK (TM_DAY * 7) 36#define TM_WEEK (TM_DAY * 7)
37#define TM_YEAR (TM_DAY * 365) 37#define TM_YEAR (TM_DAY * 365)
38#define TM_MONTH (TM_YEAR / 12.0) 38#define TM_MONTH (TM_YEAR / 12.0)
39 39
40 40
41/* 41/*
42 * Default encoding 42 * Default encoding
43 */ 43 */
44#define PAGE_ENCODING "UTF-8" 44#define PAGE_ENCODING "UTF-8"
45 45
46typedef void (*configfn)(const char *name, const char *value); 46typedef void (*configfn)(const char *name, const char *value);
47typedef void (*filepair_fn)(struct diff_filepair *pair); 47typedef void (*filepair_fn)(struct diff_filepair *pair);
48typedef void (*linediff_fn)(char *line, int len); 48typedef void (*linediff_fn)(char *line, int len);
49 49
50struct cgit_repo { 50struct cgit_repo {
51 char *url; 51 char *url;
52 char *name; 52 char *name;
53 char *path; 53 char *path;
54 char *desc; 54 char *desc;
55 char *owner; 55 char *owner;
56 char *defbranch; 56 char *defbranch;
57 char *group; 57 char *group;
58 char *module_link; 58 char *module_link;
59 char *readme; 59 char *readme;
60 char *clone_url; 60 char *clone_url;
61 int snapshots; 61 int snapshots;
62 int enable_log_filecount; 62 int enable_log_filecount;
63 int enable_log_linecount; 63 int enable_log_linecount;
64 time_t mtime;
64}; 65};
65 66
66struct cgit_repolist { 67struct cgit_repolist {
67 int length; 68 int length;
68 int count; 69 int count;
69 struct cgit_repo *repos; 70 struct cgit_repo *repos;
70}; 71};
71 72
72struct commitinfo { 73struct commitinfo {
73 struct commit *commit; 74 struct commit *commit;
74 char *author; 75 char *author;
75 char *author_email; 76 char *author_email;
76 unsigned long author_date; 77 unsigned long author_date;
77 char *committer; 78 char *committer;
78 char *committer_email; 79 char *committer_email;
79 unsigned long committer_date; 80 unsigned long committer_date;
80 char *subject; 81 char *subject;
81 char *msg; 82 char *msg;
82 char *msg_encoding; 83 char *msg_encoding;
83}; 84};
84 85
85struct taginfo { 86struct taginfo {
86 char *tagger; 87 char *tagger;
87 char *tagger_email; 88 char *tagger_email;
88 unsigned long tagger_date; 89 unsigned long tagger_date;
89 char *msg; 90 char *msg;
90}; 91};
91 92
92struct refinfo { 93struct refinfo {
93 const char *refname; 94 const char *refname;
94 struct object *object; 95 struct object *object;
95 union { 96 union {
96 struct taginfo *tag; 97 struct taginfo *tag;
97 struct commitinfo *commit; 98 struct commitinfo *commit;
98 }; 99 };
99}; 100};
100 101
101struct reflist { 102struct reflist {
102 struct refinfo **refs; 103 struct refinfo **refs;
103 int alloc; 104 int alloc;
104 int count; 105 int count;
105}; 106};
106 107
107struct cgit_query { 108struct cgit_query {
108 int has_symref; 109 int has_symref;
109 int has_sha1; 110 int has_sha1;
110 char *raw; 111 char *raw;
111 char *repo; 112 char *repo;
112 char *page; 113 char *page;
113 char *search; 114 char *search;
114 char *grep; 115 char *grep;
115 char *head; 116 char *head;
116 char *sha1; 117 char *sha1;
117 char *sha2; 118 char *sha2;
118 char *path; 119 char *path;
119 char *name; 120 char *name;
120 char *mimetype; 121 char *mimetype;
121 char *url; 122 char *url;
122 int ofs; 123 int ofs;
123 int nohead; 124 int nohead;
124 char *sort; 125 char *sort;
125}; 126};
126 127
127struct cgit_config { 128struct cgit_config {
128 char *agefile; 129 char *agefile;
129 char *cache_root; 130 char *cache_root;
130 char *clone_prefix; 131 char *clone_prefix;
131 char *css; 132 char *css;
132 char *favicon; 133 char *favicon;
133 char *footer; 134 char *footer;
134 char *index_header; 135 char *index_header;
135 char *index_info; 136 char *index_info;
136 char *logo; 137 char *logo;
137 char *logo_link; 138 char *logo_link;
138 char *module_link; 139 char *module_link;
139 char *repo_group; 140 char *repo_group;
140 char *robots; 141 char *robots;
141 char *root_title; 142 char *root_title;
142 char *root_desc; 143 char *root_desc;
143 char *root_readme; 144 char *root_readme;
144 char *script_name; 145 char *script_name;
145 char *virtual_root; 146 char *virtual_root;
146 int cache_size; 147 int cache_size;
147 int cache_dynamic_ttl; 148 int cache_dynamic_ttl;
148 int cache_max_create_time; 149 int cache_max_create_time;
149 int cache_repo_ttl; 150 int cache_repo_ttl;
150 int cache_root_ttl; 151 int cache_root_ttl;
151 int cache_static_ttl; 152 int cache_static_ttl;
152 int enable_index_links; 153 int enable_index_links;
153 int enable_log_filecount; 154 int enable_log_filecount;
154 int enable_log_linecount; 155 int enable_log_linecount;
155 int local_time; 156 int local_time;
156 int max_repo_count; 157 int max_repo_count;
157 int max_commit_count; 158 int max_commit_count;
158 int max_lock_attempts; 159 int max_lock_attempts;
159 int max_msg_len; 160 int max_msg_len;
160 int max_repodesc_len; 161 int max_repodesc_len;
161 int nocache; 162 int nocache;
162 int renamelimit; 163 int renamelimit;
163 int snapshots; 164 int snapshots;
164 int summary_branches; 165 int summary_branches;
165 int summary_log; 166 int summary_log;
166 int summary_tags; 167 int summary_tags;
167}; 168};
168 169
169struct cgit_page { 170struct cgit_page {
170 time_t modified; 171 time_t modified;
171 time_t expires; 172 time_t expires;
172 size_t size; 173 size_t size;
173 char *mimetype; 174 char *mimetype;
174 char *charset; 175 char *charset;
175 char *filename; 176 char *filename;
176 char *title; 177 char *title;
177}; 178};
178 179
179struct cgit_context { 180struct cgit_context {
180 struct cgit_query qry; 181 struct cgit_query qry;
181 struct cgit_config cfg; 182 struct cgit_config cfg;
182 struct cgit_repo *repo; 183 struct cgit_repo *repo;
183 struct cgit_page page; 184 struct cgit_page page;
184}; 185};
185 186
186struct cgit_snapshot_format { 187struct cgit_snapshot_format {
187 const char *suffix; 188 const char *suffix;
188 const char *mimetype; 189 const char *mimetype;
189 write_archive_fn_t write_func; 190 write_archive_fn_t write_func;
190 int bit; 191 int bit;
191}; 192};
192 193
193extern const char *cgit_version; 194extern const char *cgit_version;
194 195
195extern struct cgit_repolist cgit_repolist; 196extern struct cgit_repolist cgit_repolist;
196extern struct cgit_context ctx; 197extern struct cgit_context ctx;
197extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 198extern const struct cgit_snapshot_format cgit_snapshot_formats[];
198 199
199extern struct cgit_repo *cgit_add_repo(const char *url); 200extern struct cgit_repo *cgit_add_repo(const char *url);
200extern struct cgit_repo *cgit_get_repoinfo(const char *url); 201extern struct cgit_repo *cgit_get_repoinfo(const char *url);
201extern void cgit_repo_config_cb(const char *name, const char *value); 202extern void cgit_repo_config_cb(const char *name, const char *value);
202 203
203extern int chk_zero(int result, char *msg); 204extern int chk_zero(int result, char *msg);
204extern int chk_positive(int result, char *msg); 205extern int chk_positive(int result, char *msg);
205extern int chk_non_negative(int result, char *msg); 206extern int chk_non_negative(int result, char *msg);
206 207
207extern char *trim_end(const char *str, char c); 208extern char *trim_end(const char *str, char c);
208extern char *strlpart(char *txt, int maxlen); 209extern char *strlpart(char *txt, int maxlen);
209extern char *strrpart(char *txt, int maxlen); 210extern char *strrpart(char *txt, int maxlen);
210 211
211extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 212extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
212extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 213extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
213 int flags, void *cb_data); 214 int flags, void *cb_data);
214 215
215extern void *cgit_free_commitinfo(struct commitinfo *info); 216extern void *cgit_free_commitinfo(struct commitinfo *info);
216 217
217extern int cgit_diff_files(const unsigned char *old_sha1, 218extern int cgit_diff_files(const unsigned char *old_sha1,
218 const unsigned char *new_sha1, 219 const unsigned char *new_sha1,
219 linediff_fn fn); 220 linediff_fn fn);
220 221
221extern void cgit_diff_tree(const unsigned char *old_sha1, 222extern void cgit_diff_tree(const unsigned char *old_sha1,
222 const unsigned char *new_sha1, 223 const unsigned char *new_sha1,
223 filepair_fn fn, const char *prefix); 224 filepair_fn fn, const char *prefix);
224 225
225extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 226extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
226 227
227extern char *fmt(const char *format,...); 228extern char *fmt(const char *format,...);
228 229
229extern struct commitinfo *cgit_parse_commit(struct commit *commit); 230extern struct commitinfo *cgit_parse_commit(struct commit *commit);
230extern struct taginfo *cgit_parse_tag(struct tag *tag); 231extern struct taginfo *cgit_parse_tag(struct tag *tag);
231extern void cgit_parse_url(const char *url); 232extern void cgit_parse_url(const char *url);
232 233
233extern const char *cgit_repobasename(const char *reponame); 234extern const char *cgit_repobasename(const char *reponame);
234 235
235extern int cgit_parse_snapshots_mask(const char *str); 236extern int cgit_parse_snapshots_mask(const char *str);
236 237
237/* libgit.a either links against or compiles its own implementation of 238/* libgit.a either links against or compiles its own implementation of
238 * strcasestr(), and we'd like to reuse it. Simply re-declaring it 239 * strcasestr(), and we'd like to reuse it. Simply re-declaring it
239 * seems to do the trick. 240 * seems to do the trick.
240 */ 241 */
241extern char *strcasestr(const char *haystack, const char *needle); 242extern char *strcasestr(const char *haystack, const char *needle);
242 243
243 244
244#endif /* CGIT_H */ 245#endif /* CGIT_H */
diff --git a/shared.c b/shared.c
index f5875e4..89d1bab 100644
--- a/shared.c
+++ b/shared.c
@@ -1,254 +1,255 @@
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 cgit_repolist cgit_repolist; 11struct cgit_repolist cgit_repolist;
12struct cgit_context ctx; 12struct cgit_context ctx;
13int cgit_cmd; 13int cgit_cmd;
14 14
15int chk_zero(int result, char *msg) 15int chk_zero(int result, char *msg)
16{ 16{
17 if (result != 0) 17 if (result != 0)
18 die("%s: %s", msg, strerror(errno)); 18 die("%s: %s", msg, strerror(errno));
19 return result; 19 return result;
20} 20}
21 21
22int chk_positive(int result, char *msg) 22int chk_positive(int result, char *msg)
23{ 23{
24 if (result <= 0) 24 if (result <= 0)
25 die("%s: %s", msg, strerror(errno)); 25 die("%s: %s", msg, strerror(errno));
26 return result; 26 return result;
27} 27}
28 28
29int chk_non_negative(int result, char *msg) 29int chk_non_negative(int result, char *msg)
30{ 30{
31 if (result < 0) 31 if (result < 0)
32 die("%s: %s",msg, strerror(errno)); 32 die("%s: %s",msg, strerror(errno));
33 return result; 33 return result;
34} 34}
35 35
36struct cgit_repo *cgit_add_repo(const char *url) 36struct cgit_repo *cgit_add_repo(const char *url)
37{ 37{
38 struct cgit_repo *ret; 38 struct cgit_repo *ret;
39 39
40 if (++cgit_repolist.count > cgit_repolist.length) { 40 if (++cgit_repolist.count > cgit_repolist.length) {
41 if (cgit_repolist.length == 0) 41 if (cgit_repolist.length == 0)
42 cgit_repolist.length = 8; 42 cgit_repolist.length = 8;
43 else 43 else
44 cgit_repolist.length *= 2; 44 cgit_repolist.length *= 2;
45 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 45 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
46 cgit_repolist.length * 46 cgit_repolist.length *
47 sizeof(struct cgit_repo)); 47 sizeof(struct cgit_repo));
48 } 48 }
49 49
50 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 50 ret = &cgit_repolist.repos[cgit_repolist.count-1];
51 ret->url = trim_end(url, '/'); 51 ret->url = trim_end(url, '/');
52 ret->name = ret->url; 52 ret->name = ret->url;
53 ret->path = NULL; 53 ret->path = NULL;
54 ret->desc = "[no description]"; 54 ret->desc = "[no description]";
55 ret->owner = NULL; 55 ret->owner = NULL;
56 ret->group = ctx.cfg.repo_group; 56 ret->group = ctx.cfg.repo_group;
57 ret->defbranch = "master"; 57 ret->defbranch = "master";
58 ret->snapshots = ctx.cfg.snapshots; 58 ret->snapshots = ctx.cfg.snapshots;
59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->module_link = ctx.cfg.module_link; 61 ret->module_link = ctx.cfg.module_link;
62 ret->readme = NULL; 62 ret->readme = NULL;
63 ret->mtime = -1;
63 return ret; 64 return ret;
64} 65}
65 66
66struct cgit_repo *cgit_get_repoinfo(const char *url) 67struct cgit_repo *cgit_get_repoinfo(const char *url)
67{ 68{
68 int i; 69 int i;
69 struct cgit_repo *repo; 70 struct cgit_repo *repo;
70 71
71 for (i=0; i<cgit_repolist.count; i++) { 72 for (i=0; i<cgit_repolist.count; i++) {
72 repo = &cgit_repolist.repos[i]; 73 repo = &cgit_repolist.repos[i];
73 if (!strcmp(repo->url, url)) 74 if (!strcmp(repo->url, url))
74 return repo; 75 return repo;
75 } 76 }
76 return NULL; 77 return NULL;
77} 78}
78 79
79void *cgit_free_commitinfo(struct commitinfo *info) 80void *cgit_free_commitinfo(struct commitinfo *info)
80{ 81{
81 free(info->author); 82 free(info->author);
82 free(info->author_email); 83 free(info->author_email);
83 free(info->committer); 84 free(info->committer);
84 free(info->committer_email); 85 free(info->committer_email);
85 free(info->subject); 86 free(info->subject);
86 free(info->msg); 87 free(info->msg);
87 free(info->msg_encoding); 88 free(info->msg_encoding);
88 free(info); 89 free(info);
89 return NULL; 90 return NULL;
90} 91}
91 92
92char *trim_end(const char *str, char c) 93char *trim_end(const char *str, char c)
93{ 94{
94 int len; 95 int len;
95 char *s, *t; 96 char *s, *t;
96 97
97 if (str == NULL) 98 if (str == NULL)
98 return NULL; 99 return NULL;
99 t = (char *)str; 100 t = (char *)str;
100 len = strlen(t); 101 len = strlen(t);
101 while(len > 0 && t[len - 1] == c) 102 while(len > 0 && t[len - 1] == c)
102 len--; 103 len--;
103 104
104 if (len == 0) 105 if (len == 0)
105 return NULL; 106 return NULL;
106 107
107 c = t[len]; 108 c = t[len];
108 t[len] = '\0'; 109 t[len] = '\0';
109 s = xstrdup(t); 110 s = xstrdup(t);
110 t[len] = c; 111 t[len] = c;
111 return s; 112 return s;
112} 113}
113 114
114char *strlpart(char *txt, int maxlen) 115char *strlpart(char *txt, int maxlen)
115{ 116{
116 char *result; 117 char *result;
117 118
118 if (!txt) 119 if (!txt)
119 return txt; 120 return txt;
120 121
121 if (strlen(txt) <= maxlen) 122 if (strlen(txt) <= maxlen)
122 return txt; 123 return txt;
123 result = xmalloc(maxlen + 1); 124 result = xmalloc(maxlen + 1);
124 memcpy(result, txt, maxlen - 3); 125 memcpy(result, txt, maxlen - 3);
125 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.'; 126 result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.';
126 result[maxlen] = '\0'; 127 result[maxlen] = '\0';
127 return result; 128 return result;
128} 129}
129 130
130char *strrpart(char *txt, int maxlen) 131char *strrpart(char *txt, int maxlen)
131{ 132{
132 char *result; 133 char *result;
133 134
134 if (!txt) 135 if (!txt)
135 return txt; 136 return txt;
136 137
137 if (strlen(txt) <= maxlen) 138 if (strlen(txt) <= maxlen)
138 return txt; 139 return txt;
139 result = xmalloc(maxlen + 1); 140 result = xmalloc(maxlen + 1);
140 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3); 141 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
141 result[0] = result[1] = result[2] = '.'; 142 result[0] = result[1] = result[2] = '.';
142 return result; 143 return result;
143} 144}
144 145
145void cgit_add_ref(struct reflist *list, struct refinfo *ref) 146void cgit_add_ref(struct reflist *list, struct refinfo *ref)
146{ 147{
147 size_t size; 148 size_t size;
148 149
149 if (list->count >= list->alloc) { 150 if (list->count >= list->alloc) {
150 list->alloc += (list->alloc ? list->alloc : 4); 151 list->alloc += (list->alloc ? list->alloc : 4);
151 size = list->alloc * sizeof(struct refinfo *); 152 size = list->alloc * sizeof(struct refinfo *);
152 list->refs = xrealloc(list->refs, size); 153 list->refs = xrealloc(list->refs, size);
153 } 154 }
154 list->refs[list->count++] = ref; 155 list->refs[list->count++] = ref;
155} 156}
156 157
157struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 158struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
158{ 159{
159 struct refinfo *ref; 160 struct refinfo *ref;
160 161
161 ref = xmalloc(sizeof (struct refinfo)); 162 ref = xmalloc(sizeof (struct refinfo));
162 ref->refname = xstrdup(refname); 163 ref->refname = xstrdup(refname);
163 ref->object = parse_object(sha1); 164 ref->object = parse_object(sha1);
164 switch (ref->object->type) { 165 switch (ref->object->type) {
165 case OBJ_TAG: 166 case OBJ_TAG:
166 ref->tag = cgit_parse_tag((struct tag *)ref->object); 167 ref->tag = cgit_parse_tag((struct tag *)ref->object);
167 break; 168 break;
168 case OBJ_COMMIT: 169 case OBJ_COMMIT:
169 ref->commit = cgit_parse_commit((struct commit *)ref->object); 170 ref->commit = cgit_parse_commit((struct commit *)ref->object);
170 break; 171 break;
171 } 172 }
172 return ref; 173 return ref;
173} 174}
174 175
175int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, 176int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
176 void *cb_data) 177 void *cb_data)
177{ 178{
178 struct reflist *list = (struct reflist *)cb_data; 179 struct reflist *list = (struct reflist *)cb_data;
179 struct refinfo *info = cgit_mk_refinfo(refname, sha1); 180 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
180 181
181 if (info) 182 if (info)
182 cgit_add_ref(list, info); 183 cgit_add_ref(list, info);
183 return 0; 184 return 0;
184} 185}
185 186
186void cgit_diff_tree_cb(struct diff_queue_struct *q, 187void cgit_diff_tree_cb(struct diff_queue_struct *q,
187 struct diff_options *options, void *data) 188 struct diff_options *options, void *data)
188{ 189{
189 int i; 190 int i;
190 191
191 for (i = 0; i < q->nr; i++) { 192 for (i = 0; i < q->nr; i++) {
192 if (q->queue[i]->status == 'U') 193 if (q->queue[i]->status == 'U')
193 continue; 194 continue;
194 ((filepair_fn)data)(q->queue[i]); 195 ((filepair_fn)data)(q->queue[i]);
195 } 196 }
196} 197}
197 198
198static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 199static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
199{ 200{
200 enum object_type type; 201 enum object_type type;
201 202
202 if (is_null_sha1(sha1)) { 203 if (is_null_sha1(sha1)) {
203 file->ptr = (char *)""; 204 file->ptr = (char *)"";
204 file->size = 0; 205 file->size = 0;
205 } else { 206 } else {
206 file->ptr = read_sha1_file(sha1, &type, 207 file->ptr = read_sha1_file(sha1, &type,
207 (unsigned long *)&file->size); 208 (unsigned long *)&file->size);
208 } 209 }
209 return 1; 210 return 1;
210} 211}
211 212
212/* 213/*
213 * Receive diff-buffers from xdiff and concatenate them as 214 * Receive diff-buffers from xdiff and concatenate them as
214 * needed across multiple callbacks. 215 * needed across multiple callbacks.
215 * 216 *
216 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 217 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
217 * ripped from git and modified to use globals instead of 218 * ripped from git and modified to use globals instead of
218 * a special callback-struct. 219 * a special callback-struct.
219 */ 220 */
220char *diffbuf = NULL; 221char *diffbuf = NULL;
221int buflen = 0; 222int buflen = 0;
222 223
223int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 224int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
224{ 225{
225 int i; 226 int i;
226 227
227 for (i = 0; i < nbuf; i++) { 228 for (i = 0; i < nbuf; i++) {
228 if (mb[i].ptr[mb[i].size-1] != '\n') { 229 if (mb[i].ptr[mb[i].size-1] != '\n') {
229 /* Incomplete line */ 230 /* Incomplete line */
230 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 231 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
231 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 232 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
232 buflen += mb[i].size; 233 buflen += mb[i].size;
233 continue; 234 continue;
234 } 235 }
235 236
236 /* we have a complete line */ 237 /* we have a complete line */
237 if (!diffbuf) { 238 if (!diffbuf) {
238 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 239 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
239 continue; 240 continue;
240 } 241 }
241 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 242 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
242 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 243 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
243 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 244 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
244 free(diffbuf); 245 free(diffbuf);
245 diffbuf = NULL; 246 diffbuf = NULL;
246 buflen = 0; 247 buflen = 0;
247 } 248 }
248 if (diffbuf) { 249 if (diffbuf) {
249 ((linediff_fn)priv)(diffbuf, buflen); 250 ((linediff_fn)priv)(diffbuf, buflen);
250 free(diffbuf); 251 free(diffbuf);
251 diffbuf = NULL; 252 diffbuf = NULL;
252 buflen = 0; 253 buflen = 0;
253 } 254 }
254 return 0; 255 return 0;
diff --git a/ui-repolist.c b/ui-repolist.c
index cf27cb3..aa743bf 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -1,239 +1,247 @@
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>");