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