all repos — cgit @ afdff8dc1327ed14c94cf447272191bd28009ed1

a hyperfast web frontend for git written in c

ui-repolist.c (view raw)

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