summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.h1
-rw-r--r--scan-tree.c16
-rw-r--r--shared.c21
-rw-r--r--ui-repolist.c19
4 files changed, 34 insertions, 23 deletions
diff --git a/cgit.h b/cgit.h
index d90ccdc..adb8da4 100644
--- a/cgit.h
+++ b/cgit.h
@@ -30,258 +30,259 @@
30 30
31 31
32/* 32/*
33 * Limits used for relative dates 33 * Limits used for relative dates
34 */ 34 */
35#define TM_MIN 60 35#define TM_MIN 60
36#define TM_HOUR (TM_MIN * 60) 36#define TM_HOUR (TM_MIN * 60)
37#define TM_DAY (TM_HOUR * 24) 37#define TM_DAY (TM_HOUR * 24)
38#define TM_WEEK (TM_DAY * 7) 38#define TM_WEEK (TM_DAY * 7)
39#define TM_YEAR (TM_DAY * 365) 39#define TM_YEAR (TM_DAY * 365)
40#define TM_MONTH (TM_YEAR / 12.0) 40#define TM_MONTH (TM_YEAR / 12.0)
41 41
42 42
43/* 43/*
44 * Default encoding 44 * Default encoding
45 */ 45 */
46#define PAGE_ENCODING "UTF-8" 46#define PAGE_ENCODING "UTF-8"
47 47
48typedef void (*configfn)(const char *name, const char *value); 48typedef void (*configfn)(const char *name, const char *value);
49typedef void (*filepair_fn)(struct diff_filepair *pair); 49typedef void (*filepair_fn)(struct diff_filepair *pair);
50typedef void (*linediff_fn)(char *line, int len); 50typedef void (*linediff_fn)(char *line, int len);
51 51
52struct cgit_filter { 52struct cgit_filter {
53 char *cmd; 53 char *cmd;
54 char **argv; 54 char **argv;
55 int old_stdout; 55 int old_stdout;
56 int pipe_fh[2]; 56 int pipe_fh[2];
57 int pid; 57 int pid;
58 int exitstatus; 58 int exitstatus;
59}; 59};
60 60
61struct cgit_repo { 61struct cgit_repo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *group; 68 char *group;
69 char *module_link; 69 char *module_link;
70 char *readme; 70 char *readme;
71 char *clone_url; 71 char *clone_url;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75 int max_stats; 75 int max_stats;
76 time_t mtime; 76 time_t mtime;
77 struct cgit_filter *about_filter; 77 struct cgit_filter *about_filter;
78 struct cgit_filter *commit_filter; 78 struct cgit_filter *commit_filter;
79 struct cgit_filter *source_filter; 79 struct cgit_filter *source_filter;
80}; 80};
81 81
82struct cgit_repolist { 82struct cgit_repolist {
83 int length; 83 int length;
84 int count; 84 int count;
85 struct cgit_repo *repos; 85 struct cgit_repo *repos;
86}; 86};
87 87
88struct commitinfo { 88struct commitinfo {
89 struct commit *commit; 89 struct commit *commit;
90 char *author; 90 char *author;
91 char *author_email; 91 char *author_email;
92 unsigned long author_date; 92 unsigned long author_date;
93 char *committer; 93 char *committer;
94 char *committer_email; 94 char *committer_email;
95 unsigned long committer_date; 95 unsigned long committer_date;
96 char *subject; 96 char *subject;
97 char *msg; 97 char *msg;
98 char *msg_encoding; 98 char *msg_encoding;
99}; 99};
100 100
101struct taginfo { 101struct taginfo {
102 char *tagger; 102 char *tagger;
103 char *tagger_email; 103 char *tagger_email;
104 unsigned long tagger_date; 104 unsigned long tagger_date;
105 char *msg; 105 char *msg;
106}; 106};
107 107
108struct refinfo { 108struct refinfo {
109 const char *refname; 109 const char *refname;
110 struct object *object; 110 struct object *object;
111 union { 111 union {
112 struct taginfo *tag; 112 struct taginfo *tag;
113 struct commitinfo *commit; 113 struct commitinfo *commit;
114 }; 114 };
115}; 115};
116 116
117struct reflist { 117struct reflist {
118 struct refinfo **refs; 118 struct refinfo **refs;
119 int alloc; 119 int alloc;
120 int count; 120 int count;
121}; 121};
122 122
123struct cgit_query { 123struct cgit_query {
124 int has_symref; 124 int has_symref;
125 int has_sha1; 125 int has_sha1;
126 char *raw; 126 char *raw;
127 char *repo; 127 char *repo;
128 char *page; 128 char *page;
129 char *search; 129 char *search;
130 char *grep; 130 char *grep;
131 char *head; 131 char *head;
132 char *sha1; 132 char *sha1;
133 char *sha2; 133 char *sha2;
134 char *path; 134 char *path;
135 char *name; 135 char *name;
136 char *mimetype; 136 char *mimetype;
137 char *url; 137 char *url;
138 char *period; 138 char *period;
139 int ofs; 139 int ofs;
140 int nohead; 140 int nohead;
141 char *sort; 141 char *sort;
142 int showmsg; 142 int showmsg;
143}; 143};
144 144
145struct cgit_config { 145struct cgit_config {
146 char *agefile; 146 char *agefile;
147 char *cache_root; 147 char *cache_root;
148 char *clone_prefix; 148 char *clone_prefix;
149 char *css; 149 char *css;
150 char *favicon; 150 char *favicon;
151 char *footer; 151 char *footer;
152 char *head_include; 152 char *head_include;
153 char *header; 153 char *header;
154 char *index_header; 154 char *index_header;
155 char *index_info; 155 char *index_info;
156 char *logo; 156 char *logo;
157 char *logo_link; 157 char *logo_link;
158 char *module_link; 158 char *module_link;
159 char *repo_group; 159 char *repo_group;
160 char *robots; 160 char *robots;
161 char *root_title; 161 char *root_title;
162 char *root_desc; 162 char *root_desc;
163 char *root_readme; 163 char *root_readme;
164 char *script_name; 164 char *script_name;
165 char *virtual_root; 165 char *virtual_root;
166 int cache_size; 166 int cache_size;
167 int cache_dynamic_ttl; 167 int cache_dynamic_ttl;
168 int cache_max_create_time; 168 int cache_max_create_time;
169 int cache_repo_ttl; 169 int cache_repo_ttl;
170 int cache_root_ttl; 170 int cache_root_ttl;
171 int cache_static_ttl; 171 int cache_static_ttl;
172 int embedded; 172 int embedded;
173 int enable_index_links; 173 int enable_index_links;
174 int enable_log_filecount; 174 int enable_log_filecount;
175 int enable_log_linecount; 175 int enable_log_linecount;
176 int local_time; 176 int local_time;
177 int max_repo_count; 177 int max_repo_count;
178 int max_commit_count; 178 int max_commit_count;
179 int max_lock_attempts; 179 int max_lock_attempts;
180 int max_msg_len; 180 int max_msg_len;
181 int max_repodesc_len; 181 int max_repodesc_len;
182 int max_stats; 182 int max_stats;
183 int nocache; 183 int nocache;
184 int noplainemail; 184 int noplainemail;
185 int noheader; 185 int noheader;
186 int renamelimit; 186 int renamelimit;
187 int snapshots; 187 int snapshots;
188 int summary_branches; 188 int summary_branches;
189 int summary_log; 189 int summary_log;
190 int summary_tags; 190 int summary_tags;
191 struct string_list mimetypes; 191 struct string_list mimetypes;
192 struct cgit_filter *about_filter; 192 struct cgit_filter *about_filter;
193 struct cgit_filter *commit_filter; 193 struct cgit_filter *commit_filter;
194 struct cgit_filter *source_filter; 194 struct cgit_filter *source_filter;
195}; 195};
196 196
197struct cgit_page { 197struct cgit_page {
198 time_t modified; 198 time_t modified;
199 time_t expires; 199 time_t expires;
200 size_t size; 200 size_t size;
201 char *mimetype; 201 char *mimetype;
202 char *charset; 202 char *charset;
203 char *filename; 203 char *filename;
204 char *etag; 204 char *etag;
205 char *title; 205 char *title;
206 int status; 206 int status;
207 char *statusmsg; 207 char *statusmsg;
208}; 208};
209 209
210struct cgit_environment { 210struct cgit_environment {
211 char *cgit_config; 211 char *cgit_config;
212 char *http_host; 212 char *http_host;
213 char *https; 213 char *https;
214 char *no_http; 214 char *no_http;
215 char *path_info; 215 char *path_info;
216 char *query_string; 216 char *query_string;
217 char *request_method; 217 char *request_method;
218 char *script_name; 218 char *script_name;
219 char *server_name; 219 char *server_name;
220 char *server_port; 220 char *server_port;
221}; 221};
222 222
223struct cgit_context { 223struct cgit_context {
224 struct cgit_environment env; 224 struct cgit_environment env;
225 struct cgit_query qry; 225 struct cgit_query qry;
226 struct cgit_config cfg; 226 struct cgit_config cfg;
227 struct cgit_repo *repo; 227 struct cgit_repo *repo;
228 struct cgit_page page; 228 struct cgit_page page;
229}; 229};
230 230
231struct cgit_snapshot_format { 231struct cgit_snapshot_format {
232 const char *suffix; 232 const char *suffix;
233 const char *mimetype; 233 const char *mimetype;
234 write_archive_fn_t write_func; 234 write_archive_fn_t write_func;
235 int bit; 235 int bit;
236}; 236};
237 237
238extern const char *cgit_version; 238extern const char *cgit_version;
239 239
240extern struct cgit_repolist cgit_repolist; 240extern struct cgit_repolist cgit_repolist;
241extern struct cgit_context ctx; 241extern struct cgit_context ctx;
242extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 242extern const struct cgit_snapshot_format cgit_snapshot_formats[];
243 243
244extern struct cgit_repo *cgit_add_repo(const char *url); 244extern struct cgit_repo *cgit_add_repo(const char *url);
245extern struct cgit_repo *cgit_get_repoinfo(const char *url); 245extern struct cgit_repo *cgit_get_repoinfo(const char *url);
246extern void cgit_repo_config_cb(const char *name, const char *value); 246extern void cgit_repo_config_cb(const char *name, const char *value);
247 247
248extern int chk_zero(int result, char *msg); 248extern int chk_zero(int result, char *msg);
249extern int chk_positive(int result, char *msg); 249extern int chk_positive(int result, char *msg);
250extern int chk_non_negative(int result, char *msg); 250extern int chk_non_negative(int result, char *msg);
251 251
252extern char *trim_end(const char *str, char c); 252extern char *trim_end(const char *str, char c);
253extern char *strlpart(char *txt, int maxlen); 253extern char *strlpart(char *txt, int maxlen);
254extern char *strrpart(char *txt, int maxlen); 254extern char *strrpart(char *txt, int maxlen);
255 255
256extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 256extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
257extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 257extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
258 int flags, void *cb_data); 258 int flags, void *cb_data);
259 259
260extern void *cgit_free_commitinfo(struct commitinfo *info); 260extern void *cgit_free_commitinfo(struct commitinfo *info);
261 261
262extern int cgit_diff_files(const unsigned char *old_sha1, 262extern int cgit_diff_files(const unsigned char *old_sha1,
263 const unsigned char *new_sha1, 263 const unsigned char *new_sha1,
264 unsigned long *old_size, unsigned long *new_size, 264 unsigned long *old_size, unsigned long *new_size,
265 int *binary, linediff_fn fn); 265 int *binary, linediff_fn fn);
266 266
267extern void cgit_diff_tree(const unsigned char *old_sha1, 267extern void cgit_diff_tree(const unsigned char *old_sha1,
268 const unsigned char *new_sha1, 268 const unsigned char *new_sha1,
269 filepair_fn fn, const char *prefix); 269 filepair_fn fn, const char *prefix);
270 270
271extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 271extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
272 272
273extern char *fmt(const char *format,...); 273extern char *fmt(const char *format,...);
274 274
275extern struct commitinfo *cgit_parse_commit(struct commit *commit); 275extern struct commitinfo *cgit_parse_commit(struct commit *commit);
276extern struct taginfo *cgit_parse_tag(struct tag *tag); 276extern struct taginfo *cgit_parse_tag(struct tag *tag);
277extern void cgit_parse_url(const char *url); 277extern void cgit_parse_url(const char *url);
278 278
279extern const char *cgit_repobasename(const char *reponame); 279extern const char *cgit_repobasename(const char *reponame);
280 280
281extern int cgit_parse_snapshots_mask(const char *str); 281extern int cgit_parse_snapshots_mask(const char *str);
282 282
283extern int cgit_open_filter(struct cgit_filter *filter); 283extern int cgit_open_filter(struct cgit_filter *filter);
284extern int cgit_close_filter(struct cgit_filter *filter); 284extern int cgit_close_filter(struct cgit_filter *filter);
285 285
286extern int readfile(const char *path, char **buf, size_t *size);
286 287
287#endif /* CGIT_H */ 288#endif /* CGIT_H */
diff --git a/scan-tree.c b/scan-tree.c
index 47f3988..95dc65b 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -1,137 +1,125 @@
1#include "cgit.h" 1#include "cgit.h"
2#include "html.h" 2#include "html.h"
3 3
4#define MAX_PATH 4096 4#define MAX_PATH 4096
5 5
6/* return 1 if path contains a objects/ directory and a HEAD file */ 6/* return 1 if path contains a objects/ directory and a HEAD file */
7static int is_git_dir(const char *path) 7static int is_git_dir(const char *path)
8{ 8{
9 struct stat st; 9 struct stat st;
10 static char buf[MAX_PATH]; 10 static char buf[MAX_PATH];
11 11
12 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) { 12 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) {
13 fprintf(stderr, "Insanely long path: %s\n", path); 13 fprintf(stderr, "Insanely long path: %s\n", path);
14 return 0; 14 return 0;
15 } 15 }
16 if (stat(buf, &st)) { 16 if (stat(buf, &st)) {
17 if (errno != ENOENT) 17 if (errno != ENOENT)
18 fprintf(stderr, "Error checking path %s: %s (%d)\n", 18 fprintf(stderr, "Error checking path %s: %s (%d)\n",
19 path, strerror(errno), errno); 19 path, strerror(errno), errno);
20 return 0; 20 return 0;
21 } 21 }
22 if (!S_ISDIR(st.st_mode)) 22 if (!S_ISDIR(st.st_mode))
23 return 0; 23 return 0;
24 24
25 sprintf(buf, "%s/HEAD", path); 25 sprintf(buf, "%s/HEAD", path);
26 if (stat(buf, &st)) { 26 if (stat(buf, &st)) {
27 if (errno != ENOENT) 27 if (errno != ENOENT)
28 fprintf(stderr, "Error checking path %s: %s (%d)\n", 28 fprintf(stderr, "Error checking path %s: %s (%d)\n",
29 path, strerror(errno), errno); 29 path, strerror(errno), errno);
30 return 0; 30 return 0;
31 } 31 }
32 if (!S_ISREG(st.st_mode)) 32 if (!S_ISREG(st.st_mode))
33 return 0; 33 return 0;
34 34
35 return 1; 35 return 1;
36} 36}
37 37
38char *readfile(const char *path)
39{
40 FILE *f;
41 static char buf[MAX_PATH];
42
43 if (!(f = fopen(path, "r")))
44 return NULL;
45 buf[0] = 0;
46 fgets(buf, MAX_PATH, f);
47 fclose(f);
48 return buf;
49}
50
51static void add_repo(const char *base, const char *path) 38static void add_repo(const char *base, const char *path)
52{ 39{
53 struct cgit_repo *repo; 40 struct cgit_repo *repo;
54 struct stat st; 41 struct stat st;
55 struct passwd *pwd; 42 struct passwd *pwd;
56 char *p; 43 char *p;
44 size_t size;
57 45
58 if (stat(path, &st)) { 46 if (stat(path, &st)) {
59 fprintf(stderr, "Error accessing %s: %s (%d)\n", 47 fprintf(stderr, "Error accessing %s: %s (%d)\n",
60 path, strerror(errno), errno); 48 path, strerror(errno), errno);
61 return; 49 return;
62 } 50 }
63 if ((pwd = getpwuid(st.st_uid)) == NULL) { 51 if ((pwd = getpwuid(st.st_uid)) == NULL) {
64 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 52 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
65 path, strerror(errno), errno); 53 path, strerror(errno), errno);
66 return; 54 return;
67 } 55 }
68 if (base == path) 56 if (base == path)
69 p = fmt("%s", path); 57 p = fmt("%s", path);
70 else 58 else
71 p = fmt("%s", path + strlen(base) + 1); 59 p = fmt("%s", path + strlen(base) + 1);
72 60
73 if (!strcmp(p + strlen(p) - 5, "/.git")) 61 if (!strcmp(p + strlen(p) - 5, "/.git"))
74 p[strlen(p) - 5] = '\0'; 62 p[strlen(p) - 5] = '\0';
75 63
76 repo = cgit_add_repo(xstrdup(p)); 64 repo = cgit_add_repo(xstrdup(p));
77 repo->name = repo->url; 65 repo->name = repo->url;
78 repo->path = xstrdup(path); 66 repo->path = xstrdup(path);
79 repo->owner = (pwd ? xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name) : ""); 67 repo->owner = (pwd ? xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name) : "");
80 68
81 p = fmt("%s/description", path); 69 p = fmt("%s/description", path);
82 if (!stat(p, &st)) 70 if (!stat(p, &st))
83 repo->desc = xstrdup(readfile(p)); 71 readfile(p, &repo->desc, &size);
84 72
85 p = fmt("%s/README.html", path); 73 p = fmt("%s/README.html", path);
86 if (!stat(p, &st)) 74 if (!stat(p, &st))
87 repo->readme = "README.html"; 75 repo->readme = "README.html";
88} 76}
89 77
90static void scan_path(const char *base, const char *path) 78static void scan_path(const char *base, const char *path)
91{ 79{
92 DIR *dir; 80 DIR *dir;
93 struct dirent *ent; 81 struct dirent *ent;
94 char *buf; 82 char *buf;
95 struct stat st; 83 struct stat st;
96 84
97 if (is_git_dir(path)) { 85 if (is_git_dir(path)) {
98 add_repo(base, path); 86 add_repo(base, path);
99 return; 87 return;
100 } 88 }
101 dir = opendir(path); 89 dir = opendir(path);
102 if (!dir) { 90 if (!dir) {
103 fprintf(stderr, "Error opening directory %s: %s (%d)\n", 91 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
104 path, strerror(errno), errno); 92 path, strerror(errno), errno);
105 return; 93 return;
106 } 94 }
107 while((ent = readdir(dir)) != NULL) { 95 while((ent = readdir(dir)) != NULL) {
108 if (ent->d_name[0] == '.') { 96 if (ent->d_name[0] == '.') {
109 if (ent->d_name[1] == '\0') 97 if (ent->d_name[1] == '\0')
110 continue; 98 continue;
111 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 99 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
112 continue; 100 continue;
113 } 101 }
114 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 102 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
115 if (!buf) { 103 if (!buf) {
116 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 104 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
117 path, strerror(errno), errno); 105 path, strerror(errno), errno);
118 exit(1); 106 exit(1);
119 } 107 }
120 sprintf(buf, "%s/%s", path, ent->d_name); 108 sprintf(buf, "%s/%s", path, ent->d_name);
121 if (stat(buf, &st)) { 109 if (stat(buf, &st)) {
122 fprintf(stderr, "Error checking path %s: %s (%d)\n", 110 fprintf(stderr, "Error checking path %s: %s (%d)\n",
123 buf, strerror(errno), errno); 111 buf, strerror(errno), errno);
124 free(buf); 112 free(buf);
125 continue; 113 continue;
126 } 114 }
127 if (S_ISDIR(st.st_mode)) 115 if (S_ISDIR(st.st_mode))
128 scan_path(base, buf); 116 scan_path(base, buf);
129 free(buf); 117 free(buf);
130 } 118 }
131 closedir(dir); 119 closedir(dir);
132} 120}
133 121
134void scan_tree(const char *path) 122void scan_tree(const char *path)
135{ 123{
136 scan_path(path, path); 124 scan_path(path, path);
137} 125}
diff --git a/shared.c b/shared.c
index 911a55a..4cb9573 100644
--- a/shared.c
+++ b/shared.c
@@ -140,256 +140,277 @@ char *strrpart(char *txt, int maxlen)
140 return txt; 140 return txt;
141 141
142 if (strlen(txt) <= maxlen) 142 if (strlen(txt) <= maxlen)
143 return txt; 143 return txt;
144 result = xmalloc(maxlen + 1); 144 result = xmalloc(maxlen + 1);
145 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3); 145 memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
146 result[0] = result[1] = result[2] = '.'; 146 result[0] = result[1] = result[2] = '.';
147 return result; 147 return result;
148} 148}
149 149
150void cgit_add_ref(struct reflist *list, struct refinfo *ref) 150void cgit_add_ref(struct reflist *list, struct refinfo *ref)
151{ 151{
152 size_t size; 152 size_t size;
153 153
154 if (list->count >= list->alloc) { 154 if (list->count >= list->alloc) {
155 list->alloc += (list->alloc ? list->alloc : 4); 155 list->alloc += (list->alloc ? list->alloc : 4);
156 size = list->alloc * sizeof(struct refinfo *); 156 size = list->alloc * sizeof(struct refinfo *);
157 list->refs = xrealloc(list->refs, size); 157 list->refs = xrealloc(list->refs, size);
158 } 158 }
159 list->refs[list->count++] = ref; 159 list->refs[list->count++] = ref;
160} 160}
161 161
162struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1) 162struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
163{ 163{
164 struct refinfo *ref; 164 struct refinfo *ref;
165 165
166 ref = xmalloc(sizeof (struct refinfo)); 166 ref = xmalloc(sizeof (struct refinfo));
167 ref->refname = xstrdup(refname); 167 ref->refname = xstrdup(refname);
168 ref->object = parse_object(sha1); 168 ref->object = parse_object(sha1);
169 switch (ref->object->type) { 169 switch (ref->object->type) {
170 case OBJ_TAG: 170 case OBJ_TAG:
171 ref->tag = cgit_parse_tag((struct tag *)ref->object); 171 ref->tag = cgit_parse_tag((struct tag *)ref->object);
172 break; 172 break;
173 case OBJ_COMMIT: 173 case OBJ_COMMIT:
174 ref->commit = cgit_parse_commit((struct commit *)ref->object); 174 ref->commit = cgit_parse_commit((struct commit *)ref->object);
175 break; 175 break;
176 } 176 }
177 return ref; 177 return ref;
178} 178}
179 179
180int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags, 180int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
181 void *cb_data) 181 void *cb_data)
182{ 182{
183 struct reflist *list = (struct reflist *)cb_data; 183 struct reflist *list = (struct reflist *)cb_data;
184 struct refinfo *info = cgit_mk_refinfo(refname, sha1); 184 struct refinfo *info = cgit_mk_refinfo(refname, sha1);
185 185
186 if (info) 186 if (info)
187 cgit_add_ref(list, info); 187 cgit_add_ref(list, info);
188 return 0; 188 return 0;
189} 189}
190 190
191void cgit_diff_tree_cb(struct diff_queue_struct *q, 191void cgit_diff_tree_cb(struct diff_queue_struct *q,
192 struct diff_options *options, void *data) 192 struct diff_options *options, void *data)
193{ 193{
194 int i; 194 int i;
195 195
196 for (i = 0; i < q->nr; i++) { 196 for (i = 0; i < q->nr; i++) {
197 if (q->queue[i]->status == 'U') 197 if (q->queue[i]->status == 'U')
198 continue; 198 continue;
199 ((filepair_fn)data)(q->queue[i]); 199 ((filepair_fn)data)(q->queue[i]);
200 } 200 }
201} 201}
202 202
203static int load_mmfile(mmfile_t *file, const unsigned char *sha1) 203static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
204{ 204{
205 enum object_type type; 205 enum object_type type;
206 206
207 if (is_null_sha1(sha1)) { 207 if (is_null_sha1(sha1)) {
208 file->ptr = (char *)""; 208 file->ptr = (char *)"";
209 file->size = 0; 209 file->size = 0;
210 } else { 210 } else {
211 file->ptr = read_sha1_file(sha1, &type, 211 file->ptr = read_sha1_file(sha1, &type,
212 (unsigned long *)&file->size); 212 (unsigned long *)&file->size);
213 } 213 }
214 return 1; 214 return 1;
215} 215}
216 216
217/* 217/*
218 * Receive diff-buffers from xdiff and concatenate them as 218 * Receive diff-buffers from xdiff and concatenate them as
219 * needed across multiple callbacks. 219 * needed across multiple callbacks.
220 * 220 *
221 * This is basically a copy of xdiff-interface.c/xdiff_outf(), 221 * This is basically a copy of xdiff-interface.c/xdiff_outf(),
222 * ripped from git and modified to use globals instead of 222 * ripped from git and modified to use globals instead of
223 * a special callback-struct. 223 * a special callback-struct.
224 */ 224 */
225char *diffbuf = NULL; 225char *diffbuf = NULL;
226int buflen = 0; 226int buflen = 0;
227 227
228int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf) 228int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
229{ 229{
230 int i; 230 int i;
231 231
232 for (i = 0; i < nbuf; i++) { 232 for (i = 0; i < nbuf; i++) {
233 if (mb[i].ptr[mb[i].size-1] != '\n') { 233 if (mb[i].ptr[mb[i].size-1] != '\n') {
234 /* Incomplete line */ 234 /* Incomplete line */
235 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 235 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
236 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 236 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
237 buflen += mb[i].size; 237 buflen += mb[i].size;
238 continue; 238 continue;
239 } 239 }
240 240
241 /* we have a complete line */ 241 /* we have a complete line */
242 if (!diffbuf) { 242 if (!diffbuf) {
243 ((linediff_fn)priv)(mb[i].ptr, mb[i].size); 243 ((linediff_fn)priv)(mb[i].ptr, mb[i].size);
244 continue; 244 continue;
245 } 245 }
246 diffbuf = xrealloc(diffbuf, buflen + mb[i].size); 246 diffbuf = xrealloc(diffbuf, buflen + mb[i].size);
247 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size); 247 memcpy(diffbuf + buflen, mb[i].ptr, mb[i].size);
248 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size); 248 ((linediff_fn)priv)(diffbuf, buflen + mb[i].size);
249 free(diffbuf); 249 free(diffbuf);
250 diffbuf = NULL; 250 diffbuf = NULL;
251 buflen = 0; 251 buflen = 0;
252 } 252 }
253 if (diffbuf) { 253 if (diffbuf) {
254 ((linediff_fn)priv)(diffbuf, buflen); 254 ((linediff_fn)priv)(diffbuf, buflen);
255 free(diffbuf); 255 free(diffbuf);
256 diffbuf = NULL; 256 diffbuf = NULL;
257 buflen = 0; 257 buflen = 0;
258 } 258 }
259 return 0; 259 return 0;
260} 260}
261 261
262int cgit_diff_files(const unsigned char *old_sha1, 262int cgit_diff_files(const unsigned char *old_sha1,
263 const unsigned char *new_sha1, unsigned long *old_size, 263 const unsigned char *new_sha1, unsigned long *old_size,
264 unsigned long *new_size, int *binary, linediff_fn fn) 264 unsigned long *new_size, int *binary, linediff_fn fn)
265{ 265{
266 mmfile_t file1, file2; 266 mmfile_t file1, file2;
267 xpparam_t diff_params; 267 xpparam_t diff_params;
268 xdemitconf_t emit_params; 268 xdemitconf_t emit_params;
269 xdemitcb_t emit_cb; 269 xdemitcb_t emit_cb;
270 270
271 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) 271 if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
272 return 1; 272 return 1;
273 273
274 *old_size = file1.size; 274 *old_size = file1.size;
275 *new_size = file2.size; 275 *new_size = file2.size;
276 276
277 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || 277 if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) ||
278 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { 278 (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) {
279 *binary = 1; 279 *binary = 1;
280 return 0; 280 return 0;
281 } 281 }
282 282
283 memset(&diff_params, 0, sizeof(diff_params)); 283 memset(&diff_params, 0, sizeof(diff_params));
284 memset(&emit_params, 0, sizeof(emit_params)); 284 memset(&emit_params, 0, sizeof(emit_params));
285 memset(&emit_cb, 0, sizeof(emit_cb)); 285 memset(&emit_cb, 0, sizeof(emit_cb));
286 diff_params.flags = XDF_NEED_MINIMAL; 286 diff_params.flags = XDF_NEED_MINIMAL;
287 emit_params.ctxlen = 3; 287 emit_params.ctxlen = 3;
288 emit_params.flags = XDL_EMIT_FUNCNAMES; 288 emit_params.flags = XDL_EMIT_FUNCNAMES;
289 emit_cb.outf = filediff_cb; 289 emit_cb.outf = filediff_cb;
290 emit_cb.priv = fn; 290 emit_cb.priv = fn;
291 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); 291 xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
292 return 0; 292 return 0;
293} 293}
294 294
295void cgit_diff_tree(const unsigned char *old_sha1, 295void cgit_diff_tree(const unsigned char *old_sha1,
296 const unsigned char *new_sha1, 296 const unsigned char *new_sha1,
297 filepair_fn fn, const char *prefix) 297 filepair_fn fn, const char *prefix)
298{ 298{
299 struct diff_options opt; 299 struct diff_options opt;
300 int ret; 300 int ret;
301 int prefixlen; 301 int prefixlen;
302 302
303 diff_setup(&opt); 303 diff_setup(&opt);
304 opt.output_format = DIFF_FORMAT_CALLBACK; 304 opt.output_format = DIFF_FORMAT_CALLBACK;
305 opt.detect_rename = 1; 305 opt.detect_rename = 1;
306 opt.rename_limit = ctx.cfg.renamelimit; 306 opt.rename_limit = ctx.cfg.renamelimit;
307 DIFF_OPT_SET(&opt, RECURSIVE); 307 DIFF_OPT_SET(&opt, RECURSIVE);
308 opt.format_callback = cgit_diff_tree_cb; 308 opt.format_callback = cgit_diff_tree_cb;
309 opt.format_callback_data = fn; 309 opt.format_callback_data = fn;
310 if (prefix) { 310 if (prefix) {
311 opt.nr_paths = 1; 311 opt.nr_paths = 1;
312 opt.paths = &prefix; 312 opt.paths = &prefix;
313 prefixlen = strlen(prefix); 313 prefixlen = strlen(prefix);
314 opt.pathlens = &prefixlen; 314 opt.pathlens = &prefixlen;
315 } 315 }
316 diff_setup_done(&opt); 316 diff_setup_done(&opt);
317 317
318 if (old_sha1 && !is_null_sha1(old_sha1)) 318 if (old_sha1 && !is_null_sha1(old_sha1))
319 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt); 319 ret = diff_tree_sha1(old_sha1, new_sha1, "", &opt);
320 else 320 else
321 ret = diff_root_tree_sha1(new_sha1, "", &opt); 321 ret = diff_root_tree_sha1(new_sha1, "", &opt);
322 diffcore_std(&opt); 322 diffcore_std(&opt);
323 diff_flush(&opt); 323 diff_flush(&opt);
324} 324}
325 325
326void cgit_diff_commit(struct commit *commit, filepair_fn fn) 326void cgit_diff_commit(struct commit *commit, filepair_fn fn)
327{ 327{
328 unsigned char *old_sha1 = NULL; 328 unsigned char *old_sha1 = NULL;
329 329
330 if (commit->parents) 330 if (commit->parents)
331 old_sha1 = commit->parents->item->object.sha1; 331 old_sha1 = commit->parents->item->object.sha1;
332 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL); 332 cgit_diff_tree(old_sha1, commit->object.sha1, fn, NULL);
333} 333}
334 334
335int cgit_parse_snapshots_mask(const char *str) 335int cgit_parse_snapshots_mask(const char *str)
336{ 336{
337 const struct cgit_snapshot_format *f; 337 const struct cgit_snapshot_format *f;
338 static const char *delim = " \t,:/|;"; 338 static const char *delim = " \t,:/|;";
339 int tl, sl, rv = 0; 339 int tl, sl, rv = 0;
340 340
341 /* favor legacy setting */ 341 /* favor legacy setting */
342 if(atoi(str)) 342 if(atoi(str))
343 return 1; 343 return 1;
344 for(;;) { 344 for(;;) {
345 str += strspn(str,delim); 345 str += strspn(str,delim);
346 tl = strcspn(str,delim); 346 tl = strcspn(str,delim);
347 if (!tl) 347 if (!tl)
348 break; 348 break;
349 for (f = cgit_snapshot_formats; f->suffix; f++) { 349 for (f = cgit_snapshot_formats; f->suffix; f++) {
350 sl = strlen(f->suffix); 350 sl = strlen(f->suffix);
351 if((tl == sl && !strncmp(f->suffix, str, tl)) || 351 if((tl == sl && !strncmp(f->suffix, str, tl)) ||
352 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) { 352 (tl == sl-1 && !strncmp(f->suffix+1, str, tl-1))) {
353 rv |= f->bit; 353 rv |= f->bit;
354 break; 354 break;
355 } 355 }
356 } 356 }
357 str += tl; 357 str += tl;
358 } 358 }
359 return rv; 359 return rv;
360} 360}
361 361
362int cgit_open_filter(struct cgit_filter *filter) 362int cgit_open_filter(struct cgit_filter *filter)
363{ 363{
364 364
365 filter->old_stdout = chk_positive(dup(STDOUT_FILENO), 365 filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
366 "Unable to duplicate STDOUT"); 366 "Unable to duplicate STDOUT");
367 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess"); 367 chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
368 filter->pid = chk_non_negative(fork(), "Unable to create subprocess"); 368 filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
369 if (filter->pid == 0) { 369 if (filter->pid == 0) {
370 close(filter->pipe_fh[1]); 370 close(filter->pipe_fh[1]);
371 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO), 371 chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
372 "Unable to use pipe as STDIN"); 372 "Unable to use pipe as STDIN");
373 execvp(filter->cmd, filter->argv); 373 execvp(filter->cmd, filter->argv);
374 die("Unable to exec subprocess %s: %s (%d)", filter->cmd, 374 die("Unable to exec subprocess %s: %s (%d)", filter->cmd,
375 strerror(errno), errno); 375 strerror(errno), errno);
376 } 376 }
377 close(filter->pipe_fh[0]); 377 close(filter->pipe_fh[0]);
378 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO), 378 chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
379 "Unable to use pipe as STDOUT"); 379 "Unable to use pipe as STDOUT");
380 close(filter->pipe_fh[1]); 380 close(filter->pipe_fh[1]);
381 return 0; 381 return 0;
382} 382}
383 383
384int cgit_close_filter(struct cgit_filter *filter) 384int cgit_close_filter(struct cgit_filter *filter)
385{ 385{
386 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), 386 chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
387 "Unable to restore STDOUT"); 387 "Unable to restore STDOUT");
388 close(filter->old_stdout); 388 close(filter->old_stdout);
389 if (filter->pid < 0) 389 if (filter->pid < 0)
390 return 0; 390 return 0;
391 waitpid(filter->pid, &filter->exitstatus, 0); 391 waitpid(filter->pid, &filter->exitstatus, 0);
392 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus)) 392 if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus))
393 return 0; 393 return 0;
394 die("Subprocess %s exited abnormally", filter->cmd); 394 die("Subprocess %s exited abnormally", filter->cmd);
395} 395}
396
397/* Read the content of the specified file into a newly allocated buffer,
398 * zeroterminate the buffer and return 0 on success, errno otherwise.
399 */
400int readfile(const char *path, char **buf, size_t *size)
401{
402 int fd;
403 struct stat st;
404
405 fd = open(path, O_RDONLY);
406 if (fd == -1)
407 return errno;
408 if (fstat(fd, &st))
409 return errno;
410 if (!S_ISREG(st.st_mode))
411 return EISDIR;
412 *buf = xmalloc(st.st_size + 1);
413 *size = read_in_full(fd, *buf, st.st_size);
414 (*buf)[*size] = '\0';
415 return (*size == st.st_size ? 0 : errno);
416}
diff --git a/ui-repolist.c b/ui-repolist.c
index 6d2f93f..7c7aa9b 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -1,284 +1,285 @@
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/* This is needed for strcasestr to be defined by <string.h> */ 9/* This is needed for strcasestr to be defined by <string.h> */
10#define _GNU_SOURCE 1 10#define _GNU_SOURCE 1
11#include <string.h> 11#include <string.h>
12 12
13#include <time.h> 13#include <time.h>
14 14
15#include "cgit.h" 15#include "cgit.h"
16#include "html.h" 16#include "html.h"
17#include "ui-shared.h" 17#include "ui-shared.h"
18 18
19time_t read_agefile(char *path) 19time_t read_agefile(char *path)
20{ 20{
21 FILE *f; 21 time_t result;
22 static char buf[64], buf2[64]; 22 size_t size;
23 char *buf;
24 static char buf2[64];
23 25
24 if (!(f = fopen(path, "r"))) 26 if (readfile(path, &buf, &size))
25 return -1; 27 return -1;
26 buf[0] = 0; 28
27 if (fgets(buf, sizeof(buf), f) == NULL)
28 return -1;
29 fclose(f);
30 if (parse_date(buf, buf2, sizeof(buf2))) 29 if (parse_date(buf, buf2, sizeof(buf2)))
31 return strtoul(buf2, NULL, 10); 30 result = strtoul(buf2, NULL, 10);
32 else 31 else
33 return 0; 32 result = 0;
33 free(buf);
34 return result;
34} 35}
35 36
36static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) 37static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime)
37{ 38{
38 char *path; 39 char *path;
39 struct stat s; 40 struct stat s;
40 struct cgit_repo *r = (struct cgit_repo *)repo; 41 struct cgit_repo *r = (struct cgit_repo *)repo;
41 42
42 if (repo->mtime != -1) { 43 if (repo->mtime != -1) {
43 *mtime = repo->mtime; 44 *mtime = repo->mtime;
44 return 1; 45 return 1;
45 } 46 }
46 path = fmt("%s/%s", repo->path, ctx.cfg.agefile); 47 path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
47 if (stat(path, &s) == 0) { 48 if (stat(path, &s) == 0) {
48 *mtime = read_agefile(path); 49 *mtime = read_agefile(path);
49 r->mtime = *mtime; 50 r->mtime = *mtime;
50 return 1; 51 return 1;
51 } 52 }
52 53
53 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch); 54 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
54 if (stat(path, &s) == 0) 55 if (stat(path, &s) == 0)
55 *mtime = s.st_mtime; 56 *mtime = s.st_mtime;
56 else 57 else
57 *mtime = 0; 58 *mtime = 0;
58 59
59 r->mtime = *mtime; 60 r->mtime = *mtime;
60 return (r->mtime != 0); 61 return (r->mtime != 0);
61} 62}
62 63
63static void print_modtime(struct cgit_repo *repo) 64static void print_modtime(struct cgit_repo *repo)
64{ 65{
65 time_t t; 66 time_t t;
66 if (get_repo_modtime(repo, &t)) 67 if (get_repo_modtime(repo, &t))
67 cgit_print_age(t, -1, NULL); 68 cgit_print_age(t, -1, NULL);
68} 69}
69 70
70int is_match(struct cgit_repo *repo) 71int is_match(struct cgit_repo *repo)
71{ 72{
72 if (!ctx.qry.search) 73 if (!ctx.qry.search)
73 return 1; 74 return 1;
74 if (repo->url && strcasestr(repo->url, ctx.qry.search)) 75 if (repo->url && strcasestr(repo->url, ctx.qry.search))
75 return 1; 76 return 1;
76 if (repo->name && strcasestr(repo->name, ctx.qry.search)) 77 if (repo->name && strcasestr(repo->name, ctx.qry.search))
77 return 1; 78 return 1;
78 if (repo->desc && strcasestr(repo->desc, ctx.qry.search)) 79 if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
79 return 1; 80 return 1;
80 if (repo->owner && strcasestr(repo->owner, ctx.qry.search)) 81 if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
81 return 1; 82 return 1;
82 return 0; 83 return 0;
83} 84}
84 85
85int is_in_url(struct cgit_repo *repo) 86int is_in_url(struct cgit_repo *repo)
86{ 87{
87 if (!ctx.qry.url) 88 if (!ctx.qry.url)
88 return 1; 89 return 1;
89 if (repo->url && !prefixcmp(repo->url, ctx.qry.url)) 90 if (repo->url && !prefixcmp(repo->url, ctx.qry.url))
90 return 1; 91 return 1;
91 return 0; 92 return 0;
92} 93}
93 94
94void print_sort_header(const char *title, const char *sort) 95void print_sort_header(const char *title, const char *sort)
95{ 96{
96 htmlf("<th class='left'><a href='./?s=%s", sort); 97 htmlf("<th class='left'><a href='./?s=%s", sort);
97 if (ctx.qry.search) { 98 if (ctx.qry.search) {
98 html("&q="); 99 html("&q=");
99 html_url_arg(ctx.qry.search); 100 html_url_arg(ctx.qry.search);
100 } 101 }
101 htmlf("'>%s</a></th>", title); 102 htmlf("'>%s</a></th>", title);
102} 103}
103 104
104void print_header(int columns) 105void print_header(int columns)
105{ 106{
106 html("<tr class='nohover'>"); 107 html("<tr class='nohover'>");
107 print_sort_header("Name", "name"); 108 print_sort_header("Name", "name");
108 print_sort_header("Description", "desc"); 109 print_sort_header("Description", "desc");
109 print_sort_header("Owner", "owner"); 110 print_sort_header("Owner", "owner");
110 print_sort_header("Idle", "idle"); 111 print_sort_header("Idle", "idle");
111 if (ctx.cfg.enable_index_links) 112 if (ctx.cfg.enable_index_links)
112 html("<th class='left'>Links</th>"); 113 html("<th class='left'>Links</th>");
113 html("</tr>\n"); 114 html("</tr>\n");
114} 115}
115 116
116 117
117void print_pager(int items, int pagelen, char *search) 118void print_pager(int items, int pagelen, char *search)
118{ 119{
119 int i; 120 int i;
120 html("<div class='pager'>"); 121 html("<div class='pager'>");
121 for(i = 0; i * pagelen < items; i++) 122 for(i = 0; i * pagelen < items; i++)
122 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL, 123 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL,
123 search, i * pagelen); 124 search, i * pagelen);
124 html("</div>"); 125 html("</div>");
125} 126}
126 127
127static int cmp(const char *s1, const char *s2) 128static int cmp(const char *s1, const char *s2)
128{ 129{
129 if (s1 && s2) 130 if (s1 && s2)
130 return strcmp(s1, s2); 131 return strcmp(s1, s2);
131 if (s1 && !s2) 132 if (s1 && !s2)
132 return -1; 133 return -1;
133 if (s2 && !s1) 134 if (s2 && !s1)
134 return 1; 135 return 1;
135 return 0; 136 return 0;
136} 137}
137 138
138static int sort_name(const void *a, const void *b) 139static int sort_name(const void *a, const void *b)
139{ 140{
140 const struct cgit_repo *r1 = a; 141 const struct cgit_repo *r1 = a;
141 const struct cgit_repo *r2 = b; 142 const struct cgit_repo *r2 = b;
142 143
143 return cmp(r1->name, r2->name); 144 return cmp(r1->name, r2->name);
144} 145}
145 146
146static int sort_desc(const void *a, const void *b) 147static int sort_desc(const void *a, const void *b)
147{ 148{
148 const struct cgit_repo *r1 = a; 149 const struct cgit_repo *r1 = a;
149 const struct cgit_repo *r2 = b; 150 const struct cgit_repo *r2 = b;
150 151
151 return cmp(r1->desc, r2->desc); 152 return cmp(r1->desc, r2->desc);
152} 153}
153 154
154static int sort_owner(const void *a, const void *b) 155static int sort_owner(const void *a, const void *b)
155{ 156{
156 const struct cgit_repo *r1 = a; 157 const struct cgit_repo *r1 = a;
157 const struct cgit_repo *r2 = b; 158 const struct cgit_repo *r2 = b;
158 159
159 return cmp(r1->owner, r2->owner); 160 return cmp(r1->owner, r2->owner);
160} 161}
161 162
162static int sort_idle(const void *a, const void *b) 163static int sort_idle(const void *a, const void *b)
163{ 164{
164 const struct cgit_repo *r1 = a; 165 const struct cgit_repo *r1 = a;
165 const struct cgit_repo *r2 = b; 166 const struct cgit_repo *r2 = b;
166 time_t t1, t2; 167 time_t t1, t2;
167 168
168 t1 = t2 = 0; 169 t1 = t2 = 0;
169 get_repo_modtime(r1, &t1); 170 get_repo_modtime(r1, &t1);
170 get_repo_modtime(r2, &t2); 171 get_repo_modtime(r2, &t2);
171 return t2 - t1; 172 return t2 - t1;
172} 173}
173 174
174struct sortcolumn { 175struct sortcolumn {
175 const char *name; 176 const char *name;
176 int (*fn)(const void *a, const void *b); 177 int (*fn)(const void *a, const void *b);
177}; 178};
178 179
179struct sortcolumn sortcolumn[] = { 180struct sortcolumn sortcolumn[] = {
180 {"name", sort_name}, 181 {"name", sort_name},
181 {"desc", sort_desc}, 182 {"desc", sort_desc},
182 {"owner", sort_owner}, 183 {"owner", sort_owner},
183 {"idle", sort_idle}, 184 {"idle", sort_idle},
184 {NULL, NULL} 185 {NULL, NULL}
185}; 186};
186 187
187int sort_repolist(char *field) 188int sort_repolist(char *field)
188{ 189{
189 struct sortcolumn *column; 190 struct sortcolumn *column;
190 191
191 for (column = &sortcolumn[0]; column->name; column++) { 192 for (column = &sortcolumn[0]; column->name; column++) {
192 if (strcmp(field, column->name)) 193 if (strcmp(field, column->name))
193 continue; 194 continue;
194 qsort(cgit_repolist.repos, cgit_repolist.count, 195 qsort(cgit_repolist.repos, cgit_repolist.count,
195 sizeof(struct cgit_repo), column->fn); 196 sizeof(struct cgit_repo), column->fn);
196 return 1; 197 return 1;
197 } 198 }
198 return 0; 199 return 0;
199} 200}
200 201
201 202
202void cgit_print_repolist() 203void cgit_print_repolist()
203{ 204{
204 int i, columns = 4, hits = 0, header = 0; 205 int i, columns = 4, hits = 0, header = 0;
205 char *last_group = NULL; 206 char *last_group = NULL;
206 int sorted = 0; 207 int sorted = 0;
207 208
208 if (ctx.cfg.enable_index_links) 209 if (ctx.cfg.enable_index_links)
209 columns++; 210 columns++;
210 211
211 ctx.page.title = ctx.cfg.root_title; 212 ctx.page.title = ctx.cfg.root_title;
212 cgit_print_http_headers(&ctx); 213 cgit_print_http_headers(&ctx);
213 cgit_print_docstart(&ctx); 214 cgit_print_docstart(&ctx);
214 cgit_print_pageheader(&ctx); 215 cgit_print_pageheader(&ctx);
215 216
216 if (ctx.cfg.index_header) 217 if (ctx.cfg.index_header)
217 html_include(ctx.cfg.index_header); 218 html_include(ctx.cfg.index_header);
218 219
219 if(ctx.qry.sort) 220 if(ctx.qry.sort)
220 sorted = sort_repolist(ctx.qry.sort); 221 sorted = sort_repolist(ctx.qry.sort);
221 222
222 html("<table summary='repository list' class='list nowrap'>"); 223 html("<table summary='repository list' class='list nowrap'>");
223 for (i=0; i<cgit_repolist.count; i++) { 224 for (i=0; i<cgit_repolist.count; i++) {
224 ctx.repo = &cgit_repolist.repos[i]; 225 ctx.repo = &cgit_repolist.repos[i];
225 if (!(is_match(ctx.repo) && is_in_url(ctx.repo))) 226 if (!(is_match(ctx.repo) && is_in_url(ctx.repo)))
226 continue; 227 continue;
227 hits++; 228 hits++;
228 if (hits <= ctx.qry.ofs) 229 if (hits <= ctx.qry.ofs)
229 continue; 230 continue;
230 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count) 231 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
231 continue; 232 continue;
232 if (!header++) 233 if (!header++)
233 print_header(columns); 234 print_header(columns);
234 if (!sorted && 235 if (!sorted &&
235 ((last_group == NULL && ctx.repo->group != NULL) || 236 ((last_group == NULL && ctx.repo->group != NULL) ||
236 (last_group != NULL && ctx.repo->group == NULL) || 237 (last_group != NULL && ctx.repo->group == NULL) ||
237 (last_group != NULL && ctx.repo->group != NULL && 238 (last_group != NULL && ctx.repo->group != NULL &&
238 strcmp(ctx.repo->group, last_group)))) { 239 strcmp(ctx.repo->group, last_group)))) {
239 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", 240 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
240 columns); 241 columns);
241 html_txt(ctx.repo->group); 242 html_txt(ctx.repo->group);
242 html("</td></tr>"); 243 html("</td></tr>");
243 last_group = ctx.repo->group; 244 last_group = ctx.repo->group;
244 } 245 }
245 htmlf("<tr><td class='%s'>", 246 htmlf("<tr><td class='%s'>",
246 !sorted && ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); 247 !sorted && ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
247 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); 248 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
248 html("</td><td>"); 249 html("</td><td>");
249 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); 250 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
250 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); 251 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
251 html_link_close(); 252 html_link_close();
252 html("</td><td>"); 253 html("</td><td>");
253 html_txt(ctx.repo->owner); 254 html_txt(ctx.repo->owner);
254 html("</td><td>"); 255 html("</td><td>");
255 print_modtime(ctx.repo); 256 print_modtime(ctx.repo);
256 html("</td>"); 257 html("</td>");
257 if (ctx.cfg.enable_index_links) { 258 if (ctx.cfg.enable_index_links) {
258 html("<td>"); 259 html("<td>");
259 cgit_summary_link("summary", NULL, "button", NULL); 260 cgit_summary_link("summary", NULL, "button", NULL);
260 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 261 cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
261 0, NULL, NULL, ctx.qry.showmsg); 262 0, NULL, NULL, ctx.qry.showmsg);
262 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); 263 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
263 html("</td>"); 264 html("</td>");
264 } 265 }
265 html("</tr>\n"); 266 html("</tr>\n");
266 } 267 }
267 html("</table>"); 268 html("</table>");
268 if (!hits) 269 if (!hits)
269 cgit_print_error("No repositories found"); 270 cgit_print_error("No repositories found");
270 else if (hits > ctx.cfg.max_repo_count) 271 else if (hits > ctx.cfg.max_repo_count)
271 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search); 272 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search);
272 cgit_print_docend(); 273 cgit_print_docend();
273} 274}
274 275
275void cgit_print_site_readme() 276void cgit_print_site_readme()
276{ 277{
277 if (!ctx.cfg.root_readme) 278 if (!ctx.cfg.root_readme)
278 return; 279 return;
279 if (ctx.cfg.about_filter) 280 if (ctx.cfg.about_filter)
280 cgit_open_filter(ctx.cfg.about_filter); 281 cgit_open_filter(ctx.cfg.about_filter);
281 html_include(ctx.cfg.root_readme); 282 html_include(ctx.cfg.root_readme);
282 if (ctx.cfg.about_filter) 283 if (ctx.cfg.about_filter)
283 cgit_close_filter(ctx.cfg.about_filter); 284 cgit_close_filter(ctx.cfg.about_filter);
284} 285}