author | Lars Hjemli <hjemli@gmail.com> | 2008-11-29 15:46:37 (UTC) |
---|---|---|
committer | Lars Hjemli <hjemli@gmail.com> | 2008-11-29 15:46:37 (UTC) |
commit | 8813170390f3c3a0f4743afbc92ede42953fa3b0 (patch) (unidiff) | |
tree | 39305350baee1eb564aae00294634bbe544983d3 | |
parent | 54272e60965ec6a98b49cbf67d72a4b1f5adc55b (diff) | |
download | cgit-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>
-rw-r--r-- | cgit.h | 1 | ||||
-rw-r--r-- | shared.c | 1 | ||||
-rw-r--r-- | ui-repolist.c | 16 |
3 files changed, 14 insertions, 4 deletions
@@ -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 | ||
46 | typedef void (*configfn)(const char *name, const char *value); | 46 | typedef void (*configfn)(const char *name, const char *value); |
47 | typedef void (*filepair_fn)(struct diff_filepair *pair); | 47 | typedef void (*filepair_fn)(struct diff_filepair *pair); |
48 | typedef void (*linediff_fn)(char *line, int len); | 48 | typedef void (*linediff_fn)(char *line, int len); |
49 | 49 | ||
50 | struct cgit_repo { | 50 | struct 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 | ||
66 | struct cgit_repolist { | 67 | struct 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 | ||
72 | struct commitinfo { | 73 | struct 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 | ||
85 | struct taginfo { | 86 | struct 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 | ||
92 | struct refinfo { | 93 | struct 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 | ||
101 | struct reflist { | 102 | struct 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 | ||
107 | struct cgit_query { | 108 | struct 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 | ||
127 | struct cgit_config { | 128 | struct 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 | ||
169 | struct cgit_page { | 170 | struct 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 | ||
179 | struct cgit_context { | 180 | struct 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 | ||
186 | struct cgit_snapshot_format { | 187 | struct 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 | ||
193 | extern const char *cgit_version; | 194 | extern const char *cgit_version; |
194 | 195 | ||
195 | extern struct cgit_repolist cgit_repolist; | 196 | extern struct cgit_repolist cgit_repolist; |
196 | extern struct cgit_context ctx; | 197 | extern struct cgit_context ctx; |
197 | extern const struct cgit_snapshot_format cgit_snapshot_formats[]; | 198 | extern const struct cgit_snapshot_format cgit_snapshot_formats[]; |
198 | 199 | ||
199 | extern struct cgit_repo *cgit_add_repo(const char *url); | 200 | extern struct cgit_repo *cgit_add_repo(const char *url); |
200 | extern struct cgit_repo *cgit_get_repoinfo(const char *url); | 201 | extern struct cgit_repo *cgit_get_repoinfo(const char *url); |
201 | extern void cgit_repo_config_cb(const char *name, const char *value); | 202 | extern void cgit_repo_config_cb(const char *name, const char *value); |
202 | 203 | ||
203 | extern int chk_zero(int result, char *msg); | 204 | extern int chk_zero(int result, char *msg); |
204 | extern int chk_positive(int result, char *msg); | 205 | extern int chk_positive(int result, char *msg); |
205 | extern int chk_non_negative(int result, char *msg); | 206 | extern int chk_non_negative(int result, char *msg); |
206 | 207 | ||
207 | extern char *trim_end(const char *str, char c); | 208 | extern char *trim_end(const char *str, char c); |
208 | extern char *strlpart(char *txt, int maxlen); | 209 | extern char *strlpart(char *txt, int maxlen); |
209 | extern char *strrpart(char *txt, int maxlen); | 210 | extern char *strrpart(char *txt, int maxlen); |
210 | 211 | ||
211 | extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); | 212 | extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); |
212 | extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, | 213 | extern 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 | ||
215 | extern void *cgit_free_commitinfo(struct commitinfo *info); | 216 | extern void *cgit_free_commitinfo(struct commitinfo *info); |
216 | 217 | ||
217 | extern int cgit_diff_files(const unsigned char *old_sha1, | 218 | extern 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 | ||
221 | extern void cgit_diff_tree(const unsigned char *old_sha1, | 222 | extern 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 | ||
225 | extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); | 226 | extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); |
226 | 227 | ||
227 | extern char *fmt(const char *format,...); | 228 | extern char *fmt(const char *format,...); |
228 | 229 | ||
229 | extern struct commitinfo *cgit_parse_commit(struct commit *commit); | 230 | extern struct commitinfo *cgit_parse_commit(struct commit *commit); |
230 | extern struct taginfo *cgit_parse_tag(struct tag *tag); | 231 | extern struct taginfo *cgit_parse_tag(struct tag *tag); |
231 | extern void cgit_parse_url(const char *url); | 232 | extern void cgit_parse_url(const char *url); |
232 | 233 | ||
233 | extern const char *cgit_repobasename(const char *reponame); | 234 | extern const char *cgit_repobasename(const char *reponame); |
234 | 235 | ||
235 | extern int cgit_parse_snapshots_mask(const char *str); | 236 | extern 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 | */ |
241 | extern char *strcasestr(const char *haystack, const char *needle); | 242 | extern char *strcasestr(const char *haystack, const char *needle); |
242 | 243 | ||
243 | 244 | ||
244 | #endif /* CGIT_H */ | 245 | #endif /* CGIT_H */ |
@@ -1,344 +1,345 @@ | |||
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 | ||
11 | struct cgit_repolist cgit_repolist; | 11 | struct cgit_repolist cgit_repolist; |
12 | struct cgit_context ctx; | 12 | struct cgit_context ctx; |
13 | int cgit_cmd; | 13 | int cgit_cmd; |
14 | 14 | ||
15 | int chk_zero(int result, char *msg) | 15 | int 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 | ||
22 | int chk_positive(int result, char *msg) | 22 | int 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 | ||
29 | int chk_non_negative(int result, char *msg) | 29 | int 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 | ||
36 | struct cgit_repo *cgit_add_repo(const char *url) | 36 | struct 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 | ||
66 | struct cgit_repo *cgit_get_repoinfo(const char *url) | 67 | struct 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 | ||
79 | void *cgit_free_commitinfo(struct commitinfo *info) | 80 | void *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 | ||
92 | char *trim_end(const char *str, char c) | 93 | char *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 | ||
114 | char *strlpart(char *txt, int maxlen) | 115 | char *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 | ||
130 | char *strrpart(char *txt, int maxlen) | 131 | char *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 | ||
145 | void cgit_add_ref(struct reflist *list, struct refinfo *ref) | 146 | void 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 | ||
157 | struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) | 158 | struct 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 | ||
175 | int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, | 176 | int 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 | ||
186 | void cgit_diff_tree_cb(struct diff_queue_struct *q, | 187 | void 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 | ||
198 | static int load_mmfile(mmfile_t *file, const unsigned char *sha1) | 199 | static 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 | */ |
220 | char *diffbuf = NULL; | 221 | char *diffbuf = NULL; |
221 | int buflen = 0; | 222 | int buflen = 0; |
222 | 223 | ||
223 | int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) | 224 | int 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; |
255 | } | 256 | } |
256 | 257 | ||
257 | int cgit_diff_files(const unsigned char *old_sha1, | 258 | int cgit_diff_files(const unsigned char *old_sha1, |
258 | const unsigned char *new_sha1, | 259 | const unsigned char *new_sha1, |
259 | linediff_fn fn) | 260 | linediff_fn fn) |
260 | { | 261 | { |
261 | mmfile_t file1, file2; | 262 | mmfile_t file1, file2; |
262 | xpparam_t diff_params; | 263 | xpparam_t diff_params; |
263 | xdemitconf_t emit_params; | 264 | xdemitconf_t emit_params; |
264 | xdemitcb_t emit_cb; | 265 | xdemitcb_t emit_cb; |
265 | 266 | ||
266 | if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) | 267 | if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) |
267 | return 1; | 268 | return 1; |
268 | 269 | ||
269 | diff_params.flags = XDF_NEED_MINIMAL; | 270 | diff_params.flags = XDF_NEED_MINIMAL; |
270 | emit_params.ctxlen = 3; | 271 | emit_params.ctxlen = 3; |
271 | emit_params.flags = XDL_EMIT_FUNCNAMES; | 272 | emit_params.flags = XDL_EMIT_FUNCNAMES; |
272 | emit_params.find_func = NULL; | 273 | emit_params.find_func = NULL; |
273 | emit_cb.outf = filediff_cb; | 274 | emit_cb.outf = filediff_cb; |
274 | emit_cb.priv = fn; | 275 | emit_cb.priv = fn; |
275 | xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); | 276 | xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); |
276 | return 0; | 277 | return 0; |
277 | } | 278 | } |
278 | 279 | ||
279 | void cgit_diff_tree(const unsigned char *old_sha1, | 280 | void cgit_diff_tree(const unsigned char *old_sha1, |
280 | const unsigned char *new_sha1, | 281 | const unsigned char *new_sha1, |
281 | filepair_fn fn, const char *prefix) | 282 | filepair_fn fn, const char *prefix) |
282 | { | 283 | { |
283 | struct diff_options opt; | 284 | struct diff_options opt; |
284 | int ret; | 285 | int ret; |
285 | int prefixlen; | 286 | int prefixlen; |
286 | 287 | ||
287 | diff_setup(&opt); | 288 | diff_setup(&opt); |
288 | opt.output_format = DIFF_FORMAT_CALLBACK; | 289 | opt.output_format = DIFF_FORMAT_CALLBACK; |
289 | opt.detect_rename = 1; | 290 | opt.detect_rename = 1; |
290 | opt.rename_limit = ctx.cfg.renamelimit; | 291 | opt.rename_limit = ctx.cfg.renamelimit; |
291 | DIFF_OPT_SET(&opt, RECURSIVE); | 292 | DIFF_OPT_SET(&opt, RECURSIVE); |
292 | opt.format_callback = cgit_diff_tree_cb; | 293 | opt.format_callback = cgit_diff_tree_cb; |
293 | opt.format_callback_data = fn; | 294 | opt.format_callback_data = fn; |
294 | if (prefix) { | 295 | if (prefix) { |
295 | opt.nr_paths = 1; | 296 | opt.nr_paths = 1; |
296 | opt.paths = &prefix; | 297 | opt.paths = &prefix; |
297 | prefixlen = strlen(prefix); | 298 | prefixlen = strlen(prefix); |
298 | opt.pathlens = &prefixlen; | 299 | opt.pathlens = &prefixlen; |
299 | } | 300 | } |
300 | diff_setup_done(&opt); | 301 | diff_setup_done(&opt); |
301 | 302 | ||
302 | if (old_sha1 && !is_null_sha1(old_sha1)) | 303 | if (old_sha1 && !is_null_sha1(old_sha1)) |
303 | ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); | 304 | ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); |
304 | else | 305 | else |
305 | ret = diff_root_tree_sha1(new_sha1, "", &opt); | 306 | ret = diff_root_tree_sha1(new_sha1, "", &opt); |
306 | diffcore_std(&opt); | 307 | diffcore_std(&opt); |
307 | diff_flush(&opt); | 308 | diff_flush(&opt); |
308 | } | 309 | } |
309 | 310 | ||
310 | void cgit_diff_commit(struct commit *commit, filepair_fn fn) | 311 | void cgit_diff_commit(struct commit *commit, filepair_fn fn) |
311 | { | 312 | { |
312 | unsigned char *old_sha1 = NULL; | 313 | unsigned char *old_sha1 = NULL; |
313 | 314 | ||
314 | if (commit->parents) | 315 | if (commit->parents) |
315 | old_sha1 = commit->parents->item->object.sha1; | 316 | old_sha1 = commit->parents->item->object.sha1; |
316 | cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); | 317 | cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); |
317 | } | 318 | } |
318 | 319 | ||
319 | int cgit_parse_snapshots_mask(const char *str) | 320 | int cgit_parse_snapshots_mask(const char *str) |
320 | { | 321 | { |
321 | const struct cgit_snapshot_format *f; | 322 | const struct cgit_snapshot_format *f; |
322 | static const char *delim = " \t,:/|;"; | 323 | static const char *delim = " \t,:/|;"; |
323 | int tl, sl, rv = 0; | 324 | int tl, sl, rv = 0; |
324 | 325 | ||
325 | /* favor legacy setting */ | 326 | /* favor legacy setting */ |
326 | if(atoi(str)) | 327 | if(atoi(str)) |
327 | return 1; | 328 | return 1; |
328 | for(;;) { | 329 | for(;;) { |
329 | str += strspn(str,delim); | 330 | str += strspn(str,delim); |
330 | tl = strcspn(str,delim); | 331 | tl = strcspn(str,delim); |
331 | if (!tl) | 332 | if (!tl) |
332 | break; | 333 | break; |
333 | for (f = cgit_snapshot_formats; f->suffix; f++) { | 334 | for (f = cgit_snapshot_formats; f->suffix; f++) { |
334 | sl = strlen(f->suffix); | 335 | sl = strlen(f->suffix); |
335 | if((tl == sl && !strncmp(f->suffix, str, tl)) || | 336 | if((tl == sl && !strncmp(f->suffix, str, tl)) || |
336 | (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { | 337 | (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { |
337 | rv |= f->bit; | 338 | rv |= f->bit; |
338 | break; | 339 | break; |
339 | } | 340 | } |
340 | } | 341 | } |
341 | str += tl; | 342 | str += tl; |
342 | } | 343 | } |
343 | return rv; | 344 | return rv; |
344 | } | 345 | } |
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 | ||
15 | time_t read_agefile(char *path) | 15 | time_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 | ||
31 | static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) | 31 | static 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 | ||
50 | static void print_modtime(struct cgit_repo *repo) | 58 | static 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 | ||
57 | int is_match(struct cgit_repo *repo) | 65 | int 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 | ||
72 | int is_in_url(struct cgit_repo *repo) | 80 | int 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 | ||
81 | void print_sort_header(const char *title, const char *sort) | 89 | void 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 | ||
91 | void print_header(int columns) | 99 | void 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 | ||
104 | void print_pager(int items, int pagelen, char *search) | 112 | void 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 | ||
114 | static int cmp(const char *s1, const char *s2) | 122 | static 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 | ||
125 | static int sort_name(const void *a, const void *b) | 133 | static 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 | ||
133 | static int sort_desc(const void *a, const void *b) | 141 | static 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 | ||
141 | static int sort_owner(const void *a, const void *b) | 149 | static 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 | ||
149 | static int sort_idle(const void *a, const void *b) | 157 | static 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 | ||
161 | struct sortcolumn { | 169 | struct 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 | ||
166 | struct sortcolumn sortcolumn[] = { | 174 | struct 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 | ||
174 | int sort_repolist(char *field) | 182 | int 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 | ||
189 | void cgit_print_repolist() | 197 | void 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 | ||
262 | void cgit_print_site_readme() | 270 | void 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 | } |