summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.h1
-rw-r--r--ui-repolist.c39
3 files changed, 39 insertions, 3 deletions
diff --git a/cgit.c b/cgit.c
index c82587b..e09c86e 100644
--- a/cgit.c
+++ b/cgit.c
@@ -61,192 +61,194 @@ void config_cb(const char *name, const char *value)
61 else if (!strcmp(name, "cache-root-ttl")) 61 else if (!strcmp(name, "cache-root-ttl"))
62 ctx.cfg.cache_root_ttl = atoi(value); 62 ctx.cfg.cache_root_ttl = atoi(value);
63 else if (!strcmp(name, "cache-repo-ttl")) 63 else if (!strcmp(name, "cache-repo-ttl"))
64 ctx.cfg.cache_repo_ttl = atoi(value); 64 ctx.cfg.cache_repo_ttl = atoi(value);
65 else if (!strcmp(name, "cache-static-ttl")) 65 else if (!strcmp(name, "cache-static-ttl"))
66 ctx.cfg.cache_static_ttl = atoi(value); 66 ctx.cfg.cache_static_ttl = atoi(value);
67 else if (!strcmp(name, "cache-dynamic-ttl")) 67 else if (!strcmp(name, "cache-dynamic-ttl"))
68 ctx.cfg.cache_dynamic_ttl = atoi(value); 68 ctx.cfg.cache_dynamic_ttl = atoi(value);
69 else if (!strcmp(name, "max-message-length")) 69 else if (!strcmp(name, "max-message-length"))
70 ctx.cfg.max_msg_len = atoi(value); 70 ctx.cfg.max_msg_len = atoi(value);
71 else if (!strcmp(name, "max-repodesc-length")) 71 else if (!strcmp(name, "max-repodesc-length"))
72 ctx.cfg.max_repodesc_len = atoi(value); 72 ctx.cfg.max_repodesc_len = atoi(value);
73 else if (!strcmp(name, "max-repo-count")) 73 else if (!strcmp(name, "max-repo-count"))
74 ctx.cfg.max_repo_count = atoi(value); 74 ctx.cfg.max_repo_count = atoi(value);
75 else if (!strcmp(name, "max-commit-count")) 75 else if (!strcmp(name, "max-commit-count"))
76 ctx.cfg.max_commit_count = atoi(value); 76 ctx.cfg.max_commit_count = atoi(value);
77 else if (!strcmp(name, "summary-log")) 77 else if (!strcmp(name, "summary-log"))
78 ctx.cfg.summary_log = atoi(value); 78 ctx.cfg.summary_log = atoi(value);
79 else if (!strcmp(name, "summary-branches")) 79 else if (!strcmp(name, "summary-branches"))
80 ctx.cfg.summary_branches = atoi(value); 80 ctx.cfg.summary_branches = atoi(value);
81 else if (!strcmp(name, "summary-tags")) 81 else if (!strcmp(name, "summary-tags"))
82 ctx.cfg.summary_tags = atoi(value); 82 ctx.cfg.summary_tags = atoi(value);
83 else if (!strcmp(name, "agefile")) 83 else if (!strcmp(name, "agefile"))
84 ctx.cfg.agefile = xstrdup(value); 84 ctx.cfg.agefile = xstrdup(value);
85 else if (!strcmp(name, "renamelimit")) 85 else if (!strcmp(name, "renamelimit"))
86 ctx.cfg.renamelimit = atoi(value); 86 ctx.cfg.renamelimit = atoi(value);
87 else if (!strcmp(name, "robots")) 87 else if (!strcmp(name, "robots"))
88 ctx.cfg.robots = xstrdup(value); 88 ctx.cfg.robots = xstrdup(value);
89 else if (!strcmp(name, "clone-prefix")) 89 else if (!strcmp(name, "clone-prefix"))
90 ctx.cfg.clone_prefix = xstrdup(value); 90 ctx.cfg.clone_prefix = xstrdup(value);
91 else if (!strcmp(name, "local-time")) 91 else if (!strcmp(name, "local-time"))
92 ctx.cfg.local_time = atoi(value); 92 ctx.cfg.local_time = atoi(value);
93 else if (!strcmp(name, "repo.group")) 93 else if (!strcmp(name, "repo.group"))
94 ctx.cfg.repo_group = xstrdup(value); 94 ctx.cfg.repo_group = xstrdup(value);
95 else if (!strcmp(name, "repo.url")) 95 else if (!strcmp(name, "repo.url"))
96 ctx.repo = cgit_add_repo(value); 96 ctx.repo = cgit_add_repo(value);
97 else if (!strcmp(name, "repo.name")) 97 else if (!strcmp(name, "repo.name"))
98 ctx.repo->name = xstrdup(value); 98 ctx.repo->name = xstrdup(value);
99 else if (ctx.repo && !strcmp(name, "repo.path")) 99 else if (ctx.repo && !strcmp(name, "repo.path"))
100 ctx.repo->path = trim_end(value, '/'); 100 ctx.repo->path = trim_end(value, '/');
101 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 101 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
102 ctx.repo->clone_url = xstrdup(value); 102 ctx.repo->clone_url = xstrdup(value);
103 else if (ctx.repo && !strcmp(name, "repo.desc")) 103 else if (ctx.repo && !strcmp(name, "repo.desc"))
104 ctx.repo->desc = xstrdup(value); 104 ctx.repo->desc = xstrdup(value);
105 else if (ctx.repo && !strcmp(name, "repo.owner")) 105 else if (ctx.repo && !strcmp(name, "repo.owner"))
106 ctx.repo->owner = xstrdup(value); 106 ctx.repo->owner = xstrdup(value);
107 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 107 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
108 ctx.repo->defbranch = xstrdup(value); 108 ctx.repo->defbranch = xstrdup(value);
109 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 109 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
110 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 110 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
111 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 111 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
112 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 112 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
113 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 113 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
114 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 114 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
115 else if (ctx.repo && !strcmp(name, "repo.module-link")) 115 else if (ctx.repo && !strcmp(name, "repo.module-link"))
116 ctx.repo->module_link= xstrdup(value); 116 ctx.repo->module_link= xstrdup(value);
117 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 117 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
118 if (*value == '/') 118 if (*value == '/')
119 ctx.repo->readme = xstrdup(value); 119 ctx.repo->readme = xstrdup(value);
120 else 120 else
121 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 121 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
122 } else if (!strcmp(name, "include")) 122 } else if (!strcmp(name, "include"))
123 parse_configfile(value, config_cb); 123 parse_configfile(value, config_cb);
124} 124}
125 125
126static void querystring_cb(const char *name, const char *value) 126static void querystring_cb(const char *name, const char *value)
127{ 127{
128 if (!strcmp(name,"r")) { 128 if (!strcmp(name,"r")) {
129 ctx.qry.repo = xstrdup(value); 129 ctx.qry.repo = xstrdup(value);
130 ctx.repo = cgit_get_repoinfo(value); 130 ctx.repo = cgit_get_repoinfo(value);
131 } else if (!strcmp(name, "p")) { 131 } else if (!strcmp(name, "p")) {
132 ctx.qry.page = xstrdup(value); 132 ctx.qry.page = xstrdup(value);
133 } else if (!strcmp(name, "url")) { 133 } else if (!strcmp(name, "url")) {
134 ctx.qry.url = xstrdup(value); 134 ctx.qry.url = xstrdup(value);
135 cgit_parse_url(value); 135 cgit_parse_url(value);
136 } else if (!strcmp(name, "qt")) { 136 } else if (!strcmp(name, "qt")) {
137 ctx.qry.grep = xstrdup(value); 137 ctx.qry.grep = xstrdup(value);
138 } else if (!strcmp(name, "q")) { 138 } else if (!strcmp(name, "q")) {
139 ctx.qry.search = xstrdup(value); 139 ctx.qry.search = xstrdup(value);
140 } else if (!strcmp(name, "h")) { 140 } else if (!strcmp(name, "h")) {
141 ctx.qry.head = xstrdup(value); 141 ctx.qry.head = xstrdup(value);
142 ctx.qry.has_symref = 1; 142 ctx.qry.has_symref = 1;
143 } else if (!strcmp(name, "id")) { 143 } else if (!strcmp(name, "id")) {
144 ctx.qry.sha1 = xstrdup(value); 144 ctx.qry.sha1 = xstrdup(value);
145 ctx.qry.has_sha1 = 1; 145 ctx.qry.has_sha1 = 1;
146 } else if (!strcmp(name, "id2")) { 146 } else if (!strcmp(name, "id2")) {
147 ctx.qry.sha2 = xstrdup(value); 147 ctx.qry.sha2 = xstrdup(value);
148 ctx.qry.has_sha1 = 1; 148 ctx.qry.has_sha1 = 1;
149 } else if (!strcmp(name, "ofs")) { 149 } else if (!strcmp(name, "ofs")) {
150 ctx.qry.ofs = atoi(value); 150 ctx.qry.ofs = atoi(value);
151 } else if (!strcmp(name, "path")) { 151 } else if (!strcmp(name, "path")) {
152 ctx.qry.path = trim_end(value, '/'); 152 ctx.qry.path = trim_end(value, '/');
153 } else if (!strcmp(name, "name")) { 153 } else if (!strcmp(name, "name")) {
154 ctx.qry.name = xstrdup(value); 154 ctx.qry.name = xstrdup(value);
155 } else if (!strcmp(name, "mimetype")) { 155 } else if (!strcmp(name, "mimetype")) {
156 ctx.qry.mimetype = xstrdup(value); 156 ctx.qry.mimetype = xstrdup(value);
157 } else if (!strcmp(name, "s")){
158 ctx.qry.sort = xstrdup(value);
157 } 159 }
158} 160}
159 161
160static void prepare_context(struct cgit_context *ctx) 162static void prepare_context(struct cgit_context *ctx)
161{ 163{
162 memset(ctx, 0, sizeof(ctx)); 164 memset(ctx, 0, sizeof(ctx));
163 ctx->cfg.agefile = "info/web/last-modified"; 165 ctx->cfg.agefile = "info/web/last-modified";
164 ctx->cfg.nocache = 0; 166 ctx->cfg.nocache = 0;
165 ctx->cfg.cache_size = 0; 167 ctx->cfg.cache_size = 0;
166 ctx->cfg.cache_dynamic_ttl = 5; 168 ctx->cfg.cache_dynamic_ttl = 5;
167 ctx->cfg.cache_max_create_time = 5; 169 ctx->cfg.cache_max_create_time = 5;
168 ctx->cfg.cache_repo_ttl = 5; 170 ctx->cfg.cache_repo_ttl = 5;
169 ctx->cfg.cache_root = CGIT_CACHE_ROOT; 171 ctx->cfg.cache_root = CGIT_CACHE_ROOT;
170 ctx->cfg.cache_root_ttl = 5; 172 ctx->cfg.cache_root_ttl = 5;
171 ctx->cfg.cache_static_ttl = -1; 173 ctx->cfg.cache_static_ttl = -1;
172 ctx->cfg.css = "/cgit.css"; 174 ctx->cfg.css = "/cgit.css";
173 ctx->cfg.logo = "/git-logo.png"; 175 ctx->cfg.logo = "/git-logo.png";
174 ctx->cfg.local_time = 0; 176 ctx->cfg.local_time = 0;
175 ctx->cfg.max_repo_count = 50; 177 ctx->cfg.max_repo_count = 50;
176 ctx->cfg.max_commit_count = 50; 178 ctx->cfg.max_commit_count = 50;
177 ctx->cfg.max_lock_attempts = 5; 179 ctx->cfg.max_lock_attempts = 5;
178 ctx->cfg.max_msg_len = 80; 180 ctx->cfg.max_msg_len = 80;
179 ctx->cfg.max_repodesc_len = 80; 181 ctx->cfg.max_repodesc_len = 80;
180 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; 182 ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s";
181 ctx->cfg.renamelimit = -1; 183 ctx->cfg.renamelimit = -1;
182 ctx->cfg.robots = "index, nofollow"; 184 ctx->cfg.robots = "index, nofollow";
183 ctx->cfg.root_title = "Git repository browser"; 185 ctx->cfg.root_title = "Git repository browser";
184 ctx->cfg.root_desc = "a fast webinterface for the git dscm"; 186 ctx->cfg.root_desc = "a fast webinterface for the git dscm";
185 ctx->cfg.script_name = CGIT_SCRIPT_NAME; 187 ctx->cfg.script_name = CGIT_SCRIPT_NAME;
186 ctx->cfg.summary_branches = 10; 188 ctx->cfg.summary_branches = 10;
187 ctx->cfg.summary_log = 10; 189 ctx->cfg.summary_log = 10;
188 ctx->cfg.summary_tags = 10; 190 ctx->cfg.summary_tags = 10;
189 ctx->page.mimetype = "text/html"; 191 ctx->page.mimetype = "text/html";
190 ctx->page.charset = PAGE_ENCODING; 192 ctx->page.charset = PAGE_ENCODING;
191 ctx->page.filename = NULL; 193 ctx->page.filename = NULL;
192 ctx->page.size = 0; 194 ctx->page.size = 0;
193 ctx->page.modified = time(NULL); 195 ctx->page.modified = time(NULL);
194 ctx->page.expires = ctx->page.modified; 196 ctx->page.expires = ctx->page.modified;
195} 197}
196 198
197struct refmatch { 199struct refmatch {
198 char *req_ref; 200 char *req_ref;
199 char *first_ref; 201 char *first_ref;
200 int match; 202 int match;
201}; 203};
202 204
203int find_current_ref(const char *refname, const unsigned char *sha1, 205int find_current_ref(const char *refname, const unsigned char *sha1,
204 int flags, void *cb_data) 206 int flags, void *cb_data)
205{ 207{
206 struct refmatch *info; 208 struct refmatch *info;
207 209
208 info = (struct refmatch *)cb_data; 210 info = (struct refmatch *)cb_data;
209 if (!strcmp(refname, info->req_ref)) 211 if (!strcmp(refname, info->req_ref))
210 info->match = 1; 212 info->match = 1;
211 if (!info->first_ref) 213 if (!info->first_ref)
212 info->first_ref = xstrdup(refname); 214 info->first_ref = xstrdup(refname);
213 return info->match; 215 return info->match;
214} 216}
215 217
216char *find_default_branch(struct cgit_repo *repo) 218char *find_default_branch(struct cgit_repo *repo)
217{ 219{
218 struct refmatch info; 220 struct refmatch info;
219 char *ref; 221 char *ref;
220 222
221 info.req_ref = repo->defbranch; 223 info.req_ref = repo->defbranch;
222 info.first_ref = NULL; 224 info.first_ref = NULL;
223 info.match = 0; 225 info.match = 0;
224 for_each_branch_ref(find_current_ref, &info); 226 for_each_branch_ref(find_current_ref, &info);
225 if (info.match) 227 if (info.match)
226 ref = info.req_ref; 228 ref = info.req_ref;
227 else 229 else
228 ref = info.first_ref; 230 ref = info.first_ref;
229 if (ref) 231 if (ref)
230 ref = xstrdup(ref); 232 ref = xstrdup(ref);
231 return ref; 233 return ref;
232} 234}
233 235
234static int prepare_repo_cmd(struct cgit_context *ctx) 236static int prepare_repo_cmd(struct cgit_context *ctx)
235{ 237{
236 char *tmp; 238 char *tmp;
237 unsigned char sha1[20]; 239 unsigned char sha1[20];
238 int nongit = 0; 240 int nongit = 0;
239 241
240 setenv("GIT_DIR", ctx->repo->path, 1); 242 setenv("GIT_DIR", ctx->repo->path, 1);
241 setup_git_directory_gently(&nongit); 243 setup_git_directory_gently(&nongit);
242 if (nongit) { 244 if (nongit) {
243 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 245 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
244 "config error"); 246 "config error");
245 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 247 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
246 ctx->repo = NULL; 248 ctx->repo = NULL;
247 cgit_print_http_headers(ctx); 249 cgit_print_http_headers(ctx);
248 cgit_print_docstart(ctx); 250 cgit_print_docstart(ctx);
249 cgit_print_pageheader(ctx); 251 cgit_print_pageheader(ctx);
250 cgit_print_error(tmp); 252 cgit_print_error(tmp);
251 cgit_print_docend(); 253 cgit_print_docend();
252 return 1; 254 return 1;
diff --git a/cgit.h b/cgit.h
index 91db98a..ea90833 100644
--- a/cgit.h
+++ b/cgit.h
@@ -28,192 +28,193 @@
28 28
29 29
30/* 30/*
31 * Limits used for relative dates 31 * Limits used for relative dates
32 */ 32 */
33#define TM_MIN 60 33#define TM_MIN 60
34#define TM_HOUR (TM_MIN * 60) 34#define TM_HOUR (TM_MIN * 60)
35#define TM_DAY (TM_HOUR * 24) 35#define TM_DAY (TM_HOUR * 24)
36#define TM_WEEK (TM_DAY * 7) 36#define TM_WEEK (TM_DAY * 7)
37#define TM_YEAR (TM_DAY * 365) 37#define TM_YEAR (TM_DAY * 365)
38#define TM_MONTH (TM_YEAR / 12.0) 38#define TM_MONTH (TM_YEAR / 12.0)
39 39
40 40
41/* 41/*
42 * Default encoding 42 * Default encoding
43 */ 43 */
44#define PAGE_ENCODING "UTF-8" 44#define PAGE_ENCODING "UTF-8"
45 45
46typedef void (*configfn)(const char *name, const char *value); 46typedef void (*configfn)(const char *name, const char *value);
47typedef void (*filepair_fn)(struct diff_filepair *pair); 47typedef void (*filepair_fn)(struct diff_filepair *pair);
48typedef void (*linediff_fn)(char *line, int len); 48typedef void (*linediff_fn)(char *line, int len);
49 49
50struct cgit_repo { 50struct cgit_repo {
51 char *url; 51 char *url;
52 char *name; 52 char *name;
53 char *path; 53 char *path;
54 char *desc; 54 char *desc;
55 char *owner; 55 char *owner;
56 char *defbranch; 56 char *defbranch;
57 char *group; 57 char *group;
58 char *module_link; 58 char *module_link;
59 char *readme; 59 char *readme;
60 char *clone_url; 60 char *clone_url;
61 int snapshots; 61 int snapshots;
62 int enable_log_filecount; 62 int enable_log_filecount;
63 int enable_log_linecount; 63 int enable_log_linecount;
64}; 64};
65 65
66struct cgit_repolist { 66struct cgit_repolist {
67 int length; 67 int length;
68 int count; 68 int count;
69 struct cgit_repo *repos; 69 struct cgit_repo *repos;
70}; 70};
71 71
72struct commitinfo { 72struct commitinfo {
73 struct commit *commit; 73 struct commit *commit;
74 char *author; 74 char *author;
75 char *author_email; 75 char *author_email;
76 unsigned long author_date; 76 unsigned long author_date;
77 char *committer; 77 char *committer;
78 char *committer_email; 78 char *committer_email;
79 unsigned long committer_date; 79 unsigned long committer_date;
80 char *subject; 80 char *subject;
81 char *msg; 81 char *msg;
82 char *msg_encoding; 82 char *msg_encoding;
83}; 83};
84 84
85struct taginfo { 85struct taginfo {
86 char *tagger; 86 char *tagger;
87 char *tagger_email; 87 char *tagger_email;
88 unsigned long tagger_date; 88 unsigned long tagger_date;
89 char *msg; 89 char *msg;
90}; 90};
91 91
92struct refinfo { 92struct refinfo {
93 const char *refname; 93 const char *refname;
94 struct object *object; 94 struct object *object;
95 union { 95 union {
96 struct taginfo *tag; 96 struct taginfo *tag;
97 struct commitinfo *commit; 97 struct commitinfo *commit;
98 }; 98 };
99}; 99};
100 100
101struct reflist { 101struct reflist {
102 struct refinfo **refs; 102 struct refinfo **refs;
103 int alloc; 103 int alloc;
104 int count; 104 int count;
105}; 105};
106 106
107struct cgit_query { 107struct cgit_query {
108 int has_symref; 108 int has_symref;
109 int has_sha1; 109 int has_sha1;
110 char *raw; 110 char *raw;
111 char *repo; 111 char *repo;
112 char *page; 112 char *page;
113 char *search; 113 char *search;
114 char *grep; 114 char *grep;
115 char *head; 115 char *head;
116 char *sha1; 116 char *sha1;
117 char *sha2; 117 char *sha2;
118 char *path; 118 char *path;
119 char *name; 119 char *name;
120 char *mimetype; 120 char *mimetype;
121 char *url; 121 char *url;
122 int ofs; 122 int ofs;
123 int nohead; 123 int nohead;
124 char *sort;
124}; 125};
125 126
126struct cgit_config { 127struct cgit_config {
127 char *agefile; 128 char *agefile;
128 char *cache_root; 129 char *cache_root;
129 char *clone_prefix; 130 char *clone_prefix;
130 char *css; 131 char *css;
131 char *favicon; 132 char *favicon;
132 char *footer; 133 char *footer;
133 char *index_header; 134 char *index_header;
134 char *index_info; 135 char *index_info;
135 char *logo; 136 char *logo;
136 char *logo_link; 137 char *logo_link;
137 char *module_link; 138 char *module_link;
138 char *repo_group; 139 char *repo_group;
139 char *robots; 140 char *robots;
140 char *root_title; 141 char *root_title;
141 char *root_desc; 142 char *root_desc;
142 char *root_readme; 143 char *root_readme;
143 char *script_name; 144 char *script_name;
144 char *virtual_root; 145 char *virtual_root;
145 int cache_size; 146 int cache_size;
146 int cache_dynamic_ttl; 147 int cache_dynamic_ttl;
147 int cache_max_create_time; 148 int cache_max_create_time;
148 int cache_repo_ttl; 149 int cache_repo_ttl;
149 int cache_root_ttl; 150 int cache_root_ttl;
150 int cache_static_ttl; 151 int cache_static_ttl;
151 int enable_index_links; 152 int enable_index_links;
152 int enable_log_filecount; 153 int enable_log_filecount;
153 int enable_log_linecount; 154 int enable_log_linecount;
154 int local_time; 155 int local_time;
155 int max_repo_count; 156 int max_repo_count;
156 int max_commit_count; 157 int max_commit_count;
157 int max_lock_attempts; 158 int max_lock_attempts;
158 int max_msg_len; 159 int max_msg_len;
159 int max_repodesc_len; 160 int max_repodesc_len;
160 int nocache; 161 int nocache;
161 int renamelimit; 162 int renamelimit;
162 int snapshots; 163 int snapshots;
163 int summary_branches; 164 int summary_branches;
164 int summary_log; 165 int summary_log;
165 int summary_tags; 166 int summary_tags;
166}; 167};
167 168
168struct cgit_page { 169struct cgit_page {
169 time_t modified; 170 time_t modified;
170 time_t expires; 171 time_t expires;
171 size_t size; 172 size_t size;
172 char *mimetype; 173 char *mimetype;
173 char *charset; 174 char *charset;
174 char *filename; 175 char *filename;
175 char *title; 176 char *title;
176}; 177};
177 178
178struct cgit_context { 179struct cgit_context {
179 struct cgit_query qry; 180 struct cgit_query qry;
180 struct cgit_config cfg; 181 struct cgit_config cfg;
181 struct cgit_repo *repo; 182 struct cgit_repo *repo;
182 struct cgit_page page; 183 struct cgit_page page;
183}; 184};
184 185
185struct cgit_snapshot_format { 186struct cgit_snapshot_format {
186 const char *suffix; 187 const char *suffix;
187 const char *mimetype; 188 const char *mimetype;
188 write_archive_fn_t write_func; 189 write_archive_fn_t write_func;
189 int bit; 190 int bit;
190}; 191};
191 192
192extern const char *cgit_version; 193extern const char *cgit_version;
193 194
194extern struct cgit_repolist cgit_repolist; 195extern struct cgit_repolist cgit_repolist;
195extern struct cgit_context ctx; 196extern struct cgit_context ctx;
196extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 197extern const struct cgit_snapshot_format cgit_snapshot_formats[];
197 198
198extern struct cgit_repo *cgit_add_repo(const char *url); 199extern struct cgit_repo *cgit_add_repo(const char *url);
199extern struct cgit_repo *cgit_get_repoinfo(const char *url); 200extern struct cgit_repo *cgit_get_repoinfo(const char *url);
200extern void cgit_repo_config_cb(const char *name, const char *value); 201extern void cgit_repo_config_cb(const char *name, const char *value);
201 202
202extern int chk_zero(int result, char *msg); 203extern int chk_zero(int result, char *msg);
203extern int chk_positive(int result, char *msg); 204extern int chk_positive(int result, char *msg);
204extern int chk_non_negative(int result, char *msg); 205extern int chk_non_negative(int result, char *msg);
205 206
206extern char *trim_end(const char *str, char c); 207extern char *trim_end(const char *str, char c);
207extern char *strlpart(char *txt, int maxlen); 208extern char *strlpart(char *txt, int maxlen);
208extern char *strrpart(char *txt, int maxlen); 209extern char *strrpart(char *txt, int maxlen);
209 210
210extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 211extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
211extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 212extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
212 int flags, void *cb_data); 213 int flags, void *cb_data);
213 214
214extern void *cgit_free_commitinfo(struct commitinfo *info); 215extern void *cgit_free_commitinfo(struct commitinfo *info);
215 216
216extern int cgit_diff_files(const unsigned char *old_sha1, 217extern int cgit_diff_files(const unsigned char *old_sha1,
217 const unsigned char *new_sha1, 218 const unsigned char *new_sha1,
218 linediff_fn fn); 219 linediff_fn fn);
219 220
diff --git a/ui-repolist.c b/ui-repolist.c
index c23232c..312a7ee 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -1,167 +1,200 @@
1/* ui-repolist.c: functions for generating the repolist page 1/* ui-repolist.c: functions for generating the repolist page
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include <time.h> 9#include <time.h>
10 10
11#include "cgit.h" 11#include "cgit.h"
12#include "html.h" 12#include "html.h"
13#include "ui-shared.h" 13#include "ui-shared.h"
14 14
15time_t read_agefile(char *path) 15time_t read_agefile(char *path)
16{ 16{
17 FILE *f; 17 FILE *f;
18 static char buf[64], buf2[64]; 18 static char buf[64], buf2[64];
19 19
20 if (!(f = fopen(path, "r"))) 20 if (!(f = fopen(path, "r")))
21 return -1; 21 return -1;
22 if (fgets(buf, sizeof(buf), f) == NULL) 22 if (fgets(buf, sizeof(buf), f) == NULL)
23 return -1; 23 return -1;
24 fclose(f); 24 fclose(f);
25 if (parse_date(buf, buf2, sizeof(buf2))) 25 if (parse_date(buf, buf2, sizeof(buf2)))
26 return strtoul(buf2, NULL, 10); 26 return strtoul(buf2, NULL, 10);
27 else 27 else
28 return 0; 28 return 0;
29} 29}
30 30
31static void print_modtime(struct cgit_repo *repo) 31static void print_modtime(struct cgit_repo *repo)
32{ 32{
33 char *path; 33 char *path;
34 struct stat s; 34 struct stat s;
35 35
36 path = fmt("%s/%s", repo->path, ctx.cfg.agefile); 36 path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
37 if (stat(path, &s) == 0) { 37 if (stat(path, &s) == 0) {
38 cgit_print_age(read_agefile(path), -1, NULL); 38 cgit_print_age(read_agefile(path), -1, NULL);
39 return; 39 return;
40 } 40 }
41 41
42 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch); 42 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
43 if (stat(path, &s) != 0) 43 if (stat(path, &s) != 0)
44 return; 44 return;
45 cgit_print_age(s.st_mtime, -1, NULL); 45 cgit_print_age(s.st_mtime, -1, NULL);
46} 46}
47 47
48int is_match(struct cgit_repo *repo) 48int is_match(struct cgit_repo *repo)
49{ 49{
50 if (!ctx.qry.search) 50 if (!ctx.qry.search)
51 return 1; 51 return 1;
52 if (repo->url && strcasestr(repo->url, ctx.qry.search)) 52 if (repo->url && strcasestr(repo->url, ctx.qry.search))
53 return 1; 53 return 1;
54 if (repo->name && strcasestr(repo->name, ctx.qry.search)) 54 if (repo->name && strcasestr(repo->name, ctx.qry.search))
55 return 1; 55 return 1;
56 if (repo->desc && strcasestr(repo->desc, ctx.qry.search)) 56 if (repo->desc && strcasestr(repo->desc, ctx.qry.search))
57 return 1; 57 return 1;
58 if (repo->owner && strcasestr(repo->owner, ctx.qry.search)) 58 if (repo->owner && strcasestr(repo->owner, ctx.qry.search))
59 return 1; 59 return 1;
60 return 0; 60 return 0;
61} 61}
62 62
63int is_in_url(struct cgit_repo *repo) 63int is_in_url(struct cgit_repo *repo)
64{ 64{
65 if (!ctx.qry.url) 65 if (!ctx.qry.url)
66 return 1; 66 return 1;
67 if (repo->url && !prefixcmp(repo->url, ctx.qry.url)) 67 if (repo->url && !prefixcmp(repo->url, ctx.qry.url))
68 return 1; 68 return 1;
69 return 0; 69 return 0;
70} 70}
71 71
72void print_header(int columns) 72void print_header(int columns)
73{ 73{
74 html("<tr class='nohover'>" 74 html("<tr class='nohover'>"
75 "<th class='left'>Name</th>" 75 "<th class='left'>Name</th>"
76 "<th class='left'>Description</th>" 76 "<th class='left'>Description</th>"
77 "<th class='left'>Owner</th>" 77 "<th class='left'>Owner</th>"
78 "<th class='left'>Idle</th>"); 78 "<th class='left'><a href=\"?s=1\">Idle</a></th>");
79 if (ctx.cfg.enable_index_links) 79 if (ctx.cfg.enable_index_links)
80 html("<th class='left'>Links</th>"); 80 html("<th class='left'>Links</th>");
81 html("</tr>\n"); 81 html("</tr>\n");
82} 82}
83 83
84 84
85void print_pager(int items, int pagelen, char *search) 85void print_pager(int items, int pagelen, char *search)
86{ 86{
87 int i; 87 int i;
88 html("<div class='pager'>"); 88 html("<div class='pager'>");
89 for(i = 0; i * pagelen < items; i++) 89 for(i = 0; i * pagelen < items; i++)
90 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL, 90 cgit_index_link(fmt("[%d]", i+1), fmt("Page %d", i+1), NULL,
91 search, i * pagelen); 91 search, i * pagelen);
92 html("</div>"); 92 html("</div>");
93} 93}
94 94
95static int cgit_reposort_modtime(const void *a, const void *b)
96{
97 const struct cgit_repo *r1 = a;
98 const struct cgit_repo *r2 = b;
99 char *path;
100 struct stat s;
101 time_t t1, t2;
102 path = fmt("%s/%s", r1->path, ctx.cfg.agefile);
103 if (stat(path, &s) == 0) {
104 t1 = read_agefile(path);
105 } else {
106 path = fmt("%s/refs/heads/%s", r1->path, r1->defbranch);
107 if (stat(path, &s) != 0)
108 return 0;
109 t1 =s.st_mtime;
110 }
111
112 path = fmt("%s/%s", r2->path, ctx.cfg.agefile);
113 if (stat(path, &s) == 0) {
114 t2 = read_agefile(path);
115 } else {
116 path = fmt("%s/refs/heads/%s", r2->path, r2->defbranch);
117 if (stat(path, &s) != 0)
118 return 0;
119 t2 =s.st_mtime;
120 }
121 return t2-t1;
122}
123
95void cgit_print_repolist() 124void cgit_print_repolist()
96{ 125{
97 int i, columns = 4, hits = 0, header = 0; 126 int i, columns = 4, hits = 0, header = 0;
98 char *last_group = NULL; 127 char *last_group = NULL;
99 128
100 if (ctx.cfg.enable_index_links) 129 if (ctx.cfg.enable_index_links)
101 columns++; 130 columns++;
102 131
103 ctx.page.title = ctx.cfg.root_title; 132 ctx.page.title = ctx.cfg.root_title;
104 cgit_print_http_headers(&ctx); 133 cgit_print_http_headers(&ctx);
105 cgit_print_docstart(&ctx); 134 cgit_print_docstart(&ctx);
106 cgit_print_pageheader(&ctx); 135 cgit_print_pageheader(&ctx);
107 136
108 if (ctx.cfg.index_header) 137 if (ctx.cfg.index_header)
109 html_include(ctx.cfg.index_header); 138 html_include(ctx.cfg.index_header);
110 139
140 if(ctx.qry.sort)
141 qsort(cgit_repolist.repos,cgit_repolist.count,sizeof(struct cgit_repo),cgit_reposort_modtime);
142
111 html("<table summary='repository list' class='list nowrap'>"); 143 html("<table summary='repository list' class='list nowrap'>");
112 for (i=0; i<cgit_repolist.count; i++) { 144 for (i=0; i<cgit_repolist.count; i++) {
113 ctx.repo = &cgit_repolist.repos[i]; 145 ctx.repo = &cgit_repolist.repos[i];
114 if (!(is_match(ctx.repo) && is_in_url(ctx.repo))) 146 if (!(is_match(ctx.repo) && is_in_url(ctx.repo)))
115 continue; 147 continue;
116 hits++; 148 hits++;
117 if (hits <= ctx.qry.ofs) 149 if (hits <= ctx.qry.ofs)
118 continue; 150 continue;
119 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count) 151 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
120 continue; 152 continue;
121 if (!header++) 153 if (!header++)
122 print_header(columns); 154 print_header(columns);
123 if ((last_group == NULL && ctx.repo->group != NULL) || 155 if (!ctx.qry.sort &&
156 ((last_group == NULL && ctx.repo->group != NULL) ||
124 (last_group != NULL && ctx.repo->group == NULL) || 157 (last_group != NULL && ctx.repo->group == NULL) ||
125 (last_group != NULL && ctx.repo->group != NULL && 158 (last_group != NULL && ctx.repo->group != NULL &&
126 strcmp(ctx.repo->group, last_group))) { 159 strcmp(ctx.repo->group, last_group)))) {
127 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", 160 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
128 columns); 161 columns);
129 html_txt(ctx.repo->group); 162 html_txt(ctx.repo->group);
130 html("</td></tr>"); 163 html("</td></tr>");
131 last_group = ctx.repo->group; 164 last_group = ctx.repo->group;
132 } 165 }
133 htmlf("<tr><td class='%s'>", 166 htmlf("<tr><td class='%s'>",
134 ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); 167 ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
135 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); 168 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
136 html("</td><td>"); 169 html("</td><td>");
137 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); 170 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
138 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); 171 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
139 html_link_close(); 172 html_link_close();
140 html("</td><td>"); 173 html("</td><td>");
141 html_txt(ctx.repo->owner); 174 html_txt(ctx.repo->owner);
142 html("</td><td>"); 175 html("</td><td>");
143 print_modtime(ctx.repo); 176 print_modtime(ctx.repo);
144 html("</td>"); 177 html("</td>");
145 if (ctx.cfg.enable_index_links) { 178 if (ctx.cfg.enable_index_links) {
146 html("<td>"); 179 html("<td>");
147 cgit_summary_link("summary", NULL, "button", NULL); 180 cgit_summary_link("summary", NULL, "button", NULL);
148 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 181 cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
149 0, NULL, NULL); 182 0, NULL, NULL);
150 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); 183 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
151 html("</td>"); 184 html("</td>");
152 } 185 }
153 html("</tr>\n"); 186 html("</tr>\n");
154 } 187 }
155 html("</table>"); 188 html("</table>");
156 if (!hits) 189 if (!hits)
157 cgit_print_error("No repositories found"); 190 cgit_print_error("No repositories found");
158 else if (hits > ctx.cfg.max_repo_count) 191 else if (hits > ctx.cfg.max_repo_count)
159 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search); 192 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search);
160 cgit_print_docend(); 193 cgit_print_docend();
161} 194}
162 195
163void cgit_print_site_readme() 196void cgit_print_site_readme()
164{ 197{
165 if (ctx.cfg.root_readme) 198 if (ctx.cfg.root_readme)
166 html_include(ctx.cfg.root_readme); 199 html_include(ctx.cfg.root_readme);
167} 200}