summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c1
-rw-r--r--cgit.h32
-rw-r--r--ui-blob.c1
-rw-r--r--ui-commit.c1
-rw-r--r--ui-diff.c1
-rw-r--r--ui-log.c1
-rw-r--r--ui-patch.c1
-rw-r--r--ui-refs.c1
-rw-r--r--ui-repolist.c1
-rw-r--r--ui-shared.h36
-rw-r--r--ui-snapshot.c1
-rw-r--r--ui-tag.c1
-rw-r--r--ui-tree.c1
13 files changed, 47 insertions, 32 deletions
diff --git a/cgit.c b/cgit.c
index 79e0e43..dbb023e 100644
--- a/cgit.c
+++ b/cgit.c
@@ -1,138 +1,139 @@
1/* cgit.c: cgi for the git scm 1/* cgit.c: cgi for the git scm
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "cmd.h" 10#include "cmd.h"
11#include "ui-shared.h"
11 12
12static int cgit_prepare_cache(struct cacheitem *item) 13static int cgit_prepare_cache(struct cacheitem *item)
13{ 14{
14 if (!ctx.repo && ctx.qry.repo) { 15 if (!ctx.repo && ctx.qry.repo) {
15 ctx.page.title = fmt("%s - %s", ctx.cfg.root_title, 16 ctx.page.title = fmt("%s - %s", ctx.cfg.root_title,
16 "Bad request"); 17 "Bad request");
17 cgit_print_http_headers(&ctx); 18 cgit_print_http_headers(&ctx);
18 cgit_print_docstart(&ctx); 19 cgit_print_docstart(&ctx);
19 cgit_print_pageheader(&ctx); 20 cgit_print_pageheader(&ctx);
20 cgit_print_error(fmt("Unknown repo: %s", ctx.qry.repo)); 21 cgit_print_error(fmt("Unknown repo: %s", ctx.qry.repo));
21 cgit_print_docend(); 22 cgit_print_docend();
22 return 0; 23 return 0;
23 } 24 }
24 25
25 if (!ctx.repo) { 26 if (!ctx.repo) {
26 item->name = xstrdup(fmt("%s/index.html", ctx.cfg.cache_root)); 27 item->name = xstrdup(fmt("%s/index.html", ctx.cfg.cache_root));
27 item->ttl = ctx.cfg.cache_root_ttl; 28 item->ttl = ctx.cfg.cache_root_ttl;
28 return 1; 29 return 1;
29 } 30 }
30 31
31 if (!cgit_cmd) { 32 if (!cgit_cmd) {
32 item->name = xstrdup(fmt("%s/%s/index.%s.html", ctx.cfg.cache_root, 33 item->name = xstrdup(fmt("%s/%s/index.%s.html", ctx.cfg.cache_root,
33 cache_safe_filename(ctx.repo->url), 34 cache_safe_filename(ctx.repo->url),
34 cache_safe_filename(ctx.qry.raw))); 35 cache_safe_filename(ctx.qry.raw)));
35 item->ttl = ctx.cfg.cache_repo_ttl; 36 item->ttl = ctx.cfg.cache_repo_ttl;
36 } else { 37 } else {
37 item->name = xstrdup(fmt("%s/%s/%s/%s.html", ctx.cfg.cache_root, 38 item->name = xstrdup(fmt("%s/%s/%s/%s.html", ctx.cfg.cache_root,
38 cache_safe_filename(ctx.repo->url), 39 cache_safe_filename(ctx.repo->url),
39 ctx.qry.page, 40 ctx.qry.page,
40 cache_safe_filename(ctx.qry.raw))); 41 cache_safe_filename(ctx.qry.raw)));
41 if (ctx.qry.has_symref) 42 if (ctx.qry.has_symref)
42 item->ttl = ctx.cfg.cache_dynamic_ttl; 43 item->ttl = ctx.cfg.cache_dynamic_ttl;
43 else if (ctx.qry.has_sha1) 44 else if (ctx.qry.has_sha1)
44 item->ttl = ctx.cfg.cache_static_ttl; 45 item->ttl = ctx.cfg.cache_static_ttl;
45 else 46 else
46 item->ttl = ctx.cfg.cache_repo_ttl; 47 item->ttl = ctx.cfg.cache_repo_ttl;
47 } 48 }
48 return 1; 49 return 1;
49} 50}
50 51
51struct refmatch { 52struct refmatch {
52 char *req_ref; 53 char *req_ref;
53 char *first_ref; 54 char *first_ref;
54 int match; 55 int match;
55}; 56};
56 57
57int find_current_ref(const char *refname, const unsigned char *sha1, 58int find_current_ref(const char *refname, const unsigned char *sha1,
58 int flags, void *cb_data) 59 int flags, void *cb_data)
59{ 60{
60 struct refmatch *info; 61 struct refmatch *info;
61 62
62 info = (struct refmatch *)cb_data; 63 info = (struct refmatch *)cb_data;
63 if (!strcmp(refname, info->req_ref)) 64 if (!strcmp(refname, info->req_ref))
64 info->match = 1; 65 info->match = 1;
65 if (!info->first_ref) 66 if (!info->first_ref)
66 info->first_ref = xstrdup(refname); 67 info->first_ref = xstrdup(refname);
67 return info->match; 68 return info->match;
68} 69}
69 70
70char *find_default_branch(struct cgit_repo *repo) 71char *find_default_branch(struct cgit_repo *repo)
71{ 72{
72 struct refmatch info; 73 struct refmatch info;
73 74
74 info.req_ref = repo->defbranch; 75 info.req_ref = repo->defbranch;
75 info.first_ref = NULL; 76 info.first_ref = NULL;
76 info.match = 0; 77 info.match = 0;
77 for_each_branch_ref(find_current_ref, &info); 78 for_each_branch_ref(find_current_ref, &info);
78 if (info.match) 79 if (info.match)
79 return info.req_ref; 80 return info.req_ref;
80 else 81 else
81 return info.first_ref; 82 return info.first_ref;
82} 83}
83 84
84static int prepare_repo_cmd(struct cgit_context *ctx) 85static int prepare_repo_cmd(struct cgit_context *ctx)
85{ 86{
86 char *tmp; 87 char *tmp;
87 unsigned char sha1[20]; 88 unsigned char sha1[20];
88 int nongit = 0; 89 int nongit = 0;
89 90
90 setenv("GIT_DIR", ctx->repo->path, 1); 91 setenv("GIT_DIR", ctx->repo->path, 1);
91 setup_git_directory_gently(&nongit); 92 setup_git_directory_gently(&nongit);
92 if (nongit) { 93 if (nongit) {
93 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title, 94 ctx->page.title = fmt("%s - %s", ctx->cfg.root_title,
94 "config error"); 95 "config error");
95 tmp = fmt("Not a git repository: '%s'", ctx->repo->path); 96 tmp = fmt("Not a git repository: '%s'", ctx->repo->path);
96 ctx->repo = NULL; 97 ctx->repo = NULL;
97 cgit_print_http_headers(ctx); 98 cgit_print_http_headers(ctx);
98 cgit_print_docstart(ctx); 99 cgit_print_docstart(ctx);
99 cgit_print_pageheader(ctx); 100 cgit_print_pageheader(ctx);
100 cgit_print_error(tmp); 101 cgit_print_error(tmp);
101 cgit_print_docend(); 102 cgit_print_docend();
102 return 1; 103 return 1;
103 } 104 }
104 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc); 105 ctx->page.title = fmt("%s - %s", ctx->repo->name, ctx->repo->desc);
105 106
106 if (!ctx->qry.head) { 107 if (!ctx->qry.head) {
107 ctx->qry.head = xstrdup(find_default_branch(ctx->repo)); 108 ctx->qry.head = xstrdup(find_default_branch(ctx->repo));
108 ctx->repo->defbranch = ctx->qry.head; 109 ctx->repo->defbranch = ctx->qry.head;
109 } 110 }
110 111
111 if (!ctx->qry.head) { 112 if (!ctx->qry.head) {
112 cgit_print_http_headers(ctx); 113 cgit_print_http_headers(ctx);
113 cgit_print_docstart(ctx); 114 cgit_print_docstart(ctx);
114 cgit_print_pageheader(ctx); 115 cgit_print_pageheader(ctx);
115 cgit_print_error("Repository seems to be empty"); 116 cgit_print_error("Repository seems to be empty");
116 cgit_print_docend(); 117 cgit_print_docend();
117 return 1; 118 return 1;
118 } 119 }
119 120
120 if (get_sha1(ctx->qry.head, sha1)) { 121 if (get_sha1(ctx->qry.head, sha1)) {
121 tmp = xstrdup(ctx->qry.head); 122 tmp = xstrdup(ctx->qry.head);
122 ctx->qry.head = ctx->repo->defbranch; 123 ctx->qry.head = ctx->repo->defbranch;
123 cgit_print_http_headers(ctx); 124 cgit_print_http_headers(ctx);
124 cgit_print_docstart(ctx); 125 cgit_print_docstart(ctx);
125 cgit_print_pageheader(ctx); 126 cgit_print_pageheader(ctx);
126 cgit_print_error(fmt("Invalid branch: %s", tmp)); 127 cgit_print_error(fmt("Invalid branch: %s", tmp));
127 cgit_print_docend(); 128 cgit_print_docend();
128 return 1; 129 return 1;
129 } 130 }
130 return 0; 131 return 0;
131} 132}
132 133
133static void process_request(struct cgit_context *ctx) 134static void process_request(struct cgit_context *ctx)
134{ 135{
135 struct cgit_cmd *cmd; 136 struct cgit_cmd *cmd;
136 137
137 cmd = cgit_get_cmd(ctx); 138 cmd = cgit_get_cmd(ctx);
138 if (!cmd) { 139 if (!cmd) {
diff --git a/cgit.h b/cgit.h
index 1b23369..c1a231d 100644
--- a/cgit.h
+++ b/cgit.h
@@ -112,165 +112,133 @@ struct reflist {
112 112
113struct cgit_query { 113struct cgit_query {
114 int has_symref; 114 int has_symref;
115 int has_sha1; 115 int has_sha1;
116 char *raw; 116 char *raw;
117 char *repo; 117 char *repo;
118 char *page; 118 char *page;
119 char *search; 119 char *search;
120 char *grep; 120 char *grep;
121 char *head; 121 char *head;
122 char *sha1; 122 char *sha1;
123 char *sha2; 123 char *sha2;
124 char *path; 124 char *path;
125 char *name; 125 char *name;
126 int ofs; 126 int ofs;
127}; 127};
128 128
129struct cgit_config { 129struct cgit_config {
130 char *agefile; 130 char *agefile;
131 char *cache_root; 131 char *cache_root;
132 char *clone_prefix; 132 char *clone_prefix;
133 char *css; 133 char *css;
134 char *index_header; 134 char *index_header;
135 char *index_info; 135 char *index_info;
136 char *logo; 136 char *logo;
137 char *logo_link; 137 char *logo_link;
138 char *module_link; 138 char *module_link;
139 char *repo_group; 139 char *repo_group;
140 char *robots; 140 char *robots;
141 char *root_title; 141 char *root_title;
142 char *script_name; 142 char *script_name;
143 char *virtual_root; 143 char *virtual_root;
144 int cache_dynamic_ttl; 144 int cache_dynamic_ttl;
145 int cache_max_create_time; 145 int cache_max_create_time;
146 int cache_repo_ttl; 146 int cache_repo_ttl;
147 int cache_root_ttl; 147 int cache_root_ttl;
148 int cache_static_ttl; 148 int cache_static_ttl;
149 int enable_index_links; 149 int enable_index_links;
150 int enable_log_filecount; 150 int enable_log_filecount;
151 int enable_log_linecount; 151 int enable_log_linecount;
152 int max_commit_count; 152 int max_commit_count;
153 int max_lock_attempts; 153 int max_lock_attempts;
154 int max_msg_len; 154 int max_msg_len;
155 int max_repodesc_len; 155 int max_repodesc_len;
156 int nocache; 156 int nocache;
157 int renamelimit; 157 int renamelimit;
158 int snapshots; 158 int snapshots;
159 int summary_branches; 159 int summary_branches;
160 int summary_log; 160 int summary_log;
161 int summary_tags; 161 int summary_tags;
162}; 162};
163 163
164struct cgit_page { 164struct cgit_page {
165 time_t modified; 165 time_t modified;
166 time_t expires; 166 time_t expires;
167 char *mimetype; 167 char *mimetype;
168 char *charset; 168 char *charset;
169 char *filename; 169 char *filename;
170 char *title; 170 char *title;
171}; 171};
172 172
173struct cgit_context { 173struct cgit_context {
174 struct cgit_query qry; 174 struct cgit_query qry;
175 struct cgit_config cfg; 175 struct cgit_config cfg;
176 struct cgit_repo *repo; 176 struct cgit_repo *repo;
177 struct cgit_page page; 177 struct cgit_page page;
178}; 178};
179 179
180struct cgit_snapshot_format { 180struct cgit_snapshot_format {
181 const char *suffix; 181 const char *suffix;
182 const char *mimetype; 182 const char *mimetype;
183 write_archive_fn_t write_func; 183 write_archive_fn_t write_func;
184 int bit; 184 int bit;
185}; 185};
186 186
187extern const char *cgit_version; 187extern const char *cgit_version;
188 188
189extern struct cgit_repolist cgit_repolist; 189extern struct cgit_repolist cgit_repolist;
190extern struct cgit_context ctx; 190extern struct cgit_context ctx;
191extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 191extern const struct cgit_snapshot_format cgit_snapshot_formats[];
192extern int cgit_cmd; 192extern int cgit_cmd;
193 193
194extern void cgit_prepare_context(struct cgit_context *ctx); 194extern void cgit_prepare_context(struct cgit_context *ctx);
195extern struct cgit_repo *cgit_get_repoinfo(const char *url); 195extern struct cgit_repo *cgit_get_repoinfo(const char *url);
196extern void cgit_global_config_cb(const char *name, const char *value); 196extern void cgit_global_config_cb(const char *name, const char *value);
197extern void cgit_repo_config_cb(const char *name, const char *value); 197extern void cgit_repo_config_cb(const char *name, const char *value);
198extern void cgit_querystring_cb(const char *name, const char *value); 198extern void cgit_querystring_cb(const char *name, const char *value);
199 199
200extern int chk_zero(int result, char *msg); 200extern int chk_zero(int result, char *msg);
201extern int chk_positive(int result, char *msg); 201extern int chk_positive(int result, char *msg);
202extern int chk_non_negative(int result, char *msg); 202extern int chk_non_negative(int result, char *msg);
203 203
204extern int hextoint(char c); 204extern int hextoint(char c);
205extern char *trim_end(const char *str, char c); 205extern char *trim_end(const char *str, char c);
206extern char *strlpart(char *txt, int maxlen); 206extern char *strlpart(char *txt, int maxlen);
207extern char *strrpart(char *txt, int maxlen); 207extern char *strrpart(char *txt, int maxlen);
208 208
209extern void cgit_add_ref(struct reflist *list, struct refinfo *ref); 209extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
210extern int cgit_refs_cb(const char *refname, const unsigned char *sha1, 210extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
211 int flags, void *cb_data); 211 int flags, void *cb_data);
212 212
213extern void *cgit_free_commitinfo(struct commitinfo *info); 213extern void *cgit_free_commitinfo(struct commitinfo *info);
214 214
215extern int cgit_diff_files(const unsigned char *old_sha1, 215extern int cgit_diff_files(const unsigned char *old_sha1,
216 const unsigned char *new_sha1, 216 const unsigned char *new_sha1,
217 linediff_fn fn); 217 linediff_fn fn);
218 218
219extern void cgit_diff_tree(const unsigned char *old_sha1, 219extern void cgit_diff_tree(const unsigned char *old_sha1,
220 const unsigned char *new_sha1, 220 const unsigned char *new_sha1,
221 filepair_fn fn, const char *prefix); 221 filepair_fn fn, const char *prefix);
222 222
223extern void cgit_diff_commit(struct commit *commit, filepair_fn fn); 223extern void cgit_diff_commit(struct commit *commit, filepair_fn fn);
224 224
225extern char *fmt(const char *format,...); 225extern char *fmt(const char *format,...);
226 226
227extern int cgit_read_config(const char *filename, configfn fn); 227extern int cgit_read_config(const char *filename, configfn fn);
228extern int cgit_parse_query(char *txt, configfn fn); 228extern int cgit_parse_query(char *txt, configfn fn);
229extern struct commitinfo *cgit_parse_commit(struct commit *commit); 229extern struct commitinfo *cgit_parse_commit(struct commit *commit);
230extern struct taginfo *cgit_parse_tag(struct tag *tag); 230extern struct taginfo *cgit_parse_tag(struct tag *tag);
231extern void cgit_parse_url(const char *url); 231extern void cgit_parse_url(const char *url);
232 232
233extern char *cache_safe_filename(const char *unsafe); 233extern char *cache_safe_filename(const char *unsafe);
234extern int cache_lock(struct cacheitem *item); 234extern int cache_lock(struct cacheitem *item);
235extern int cache_unlock(struct cacheitem *item); 235extern int cache_unlock(struct cacheitem *item);
236extern int cache_cancel_lock(struct cacheitem *item); 236extern int cache_cancel_lock(struct cacheitem *item);
237extern int cache_exist(struct cacheitem *item); 237extern int cache_exist(struct cacheitem *item);
238extern int cache_expired(struct cacheitem *item); 238extern int cache_expired(struct cacheitem *item);
239 239
240extern char *cgit_repourl(const char *reponame);
241extern char *cgit_fileurl(const char *reponame, const char *pagename,
242 const char *filename, const char *query);
243extern char *cgit_pageurl(const char *reponame, const char *pagename,
244 const char *query);
245
246extern const char *cgit_repobasename(const char *reponame); 240extern const char *cgit_repobasename(const char *reponame);
247 241
248extern void cgit_tree_link(char *name, char *title, char *class, char *head,
249 char *rev, char *path);
250extern void cgit_log_link(char *name, char *title, char *class, char *head,
251 char *rev, char *path, int ofs, char *grep,
252 char *pattern);
253extern void cgit_commit_link(char *name, char *title, char *class, char *head,
254 char *rev);
255extern void cgit_refs_link(char *name, char *title, char *class, char *head,
256 char *rev, char *path);
257extern void cgit_snapshot_link(char *name, char *title, char *class,
258 char *head, char *rev, char *archivename);
259extern void cgit_diff_link(char *name, char *title, char *class, char *head,
260 char *new_rev, char *old_rev, char *path);
261
262extern void cgit_object_link(struct object *obj);
263
264extern void cgit_print_error(char *msg);
265extern void cgit_print_date(time_t secs, char *format);
266extern void cgit_print_age(time_t t, time_t max_relative, char *format);
267extern void cgit_print_http_headers(struct cgit_context *ctx);
268extern void cgit_print_docstart(struct cgit_context *ctx);
269extern void cgit_print_docend();
270extern void cgit_print_pageheader(struct cgit_context *ctx);
271extern void cgit_print_filemode(unsigned short mode);
272extern void cgit_print_snapshot_links(const char *repo, const char *head,
273 const char *hex, int snapshots);
274extern int cgit_parse_snapshots_mask(const char *str); 242extern int cgit_parse_snapshots_mask(const char *str);
275 243
276#endif /* CGIT_H */ 244#endif /* CGIT_H */
diff --git a/ui-blob.c b/ui-blob.c
index 3b29132..11589db 100644
--- a/ui-blob.c
+++ b/ui-blob.c
@@ -1,42 +1,43 @@
1/* ui-blob.c: show blob content 1/* ui-blob.c: show blob content
2 * 2 *
3 * Copyright (C) 2008 Lars Hjemli 3 * Copyright (C) 2008 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12void cgit_print_blob(const char *hex, char *path) 13void cgit_print_blob(const char *hex, char *path)
13{ 14{
14 15
15 unsigned char sha1[20]; 16 unsigned char sha1[20];
16 enum object_type type; 17 enum object_type type;
17 unsigned char *buf; 18 unsigned char *buf;
18 unsigned long size; 19 unsigned long size;
19 20
20 if (get_sha1_hex(hex, sha1)){ 21 if (get_sha1_hex(hex, sha1)){
21 cgit_print_error(fmt("Bad hex value: %s", hex)); 22 cgit_print_error(fmt("Bad hex value: %s", hex));
22 return; 23 return;
23 } 24 }
24 25
25 type = sha1_object_info(sha1, &size); 26 type = sha1_object_info(sha1, &size);
26 if (type == OBJ_BAD) { 27 if (type == OBJ_BAD) {
27 cgit_print_error(fmt("Bad object name: %s", hex)); 28 cgit_print_error(fmt("Bad object name: %s", hex));
28 return; 29 return;
29 } 30 }
30 31
31 buf = read_sha1_file(sha1, &type, &size); 32 buf = read_sha1_file(sha1, &type, &size);
32 if (!buf) { 33 if (!buf) {
33 cgit_print_error(fmt("Error reading object %s", hex)); 34 cgit_print_error(fmt("Error reading object %s", hex));
34 return; 35 return;
35 } 36 }
36 37
37 buf[size] = '\0'; 38 buf[size] = '\0';
38 ctx.page.mimetype = "text/plain"; 39 ctx.page.mimetype = "text/plain";
39 ctx.page.filename = path; 40 ctx.page.filename = path;
40 cgit_print_http_headers(&ctx); 41 cgit_print_http_headers(&ctx);
41 write(htmlfd, buf, size); 42 write(htmlfd, buf, size);
42} 43}
diff --git a/ui-commit.c b/ui-commit.c
index ed25824..8019e36 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -1,138 +1,139 @@
1/* ui-commit.c: generate commit view 1/* ui-commit.c: generate commit view
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12static int files, slots; 13static int files, slots;
13static int total_adds, total_rems, max_changes; 14static int total_adds, total_rems, max_changes;
14static int lines_added, lines_removed; 15static int lines_added, lines_removed;
15static char *curr_rev; 16static char *curr_rev;
16 17
17static struct fileinfo { 18static struct fileinfo {
18 char status; 19 char status;
19 unsigned char old_sha1[20]; 20 unsigned char old_sha1[20];
20 unsigned char new_sha1[20]; 21 unsigned char new_sha1[20];
21 unsigned short old_mode; 22 unsigned short old_mode;
22 unsigned short new_mode; 23 unsigned short new_mode;
23 char *old_path; 24 char *old_path;
24 char *new_path; 25 char *new_path;
25 unsigned int added; 26 unsigned int added;
26 unsigned int removed; 27 unsigned int removed;
27} *items; 28} *items;
28 29
29 30
30void print_fileinfo(struct fileinfo *info) 31void print_fileinfo(struct fileinfo *info)
31{ 32{
32 char *class; 33 char *class;
33 34
34 switch (info->status) { 35 switch (info->status) {
35 case DIFF_STATUS_ADDED: 36 case DIFF_STATUS_ADDED:
36 class = "add"; 37 class = "add";
37 break; 38 break;
38 case DIFF_STATUS_COPIED: 39 case DIFF_STATUS_COPIED:
39 class = "cpy"; 40 class = "cpy";
40 break; 41 break;
41 case DIFF_STATUS_DELETED: 42 case DIFF_STATUS_DELETED:
42 class = "del"; 43 class = "del";
43 break; 44 break;
44 case DIFF_STATUS_MODIFIED: 45 case DIFF_STATUS_MODIFIED:
45 class = "upd"; 46 class = "upd";
46 break; 47 break;
47 case DIFF_STATUS_RENAMED: 48 case DIFF_STATUS_RENAMED:
48 class = "mov"; 49 class = "mov";
49 break; 50 break;
50 case DIFF_STATUS_TYPE_CHANGED: 51 case DIFF_STATUS_TYPE_CHANGED:
51 class = "typ"; 52 class = "typ";
52 break; 53 break;
53 case DIFF_STATUS_UNKNOWN: 54 case DIFF_STATUS_UNKNOWN:
54 class = "unk"; 55 class = "unk";
55 break; 56 break;
56 case DIFF_STATUS_UNMERGED: 57 case DIFF_STATUS_UNMERGED:
57 class = "stg"; 58 class = "stg";
58 break; 59 break;
59 default: 60 default:
60 die("bug: unhandled diff status %c", info->status); 61 die("bug: unhandled diff status %c", info->status);
61 } 62 }
62 63
63 html("<tr>"); 64 html("<tr>");
64 htmlf("<td class='mode'>"); 65 htmlf("<td class='mode'>");
65 if (is_null_sha1(info->new_sha1)) { 66 if (is_null_sha1(info->new_sha1)) {
66 cgit_print_filemode(info->old_mode); 67 cgit_print_filemode(info->old_mode);
67 } else { 68 } else {
68 cgit_print_filemode(info->new_mode); 69 cgit_print_filemode(info->new_mode);
69 } 70 }
70 71
71 if (info->old_mode != info->new_mode && 72 if (info->old_mode != info->new_mode &&
72 !is_null_sha1(info->old_sha1) && 73 !is_null_sha1(info->old_sha1) &&
73 !is_null_sha1(info->new_sha1)) { 74 !is_null_sha1(info->new_sha1)) {
74 html("<span class='modechange'>["); 75 html("<span class='modechange'>[");
75 cgit_print_filemode(info->old_mode); 76 cgit_print_filemode(info->old_mode);
76 html("]</span>"); 77 html("]</span>");
77 } 78 }
78 htmlf("</td><td class='%s'>", class); 79 htmlf("</td><td class='%s'>", class);
79 cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, curr_rev, 80 cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, curr_rev,
80 NULL, info->new_path); 81 NULL, info->new_path);
81 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) 82 if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED)
82 htmlf(" (%s from %s)", 83 htmlf(" (%s from %s)",
83 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed", 84 info->status == DIFF_STATUS_COPIED ? "copied" : "renamed",
84 info->old_path); 85 info->old_path);
85 html("</td><td class='right'>"); 86 html("</td><td class='right'>");
86 htmlf("%d", info->added + info->removed); 87 htmlf("%d", info->added + info->removed);
87 html("</td><td class='graph'>"); 88 html("</td><td class='graph'>");
88 htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes)); 89 htmlf("<table summary='file diffstat' width='%d%%'><tr>", (max_changes > 100 ? 100 : max_changes));
89 htmlf("<td class='add' style='width: %.1f%%;'/>", 90 htmlf("<td class='add' style='width: %.1f%%;'/>",
90 info->added * 100.0 / max_changes); 91 info->added * 100.0 / max_changes);
91 htmlf("<td class='rem' style='width: %.1f%%;'/>", 92 htmlf("<td class='rem' style='width: %.1f%%;'/>",
92 info->removed * 100.0 / max_changes); 93 info->removed * 100.0 / max_changes);
93 htmlf("<td class='none' style='width: %.1f%%;'/>", 94 htmlf("<td class='none' style='width: %.1f%%;'/>",
94 (max_changes - info->removed - info->added) * 100.0 / max_changes); 95 (max_changes - info->removed - info->added) * 100.0 / max_changes);
95 html("</tr></table></td></tr>\n"); 96 html("</tr></table></td></tr>\n");
96} 97}
97 98
98void cgit_count_diff_lines(char *line, int len) 99void cgit_count_diff_lines(char *line, int len)
99{ 100{
100 if (line && (len > 0)) { 101 if (line && (len > 0)) {
101 if (line[0] == '+') 102 if (line[0] == '+')
102 lines_added++; 103 lines_added++;
103 else if (line[0] == '-') 104 else if (line[0] == '-')
104 lines_removed++; 105 lines_removed++;
105 } 106 }
106} 107}
107 108
108void inspect_filepair(struct diff_filepair *pair) 109void inspect_filepair(struct diff_filepair *pair)
109{ 110{
110 files++; 111 files++;
111 lines_added = 0; 112 lines_added = 0;
112 lines_removed = 0; 113 lines_removed = 0;
113 cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines); 114 cgit_diff_files(pair->one->sha1, pair->two->sha1, cgit_count_diff_lines);
114 if (files >= slots) { 115 if (files >= slots) {
115 if (slots == 0) 116 if (slots == 0)
116 slots = 4; 117 slots = 4;
117 else 118 else
118 slots = slots * 2; 119 slots = slots * 2;
119 items = xrealloc(items, slots * sizeof(struct fileinfo)); 120 items = xrealloc(items, slots * sizeof(struct fileinfo));
120 } 121 }
121 items[files-1].status = pair->status; 122 items[files-1].status = pair->status;
122 hashcpy(items[files-1].old_sha1, pair->one->sha1); 123 hashcpy(items[files-1].old_sha1, pair->one->sha1);
123 hashcpy(items[files-1].new_sha1, pair->two->sha1); 124 hashcpy(items[files-1].new_sha1, pair->two->sha1);
124 items[files-1].old_mode = pair->one->mode; 125 items[files-1].old_mode = pair->one->mode;
125 items[files-1].new_mode = pair->two->mode; 126 items[files-1].new_mode = pair->two->mode;
126 items[files-1].old_path = xstrdup(pair->one->path); 127 items[files-1].old_path = xstrdup(pair->one->path);
127 items[files-1].new_path = xstrdup(pair->two->path); 128 items[files-1].new_path = xstrdup(pair->two->path);
128 items[files-1].added = lines_added; 129 items[files-1].added = lines_added;
129 items[files-1].removed = lines_removed; 130 items[files-1].removed = lines_removed;
130 if (lines_added + lines_removed > max_changes) 131 if (lines_added + lines_removed > max_changes)
131 max_changes = lines_added + lines_removed; 132 max_changes = lines_added + lines_removed;
132 total_adds += lines_added; 133 total_adds += lines_added;
133 total_rems += lines_removed; 134 total_rems += lines_removed;
134} 135}
135 136
136 137
137void cgit_print_commit(char *hex) 138void cgit_print_commit(char *hex)
138{ 139{
diff --git a/ui-diff.c b/ui-diff.c
index 5c3bc98..2a22009 100644
--- a/ui-diff.c
+++ b/ui-diff.c
@@ -1,138 +1,139 @@
1/* ui-diff.c: show diff between two blobs 1/* ui-diff.c: show diff between two blobs
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12unsigned char old_rev_sha1[20]; 13unsigned char old_rev_sha1[20];
13unsigned char new_rev_sha1[20]; 14unsigned char new_rev_sha1[20];
14 15
15/* 16/*
16 * print a single line returned from xdiff 17 * print a single line returned from xdiff
17 */ 18 */
18static void print_line(char *line, int len) 19static void print_line(char *line, int len)
19{ 20{
20 char *class = "ctx"; 21 char *class = "ctx";
21 char c = line[len-1]; 22 char c = line[len-1];
22 23
23 if (line[0] == '+') 24 if (line[0] == '+')
24 class = "add"; 25 class = "add";
25 else if (line[0] == '-') 26 else if (line[0] == '-')
26 class = "del"; 27 class = "del";
27 else if (line[0] == '@') 28 else if (line[0] == '@')
28 class = "hunk"; 29 class = "hunk";
29 30
30 htmlf("<div class='%s'>", class); 31 htmlf("<div class='%s'>", class);
31 line[len-1] = '\0'; 32 line[len-1] = '\0';
32 html_txt(line); 33 html_txt(line);
33 html("</div>"); 34 html("</div>");
34 line[len-1] = c; 35 line[len-1] = c;
35} 36}
36 37
37static void header(unsigned char *sha1, char *path1, int mode1, 38static void header(unsigned char *sha1, char *path1, int mode1,
38 unsigned char *sha2, char *path2, int mode2) 39 unsigned char *sha2, char *path2, int mode2)
39{ 40{
40 char *abbrev1, *abbrev2; 41 char *abbrev1, *abbrev2;
41 int subproject; 42 int subproject;
42 43
43 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); 44 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
44 html("<div class='head'>"); 45 html("<div class='head'>");
45 html("diff --git a/"); 46 html("diff --git a/");
46 html_txt(path1); 47 html_txt(path1);
47 html(" b/"); 48 html(" b/");
48 html_txt(path2); 49 html_txt(path2);
49 50
50 if (is_null_sha1(sha1)) 51 if (is_null_sha1(sha1))
51 path1 = "dev/null"; 52 path1 = "dev/null";
52 if (is_null_sha1(sha2)) 53 if (is_null_sha1(sha2))
53 path2 = "dev/null"; 54 path2 = "dev/null";
54 55
55 if (mode1 == 0) 56 if (mode1 == 0)
56 htmlf("<br/>new file mode %.6o", mode2); 57 htmlf("<br/>new file mode %.6o", mode2);
57 58
58 if (mode2 == 0) 59 if (mode2 == 0)
59 htmlf("<br/>deleted file mode %.6o", mode1); 60 htmlf("<br/>deleted file mode %.6o", mode1);
60 61
61 if (!subproject) { 62 if (!subproject) {
62 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); 63 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
63 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); 64 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
64 htmlf("<br/>index %s..%s", abbrev1, abbrev2); 65 htmlf("<br/>index %s..%s", abbrev1, abbrev2);
65 free(abbrev1); 66 free(abbrev1);
66 free(abbrev2); 67 free(abbrev2);
67 if (mode1 != 0 && mode2 != 0) { 68 if (mode1 != 0 && mode2 != 0) {
68 htmlf(" %.6o", mode1); 69 htmlf(" %.6o", mode1);
69 if (mode2 != mode1) 70 if (mode2 != mode1)
70 htmlf("..%.6o", mode2); 71 htmlf("..%.6o", mode2);
71 } 72 }
72 html("<br/>--- a/"); 73 html("<br/>--- a/");
73 if (mode1 != 0) 74 if (mode1 != 0)
74 cgit_tree_link(path1, NULL, NULL, ctx.qry.head, 75 cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
75 sha1_to_hex(old_rev_sha1), path1); 76 sha1_to_hex(old_rev_sha1), path1);
76 else 77 else
77 html_txt(path1); 78 html_txt(path1);
78 html("<br/>+++ b/"); 79 html("<br/>+++ b/");
79 if (mode2 != 0) 80 if (mode2 != 0)
80 cgit_tree_link(path2, NULL, NULL, ctx.qry.head, 81 cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
81 sha1_to_hex(new_rev_sha1), path2); 82 sha1_to_hex(new_rev_sha1), path2);
82 else 83 else
83 html_txt(path2); 84 html_txt(path2);
84 } 85 }
85 html("</div>"); 86 html("</div>");
86} 87}
87 88
88static void filepair_cb(struct diff_filepair *pair) 89static void filepair_cb(struct diff_filepair *pair)
89{ 90{
90 header(pair->one->sha1, pair->one->path, pair->one->mode, 91 header(pair->one->sha1, pair->one->path, pair->one->mode,
91 pair->two->sha1, pair->two->path, pair->two->mode); 92 pair->two->sha1, pair->two->path, pair->two->mode);
92 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 93 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
93 if (S_ISGITLINK(pair->one->mode)) 94 if (S_ISGITLINK(pair->one->mode))
94 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 95 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
95 if (S_ISGITLINK(pair->two->mode)) 96 if (S_ISGITLINK(pair->two->mode))
96 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 97 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
97 return; 98 return;
98 } 99 }
99 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line)) 100 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line))
100 cgit_print_error("Error running diff"); 101 cgit_print_error("Error running diff");
101} 102}
102 103
103void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix) 104void cgit_print_diff(const char *new_rev, const char *old_rev, const char *prefix)
104{ 105{
105 enum object_type type; 106 enum object_type type;
106 unsigned long size; 107 unsigned long size;
107 struct commit *commit, *commit2; 108 struct commit *commit, *commit2;
108 109
109 if (!new_rev) 110 if (!new_rev)
110 new_rev = ctx.qry.head; 111 new_rev = ctx.qry.head;
111 get_sha1(new_rev, new_rev_sha1); 112 get_sha1(new_rev, new_rev_sha1);
112 type = sha1_object_info(new_rev_sha1, &size); 113 type = sha1_object_info(new_rev_sha1, &size);
113 if (type == OBJ_BAD) { 114 if (type == OBJ_BAD) {
114 cgit_print_error(fmt("Bad object name: %s", new_rev)); 115 cgit_print_error(fmt("Bad object name: %s", new_rev));
115 return; 116 return;
116 } 117 }
117 if (type != OBJ_COMMIT) { 118 if (type != OBJ_COMMIT) {
118 cgit_print_error(fmt("Unhandled object type: %s", 119 cgit_print_error(fmt("Unhandled object type: %s",
119 typename(type))); 120 typename(type)));
120 return; 121 return;
121 } 122 }
122 123
123 commit = lookup_commit_reference(new_rev_sha1); 124 commit = lookup_commit_reference(new_rev_sha1);
124 if (!commit || parse_commit(commit)) 125 if (!commit || parse_commit(commit))
125 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1))); 126 cgit_print_error(fmt("Bad commit: %s", sha1_to_hex(new_rev_sha1)));
126 127
127 if (old_rev) 128 if (old_rev)
128 get_sha1(old_rev, old_rev_sha1); 129 get_sha1(old_rev, old_rev_sha1);
129 else if (commit->parents && commit->parents->item) 130 else if (commit->parents && commit->parents->item)
130 hashcpy(old_rev_sha1, commit->parents->item->object.sha1); 131 hashcpy(old_rev_sha1, commit->parents->item->object.sha1);
131 else 132 else
132 hashclr(old_rev_sha1); 133 hashclr(old_rev_sha1);
133 134
134 if (!is_null_sha1(old_rev_sha1)) { 135 if (!is_null_sha1(old_rev_sha1)) {
135 type = sha1_object_info(old_rev_sha1, &size); 136 type = sha1_object_info(old_rev_sha1, &size);
136 if (type == OBJ_BAD) { 137 if (type == OBJ_BAD) {
137 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1))); 138 cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(old_rev_sha1)));
138 return; 139 return;
diff --git a/ui-log.c b/ui-log.c
index e1d324d..60c9269 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -1,138 +1,139 @@
1/* ui-log.c: functions for log output 1/* ui-log.c: functions for log output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12int files, add_lines, rem_lines; 13int files, add_lines, rem_lines;
13 14
14void count_lines(char *line, int size) 15void count_lines(char *line, int size)
15{ 16{
16 if (size <= 0) 17 if (size <= 0)
17 return; 18 return;
18 19
19 if (line[0] == '+') 20 if (line[0] == '+')
20 add_lines++; 21 add_lines++;
21 22
22 else if (line[0] == '-') 23 else if (line[0] == '-')
23 rem_lines++; 24 rem_lines++;
24} 25}
25 26
26void inspect_files(struct diff_filepair *pair) 27void inspect_files(struct diff_filepair *pair)
27{ 28{
28 files++; 29 files++;
29 if (ctx.repo->enable_log_linecount) 30 if (ctx.repo->enable_log_linecount)
30 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines); 31 cgit_diff_files(pair->one->sha1, pair->two->sha1, count_lines);
31} 32}
32 33
33void print_commit(struct commit *commit) 34void print_commit(struct commit *commit)
34{ 35{
35 struct commitinfo *info; 36 struct commitinfo *info;
36 37
37 info = cgit_parse_commit(commit); 38 info = cgit_parse_commit(commit);
38 html("<tr><td>"); 39 html("<tr><td>");
39 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); 40 cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
40 html("</td><td>"); 41 html("</td><td>");
41 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, 42 cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
42 sha1_to_hex(commit->object.sha1)); 43 sha1_to_hex(commit->object.sha1));
43 if (ctx.repo->enable_log_filecount) { 44 if (ctx.repo->enable_log_filecount) {
44 files = 0; 45 files = 0;
45 add_lines = 0; 46 add_lines = 0;
46 rem_lines = 0; 47 rem_lines = 0;
47 cgit_diff_commit(commit, inspect_files); 48 cgit_diff_commit(commit, inspect_files);
48 html("</td><td class='right'>"); 49 html("</td><td class='right'>");
49 htmlf("%d", files); 50 htmlf("%d", files);
50 if (ctx.repo->enable_log_linecount) { 51 if (ctx.repo->enable_log_linecount) {
51 html("</td><td class='right'>"); 52 html("</td><td class='right'>");
52 htmlf("-%d/+%d", rem_lines, add_lines); 53 htmlf("-%d/+%d", rem_lines, add_lines);
53 } 54 }
54 } 55 }
55 html("</td><td>"); 56 html("</td><td>");
56 html_txt(info->author); 57 html_txt(info->author);
57 html("</td></tr>\n"); 58 html("</td></tr>\n");
58 cgit_free_commitinfo(info); 59 cgit_free_commitinfo(info);
59} 60}
60 61
61 62
62void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager) 63void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern, char *path, int pager)
63{ 64{
64 struct rev_info rev; 65 struct rev_info rev;
65 struct commit *commit; 66 struct commit *commit;
66 const char *argv[] = {NULL, tip, NULL, NULL, NULL}; 67 const char *argv[] = {NULL, tip, NULL, NULL, NULL};
67 int argc = 2; 68 int argc = 2;
68 int i; 69 int i;
69 70
70 if (!tip) 71 if (!tip)
71 argv[1] = ctx.qry.head; 72 argv[1] = ctx.qry.head;
72 73
73 if (grep && pattern && (!strcmp(grep, "grep") || 74 if (grep && pattern && (!strcmp(grep, "grep") ||
74 !strcmp(grep, "author") || 75 !strcmp(grep, "author") ||
75 !strcmp(grep, "committer"))) 76 !strcmp(grep, "committer")))
76 argv[argc++] = fmt("--%s=%s", grep, pattern); 77 argv[argc++] = fmt("--%s=%s", grep, pattern);
77 78
78 if (path) { 79 if (path) {
79 argv[argc++] = "--"; 80 argv[argc++] = "--";
80 argv[argc++] = path; 81 argv[argc++] = path;
81 } 82 }
82 init_revisions(&rev, NULL); 83 init_revisions(&rev, NULL);
83 rev.abbrev = DEFAULT_ABBREV; 84 rev.abbrev = DEFAULT_ABBREV;
84 rev.commit_format = CMIT_FMT_DEFAULT; 85 rev.commit_format = CMIT_FMT_DEFAULT;
85 rev.verbose_header = 1; 86 rev.verbose_header = 1;
86 rev.show_root_diff = 0; 87 rev.show_root_diff = 0;
87 setup_revisions(argc, argv, &rev, NULL); 88 setup_revisions(argc, argv, &rev, NULL);
88 if (rev.grep_filter) { 89 if (rev.grep_filter) {
89 rev.grep_filter->regflags |= REG_ICASE; 90 rev.grep_filter->regflags |= REG_ICASE;
90 compile_grep_patterns(rev.grep_filter); 91 compile_grep_patterns(rev.grep_filter);
91 } 92 }
92 prepare_revision_walk(&rev); 93 prepare_revision_walk(&rev);
93 94
94 html("<table summary='log' class='list nowrap'>"); 95 html("<table summary='log' class='list nowrap'>");
95 html("<tr class='nohover'><th class='left'>Age</th>" 96 html("<tr class='nohover'><th class='left'>Age</th>"
96 "<th class='left'>Message</th>"); 97 "<th class='left'>Message</th>");
97 98
98 if (ctx.repo->enable_log_filecount) { 99 if (ctx.repo->enable_log_filecount) {
99 html("<th class='right'>Files</th>"); 100 html("<th class='right'>Files</th>");
100 if (ctx.repo->enable_log_linecount) 101 if (ctx.repo->enable_log_linecount)
101 html("<th class='right'>Lines</th>"); 102 html("<th class='right'>Lines</th>");
102 } 103 }
103 html("<th class='left'>Author</th></tr>\n"); 104 html("<th class='left'>Author</th></tr>\n");
104 105
105 if (ofs<0) 106 if (ofs<0)
106 ofs = 0; 107 ofs = 0;
107 108
108 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) { 109 for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
109 free(commit->buffer); 110 free(commit->buffer);
110 commit->buffer = NULL; 111 commit->buffer = NULL;
111 free_commit_list(commit->parents); 112 free_commit_list(commit->parents);
112 commit->parents = NULL; 113 commit->parents = NULL;
113 } 114 }
114 115
115 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) { 116 for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
116 print_commit(commit); 117 print_commit(commit);
117 free(commit->buffer); 118 free(commit->buffer);
118 commit->buffer = NULL; 119 commit->buffer = NULL;
119 free_commit_list(commit->parents); 120 free_commit_list(commit->parents);
120 commit->parents = NULL; 121 commit->parents = NULL;
121 } 122 }
122 html("</table>\n"); 123 html("</table>\n");
123 124
124 if (pager) { 125 if (pager) {
125 html("<div class='pager'>"); 126 html("<div class='pager'>");
126 if (ofs > 0) { 127 if (ofs > 0) {
127 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head, 128 cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
128 ctx.qry.sha1, ctx.qry.path, 129 ctx.qry.sha1, ctx.qry.path,
129 ofs - cnt, ctx.qry.grep, 130 ofs - cnt, ctx.qry.grep,
130 ctx.qry.search); 131 ctx.qry.search);
131 html("&nbsp;"); 132 html("&nbsp;");
132 } 133 }
133 if ((commit = get_revision(&rev)) != NULL) { 134 if ((commit = get_revision(&rev)) != NULL) {
134 cgit_log_link("[next]", NULL, NULL, ctx.qry.head, 135 cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
135 ctx.qry.sha1, ctx.qry.path, 136 ctx.qry.sha1, ctx.qry.path,
136 ofs + cnt, ctx.qry.grep, 137 ofs + cnt, ctx.qry.grep,
137 ctx.qry.search); 138 ctx.qry.search);
138 } 139 }
diff --git a/ui-patch.c b/ui-patch.c
index 68ebb15..36bfae4 100644
--- a/ui-patch.c
+++ b/ui-patch.c
@@ -1,113 +1,114 @@
1/* ui-patch.c: generate patch view 1/* ui-patch.c: generate patch view
2 * 2 *
3 * Copyright (C) 2007 Lars Hjemli 3 * Copyright (C) 2007 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12static void print_line(char *line, int len) 13static void print_line(char *line, int len)
13{ 14{
14 char c = line[len-1]; 15 char c = line[len-1];
15 16
16 line[len-1] = '\0'; 17 line[len-1] = '\0';
17 htmlf("%s\n", line); 18 htmlf("%s\n", line);
18 line[len-1] = c; 19 line[len-1] = c;
19} 20}
20 21
21static void header(unsigned char *sha1, char *path1, int mode1, 22static void header(unsigned char *sha1, char *path1, int mode1,
22 unsigned char *sha2, char *path2, int mode2) 23 unsigned char *sha2, char *path2, int mode2)
23{ 24{
24 char *abbrev1, *abbrev2; 25 char *abbrev1, *abbrev2;
25 int subproject; 26 int subproject;
26 27
27 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); 28 subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2));
28 htmlf("diff --git a/%s b/%s\n", path1, path2); 29 htmlf("diff --git a/%s b/%s\n", path1, path2);
29 30
30 if (is_null_sha1(sha1)) 31 if (is_null_sha1(sha1))
31 path1 = "dev/null"; 32 path1 = "dev/null";
32 if (is_null_sha1(sha2)) 33 if (is_null_sha1(sha2))
33 path2 = "dev/null"; 34 path2 = "dev/null";
34 35
35 if (mode1 == 0) 36 if (mode1 == 0)
36 htmlf("new file mode %.6o\n", mode2); 37 htmlf("new file mode %.6o\n", mode2);
37 38
38 if (mode2 == 0) 39 if (mode2 == 0)
39 htmlf("deleted file mode %.6o\n", mode1); 40 htmlf("deleted file mode %.6o\n", mode1);
40 41
41 if (!subproject) { 42 if (!subproject) {
42 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); 43 abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
43 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV)); 44 abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
44 htmlf("index %s..%s", abbrev1, abbrev2); 45 htmlf("index %s..%s", abbrev1, abbrev2);
45 free(abbrev1); 46 free(abbrev1);
46 free(abbrev2); 47 free(abbrev2);
47 if (mode1 != 0 && mode2 != 0) { 48 if (mode1 != 0 && mode2 != 0) {
48 htmlf(" %.6o", mode1); 49 htmlf(" %.6o", mode1);
49 if (mode2 != mode1) 50 if (mode2 != mode1)
50 htmlf("..%.6o", mode2); 51 htmlf("..%.6o", mode2);
51 } 52 }
52 htmlf("\n--- a/%s\n", path1); 53 htmlf("\n--- a/%s\n", path1);
53 htmlf("+++ b/%s\n", path2); 54 htmlf("+++ b/%s\n", path2);
54 } 55 }
55} 56}
56 57
57static void filepair_cb(struct diff_filepair *pair) 58static void filepair_cb(struct diff_filepair *pair)
58{ 59{
59 header(pair->one->sha1, pair->one->path, pair->one->mode, 60 header(pair->one->sha1, pair->one->path, pair->one->mode,
60 pair->two->sha1, pair->two->path, pair->two->mode); 61 pair->two->sha1, pair->two->path, pair->two->mode);
61 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) { 62 if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
62 if (S_ISGITLINK(pair->one->mode)) 63 if (S_ISGITLINK(pair->one->mode))
63 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52); 64 print_line(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
64 if (S_ISGITLINK(pair->two->mode)) 65 if (S_ISGITLINK(pair->two->mode))
65 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52); 66 print_line(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
66 return; 67 return;
67 } 68 }
68 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line)) 69 if (cgit_diff_files(pair->one->sha1, pair->two->sha1, print_line))
69 html("Error running diff"); 70 html("Error running diff");
70} 71}
71 72
72void cgit_print_patch(char *hex) 73void cgit_print_patch(char *hex)
73{ 74{
74 struct commit *commit; 75 struct commit *commit;
75 struct commitinfo *info; 76 struct commitinfo *info;
76 unsigned char sha1[20], old_sha1[20]; 77 unsigned char sha1[20], old_sha1[20];
77 char *patchname; 78 char *patchname;
78 79
79 if (!hex) 80 if (!hex)
80 hex = ctx.qry.head; 81 hex = ctx.qry.head;
81 82
82 if (get_sha1(hex, sha1)) { 83 if (get_sha1(hex, sha1)) {
83 cgit_print_error(fmt("Bad object id: %s", hex)); 84 cgit_print_error(fmt("Bad object id: %s", hex));
84 return; 85 return;
85 } 86 }
86 commit = lookup_commit_reference(sha1); 87 commit = lookup_commit_reference(sha1);
87 if (!commit) { 88 if (!commit) {
88 cgit_print_error(fmt("Bad commit reference: %s", hex)); 89 cgit_print_error(fmt("Bad commit reference: %s", hex));
89 return; 90 return;
90 } 91 }
91 info = cgit_parse_commit(commit); 92 info = cgit_parse_commit(commit);
92 hashcpy(old_sha1, commit->parents->item->object.sha1); 93 hashcpy(old_sha1, commit->parents->item->object.sha1);
93 94
94 patchname = fmt("%s.patch", sha1_to_hex(sha1)); 95 patchname = fmt("%s.patch", sha1_to_hex(sha1));
95 ctx.page.mimetype = "text/plain"; 96 ctx.page.mimetype = "text/plain";
96 ctx.page.filename = patchname; 97 ctx.page.filename = patchname;
97 cgit_print_http_headers(&ctx); 98 cgit_print_http_headers(&ctx);
98 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1)); 99 htmlf("From %s Mon Sep 17 00:00:00 2001\n", sha1_to_hex(sha1));
99 htmlf("From: %s%s\n", info->author, info->author_email); 100 htmlf("From: %s%s\n", info->author, info->author_email);
100 html("Date: "); 101 html("Date: ");
101 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n"); 102 cgit_print_date(info->author_date, "%a, %d %b %Y %H:%M:%S %z%n");
102 htmlf("Subject: %s\n\n", info->subject); 103 htmlf("Subject: %s\n\n", info->subject);
103 if (info->msg && *info->msg) { 104 if (info->msg && *info->msg) {
104 htmlf("%s", info->msg); 105 htmlf("%s", info->msg);
105 if (info->msg[strlen(info->msg) - 1] != '\n') 106 if (info->msg[strlen(info->msg) - 1] != '\n')
106 html("\n"); 107 html("\n");
107 } 108 }
108 html("---\n"); 109 html("---\n");
109 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL); 110 cgit_diff_tree(old_sha1, sha1, filepair_cb, NULL);
110 html("--\n"); 111 html("--\n");
111 htmlf("cgit %s\n", CGIT_VERSION); 112 htmlf("cgit %s\n", CGIT_VERSION);
112 cgit_free_commitinfo(info); 113 cgit_free_commitinfo(info);
113} 114}
diff --git a/ui-refs.c b/ui-refs.c
index fc82ca7..12533cd 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -1,138 +1,139 @@
1/* ui-refs.c: browse symbolic refs 1/* ui-refs.c: browse symbolic refs
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12static int header; 13static int header;
13 14
14static int cmp_age(int age1, int age2) 15static int cmp_age(int age1, int age2)
15{ 16{
16 if (age1 != 0 && age2 != 0) 17 if (age1 != 0 && age2 != 0)
17 return age2 - age1; 18 return age2 - age1;
18 19
19 if (age1 == 0 && age2 == 0) 20 if (age1 == 0 && age2 == 0)
20 return 0; 21 return 0;
21 22
22 if (age1 == 0) 23 if (age1 == 0)
23 return +1; 24 return +1;
24 25
25 return -1; 26 return -1;
26} 27}
27 28
28static int cmp_ref_name(const void *a, const void *b) 29static int cmp_ref_name(const void *a, const void *b)
29{ 30{
30 struct refinfo *r1 = *(struct refinfo **)a; 31 struct refinfo *r1 = *(struct refinfo **)a;
31 struct refinfo *r2 = *(struct refinfo **)b; 32 struct refinfo *r2 = *(struct refinfo **)b;
32 33
33 return strcmp(r1->refname, r2->refname); 34 return strcmp(r1->refname, r2->refname);
34} 35}
35 36
36static int cmp_branch_age(const void *a, const void *b) 37static int cmp_branch_age(const void *a, const void *b)
37{ 38{
38 struct refinfo *r1 = *(struct refinfo **)a; 39 struct refinfo *r1 = *(struct refinfo **)a;
39 struct refinfo *r2 = *(struct refinfo **)b; 40 struct refinfo *r2 = *(struct refinfo **)b;
40 41
41 return cmp_age(r1->commit->committer_date, r2->commit->committer_date); 42 return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
42} 43}
43 44
44static int cmp_tag_age(const void *a, const void *b) 45static int cmp_tag_age(const void *a, const void *b)
45{ 46{
46 struct refinfo *r1 = *(struct refinfo **)a; 47 struct refinfo *r1 = *(struct refinfo **)a;
47 struct refinfo *r2 = *(struct refinfo **)b; 48 struct refinfo *r2 = *(struct refinfo **)b;
48 49
49 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date); 50 return cmp_age(r1->tag->tagger_date, r2->tag->tagger_date);
50} 51}
51 52
52static int print_branch(struct refinfo *ref) 53static int print_branch(struct refinfo *ref)
53{ 54{
54 struct commitinfo *info = ref->commit; 55 struct commitinfo *info = ref->commit;
55 char *name = (char *)ref->refname; 56 char *name = (char *)ref->refname;
56 57
57 if (!info) 58 if (!info)
58 return 1; 59 return 1;
59 html("<tr><td>"); 60 html("<tr><td>");
60 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL); 61 cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL);
61 html("</td><td>"); 62 html("</td><td>");
62 63
63 if (ref->object->type == OBJ_COMMIT) { 64 if (ref->object->type == OBJ_COMMIT) {
64 cgit_print_age(info->commit->date, -1, NULL); 65 cgit_print_age(info->commit->date, -1, NULL);
65 html("</td><td>"); 66 html("</td><td>");
66 html_txt(info->author); 67 html_txt(info->author);
67 html("</td><td>"); 68 html("</td><td>");
68 cgit_commit_link(info->subject, NULL, NULL, name, NULL); 69 cgit_commit_link(info->subject, NULL, NULL, name, NULL);
69 } else { 70 } else {
70 html("</td><td></td><td>"); 71 html("</td><td></td><td>");
71 cgit_object_link(ref->object); 72 cgit_object_link(ref->object);
72 } 73 }
73 html("</td></tr>\n"); 74 html("</td></tr>\n");
74 return 0; 75 return 0;
75} 76}
76 77
77static void print_tag_header() 78static void print_tag_header()
78{ 79{
79 html("<tr class='nohover'><th class='left'>Tag</th>" 80 html("<tr class='nohover'><th class='left'>Tag</th>"
80 "<th class='left'>Age</th>" 81 "<th class='left'>Age</th>"
81 "<th class='left'>Author</th>" 82 "<th class='left'>Author</th>"
82 "<th class='left'>Reference</th></tr>\n"); 83 "<th class='left'>Reference</th></tr>\n");
83 header = 1; 84 header = 1;
84} 85}
85 86
86static int print_tag(struct refinfo *ref) 87static int print_tag(struct refinfo *ref)
87{ 88{
88 struct tag *tag; 89 struct tag *tag;
89 struct taginfo *info; 90 struct taginfo *info;
90 char *url, *name = (char *)ref->refname; 91 char *url, *name = (char *)ref->refname;
91 92
92 if (ref->object->type == OBJ_TAG) { 93 if (ref->object->type == OBJ_TAG) {
93 tag = (struct tag *)ref->object; 94 tag = (struct tag *)ref->object;
94 info = ref->tag; 95 info = ref->tag;
95 if (!tag || !info) 96 if (!tag || !info)
96 return 1; 97 return 1;
97 html("<tr><td>"); 98 html("<tr><td>");
98 url = cgit_pageurl(ctx.qry.repo, "tag", 99 url = cgit_pageurl(ctx.qry.repo, "tag",
99 fmt("id=%s", name)); 100 fmt("id=%s", name));
100 html_link_open(url, NULL, NULL); 101 html_link_open(url, NULL, NULL);
101 html_txt(name); 102 html_txt(name);
102 html_link_close(); 103 html_link_close();
103 html("</td><td>"); 104 html("</td><td>");
104 if (info->tagger_date > 0) 105 if (info->tagger_date > 0)
105 cgit_print_age(info->tagger_date, -1, NULL); 106 cgit_print_age(info->tagger_date, -1, NULL);
106 html("</td><td>"); 107 html("</td><td>");
107 if (info->tagger) 108 if (info->tagger)
108 html(info->tagger); 109 html(info->tagger);
109 html("</td><td>"); 110 html("</td><td>");
110 cgit_object_link(tag->tagged); 111 cgit_object_link(tag->tagged);
111 html("</td></tr>\n"); 112 html("</td></tr>\n");
112 } else { 113 } else {
113 if (!header) 114 if (!header)
114 print_tag_header(); 115 print_tag_header();
115 html("<tr><td>"); 116 html("<tr><td>");
116 html_txt(name); 117 html_txt(name);
117 html("</td><td colspan='2'/><td>"); 118 html("</td><td colspan='2'/><td>");
118 cgit_object_link(ref->object); 119 cgit_object_link(ref->object);
119 html("</td></tr>\n"); 120 html("</td></tr>\n");
120 } 121 }
121 return 0; 122 return 0;
122} 123}
123 124
124static void print_refs_link(char *path) 125static void print_refs_link(char *path)
125{ 126{
126 html("<tr class='nohover'><td colspan='4'>"); 127 html("<tr class='nohover'><td colspan='4'>");
127 cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path); 128 cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path);
128 html("</td></tr>"); 129 html("</td></tr>");
129} 130}
130 131
131void cgit_print_branches(int maxcount) 132void cgit_print_branches(int maxcount)
132{ 133{
133 struct reflist list; 134 struct reflist list;
134 int i; 135 int i;
135 136
136 html("<tr class='nohover'><th class='left'>Branch</th>" 137 html("<tr class='nohover'><th class='left'>Branch</th>"
137 "<th class='left'>Idle</th>" 138 "<th class='left'>Idle</th>"
138 "<th class='left'>Author</th>" 139 "<th class='left'>Author</th>"
diff --git a/ui-repolist.c b/ui-repolist.c
index ad9b1bc..eeeaf3d 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -1,113 +1,114 @@
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 14
14time_t read_agefile(char *path) 15time_t read_agefile(char *path)
15{ 16{
16 FILE *f; 17 FILE *f;
17 static char buf[64], buf2[64]; 18 static char buf[64], buf2[64];
18 19
19 if (!(f = fopen(path, "r"))) 20 if (!(f = fopen(path, "r")))
20 return -1; 21 return -1;
21 fgets(buf, sizeof(buf), f); 22 fgets(buf, sizeof(buf), f);
22 fclose(f); 23 fclose(f);
23 if (parse_date(buf, buf2, sizeof(buf2))) 24 if (parse_date(buf, buf2, sizeof(buf2)))
24 return strtoul(buf2, NULL, 10); 25 return strtoul(buf2, NULL, 10);
25 else 26 else
26 return 0; 27 return 0;
27} 28}
28 29
29static void print_modtime(struct cgit_repo *repo) 30static void print_modtime(struct cgit_repo *repo)
30{ 31{
31 char *path; 32 char *path;
32 struct stat s; 33 struct stat s;
33 34
34 path = fmt("%s/%s", repo->path, ctx.cfg.agefile); 35 path = fmt("%s/%s", repo->path, ctx.cfg.agefile);
35 if (stat(path, &s) == 0) { 36 if (stat(path, &s) == 0) {
36 cgit_print_age(read_agefile(path), -1, NULL); 37 cgit_print_age(read_agefile(path), -1, NULL);
37 return; 38 return;
38 } 39 }
39 40
40 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch); 41 path = fmt("%s/refs/heads/%s", repo->path, repo->defbranch);
41 if (stat(path, &s) != 0) 42 if (stat(path, &s) != 0)
42 return; 43 return;
43 cgit_print_age(s.st_mtime, -1, NULL); 44 cgit_print_age(s.st_mtime, -1, NULL);
44} 45}
45 46
46void cgit_print_repolist() 47void cgit_print_repolist()
47{ 48{
48 int i, columns = 4; 49 int i, columns = 4;
49 char *last_group = NULL; 50 char *last_group = NULL;
50 51
51 if (ctx.cfg.enable_index_links) 52 if (ctx.cfg.enable_index_links)
52 columns++; 53 columns++;
53 54
54 ctx.page.title = ctx.cfg.root_title; 55 ctx.page.title = ctx.cfg.root_title;
55 cgit_print_http_headers(&ctx); 56 cgit_print_http_headers(&ctx);
56 cgit_print_docstart(&ctx); 57 cgit_print_docstart(&ctx);
57 cgit_print_pageheader(&ctx); 58 cgit_print_pageheader(&ctx);
58 59
59 html("<table summary='repository list' class='list nowrap'>"); 60 html("<table summary='repository list' class='list nowrap'>");
60 if (ctx.cfg.index_header) { 61 if (ctx.cfg.index_header) {
61 htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>", 62 htmlf("<tr class='nohover'><td colspan='%d' class='include-block'>",
62 columns); 63 columns);
63 html_include(ctx.cfg.index_header); 64 html_include(ctx.cfg.index_header);
64 html("</td></tr>"); 65 html("</td></tr>");
65 } 66 }
66 html("<tr class='nohover'>" 67 html("<tr class='nohover'>"
67 "<th class='left'>Name</th>" 68 "<th class='left'>Name</th>"
68 "<th class='left'>Description</th>" 69 "<th class='left'>Description</th>"
69 "<th class='left'>Owner</th>" 70 "<th class='left'>Owner</th>"
70 "<th class='left'>Idle</th>"); 71 "<th class='left'>Idle</th>");
71 if (ctx.cfg.enable_index_links) 72 if (ctx.cfg.enable_index_links)
72 html("<th>Links</th>"); 73 html("<th>Links</th>");
73 html("</tr>\n"); 74 html("</tr>\n");
74 75
75 for (i=0; i<cgit_repolist.count; i++) { 76 for (i=0; i<cgit_repolist.count; i++) {
76 ctx.repo = &cgit_repolist.repos[i]; 77 ctx.repo = &cgit_repolist.repos[i];
77 if ((last_group == NULL && ctx.repo->group != NULL) || 78 if ((last_group == NULL && ctx.repo->group != NULL) ||
78 (last_group != NULL && ctx.repo->group == NULL) || 79 (last_group != NULL && ctx.repo->group == NULL) ||
79 (last_group != NULL && ctx.repo->group != NULL && 80 (last_group != NULL && ctx.repo->group != NULL &&
80 strcmp(ctx.repo->group, last_group))) { 81 strcmp(ctx.repo->group, last_group))) {
81 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", 82 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
82 columns); 83 columns);
83 html_txt(ctx.repo->group); 84 html_txt(ctx.repo->group);
84 html("</td></tr>"); 85 html("</td></tr>");
85 last_group = ctx.repo->group; 86 last_group = ctx.repo->group;
86 } 87 }
87 htmlf("<tr><td class='%s'>", 88 htmlf("<tr><td class='%s'>",
88 ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); 89 ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
89 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); 90 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
90 html_txt(ctx.repo->name); 91 html_txt(ctx.repo->name);
91 html_link_close(); 92 html_link_close();
92 html("</td><td>"); 93 html("</td><td>");
93 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); 94 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
94 html("</td><td>"); 95 html("</td><td>");
95 html_txt(ctx.repo->owner); 96 html_txt(ctx.repo->owner);
96 html("</td><td>"); 97 html("</td><td>");
97 print_modtime(ctx.repo); 98 print_modtime(ctx.repo);
98 html("</td>"); 99 html("</td>");
99 if (ctx.cfg.enable_index_links) { 100 if (ctx.cfg.enable_index_links) {
100 html("<td>"); 101 html("<td>");
101 html_link_open(cgit_repourl(ctx.repo->url), 102 html_link_open(cgit_repourl(ctx.repo->url),
102 NULL, "button"); 103 NULL, "button");
103 html("summary</a>"); 104 html("summary</a>");
104 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 105 cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
105 0, NULL, NULL); 106 0, NULL, NULL);
106 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); 107 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
107 html("</td>"); 108 html("</td>");
108 } 109 }
109 html("</tr>\n"); 110 html("</tr>\n");
110 } 111 }
111 html("</table>"); 112 html("</table>");
112 cgit_print_docend(); 113 cgit_print_docend();
113} 114}
diff --git a/ui-shared.h b/ui-shared.h
new file mode 100644
index 0000000..94de884
--- a/dev/null
+++ b/ui-shared.h
@@ -0,0 +1,36 @@
1#ifndef UI_SHARED_H
2#define UI_SHARED_H
3
4extern char *cgit_repourl(const char *reponame);
5extern char *cgit_fileurl(const char *reponame, const char *pagename,
6 const char *filename, const char *query);
7extern char *cgit_pageurl(const char *reponame, const char *pagename,
8 const char *query);
9
10extern void cgit_tree_link(char *name, char *title, char *class, char *head,
11 char *rev, char *path);
12extern void cgit_log_link(char *name, char *title, char *class, char *head,
13 char *rev, char *path, int ofs, char *grep,
14 char *pattern);
15extern void cgit_commit_link(char *name, char *title, char *class, char *head,
16 char *rev);
17extern void cgit_refs_link(char *name, char *title, char *class, char *head,
18 char *rev, char *path);
19extern void cgit_snapshot_link(char *name, char *title, char *class,
20 char *head, char *rev, char *archivename);
21extern void cgit_diff_link(char *name, char *title, char *class, char *head,
22 char *new_rev, char *old_rev, char *path);
23extern void cgit_object_link(struct object *obj);
24
25extern void cgit_print_error(char *msg);
26extern void cgit_print_date(time_t secs, char *format);
27extern void cgit_print_age(time_t t, time_t max_relative, char *format);
28extern void cgit_print_http_headers(struct cgit_context *ctx);
29extern void cgit_print_docstart(struct cgit_context *ctx);
30extern void cgit_print_docend();
31extern void cgit_print_pageheader(struct cgit_context *ctx);
32extern void cgit_print_filemode(unsigned short mode);
33extern void cgit_print_snapshot_links(const char *repo, const char *head,
34 const char *hex, int snapshots);
35
36#endif /* UI_SHARED_H */
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 512fcd2..966a140 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -1,113 +1,114 @@
1/* ui-snapshot.c: generate snapshot of a commit 1/* ui-snapshot.c: generate snapshot of a commit
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12static int write_compressed_tar_archive(struct archiver_args *args,const char *filter) 13static int write_compressed_tar_archive(struct archiver_args *args,const char *filter)
13{ 14{
14 int rw[2]; 15 int rw[2];
15 pid_t gzpid; 16 pid_t gzpid;
16 int stdout2; 17 int stdout2;
17 int status; 18 int status;
18 int rv; 19 int rv;
19 20
20 stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing"); 21 stdout2 = chk_non_negative(dup(STDIN_FILENO), "Preserving STDOUT before compressing");
21 chk_zero(pipe(rw), "Opening pipe from compressor subprocess"); 22 chk_zero(pipe(rw), "Opening pipe from compressor subprocess");
22 gzpid = chk_non_negative(fork(), "Forking compressor subprocess"); 23 gzpid = chk_non_negative(fork(), "Forking compressor subprocess");
23 if(gzpid==0) { 24 if(gzpid==0) {
24 /* child */ 25 /* child */
25 chk_zero(close(rw[1]), "Closing write end of pipe in child"); 26 chk_zero(close(rw[1]), "Closing write end of pipe in child");
26 chk_zero(close(STDIN_FILENO), "Closing STDIN"); 27 chk_zero(close(STDIN_FILENO), "Closing STDIN");
27 chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin"); 28 chk_non_negative(dup2(rw[0],STDIN_FILENO), "Redirecting compressor input to stdin");
28 execlp(filter,filter,NULL); 29 execlp(filter,filter,NULL);
29 _exit(-1); 30 _exit(-1);
30 } 31 }
31 /* parent */ 32 /* parent */
32 chk_zero(close(rw[0]), "Closing read end of pipe"); 33 chk_zero(close(rw[0]), "Closing read end of pipe");
33 chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor"); 34 chk_non_negative(dup2(rw[1],STDOUT_FILENO), "Redirecting output to compressor");
34 35
35 rv = write_tar_archive(args); 36 rv = write_tar_archive(args);
36 37
37 chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor"); 38 chk_zero(close(STDOUT_FILENO), "Closing STDOUT redirected to compressor");
38 chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT"); 39 chk_non_negative(dup2(stdout2,STDOUT_FILENO), "Restoring uncompressed STDOUT");
39 chk_zero(close(stdout2), "Closing uncompressed STDOUT"); 40 chk_zero(close(stdout2), "Closing uncompressed STDOUT");
40 chk_zero(close(rw[1]), "Closing write end of pipe in parent"); 41 chk_zero(close(rw[1]), "Closing write end of pipe in parent");
41 chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process"); 42 chk_positive(waitpid(gzpid,&status,0), "Waiting on compressor process");
42 if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) ) 43 if(! ( WIFEXITED(status) && WEXITSTATUS(status)==0 ) )
43 cgit_print_error("Failed to compress archive"); 44 cgit_print_error("Failed to compress archive");
44 45
45 return rv; 46 return rv;
46} 47}
47 48
48static int write_tar_gzip_archive(struct archiver_args *args) 49static int write_tar_gzip_archive(struct archiver_args *args)
49{ 50{
50 return write_compressed_tar_archive(args,"gzip"); 51 return write_compressed_tar_archive(args,"gzip");
51} 52}
52 53
53static int write_tar_bzip2_archive(struct archiver_args *args) 54static int write_tar_bzip2_archive(struct archiver_args *args)
54{ 55{
55 return write_compressed_tar_archive(args,"bzip2"); 56 return write_compressed_tar_archive(args,"bzip2");
56} 57}
57 58
58const struct cgit_snapshot_format cgit_snapshot_formats[] = { 59const struct cgit_snapshot_format cgit_snapshot_formats[] = {
59 { ".zip", "application/x-zip", write_zip_archive, 0x1 }, 60 { ".zip", "application/x-zip", write_zip_archive, 0x1 },
60 { ".tar.gz", "application/x-tar", write_tar_gzip_archive, 0x2 }, 61 { ".tar.gz", "application/x-tar", write_tar_gzip_archive, 0x2 },
61 { ".tar.bz2", "application/x-tar", write_tar_bzip2_archive, 0x4 }, 62 { ".tar.bz2", "application/x-tar", write_tar_bzip2_archive, 0x4 },
62 { ".tar", "application/x-tar", write_tar_archive, 0x8 }, 63 { ".tar", "application/x-tar", write_tar_archive, 0x8 },
63 {} 64 {}
64}; 65};
65 66
66static int make_snapshot(const struct cgit_snapshot_format *format, 67static int make_snapshot(const struct cgit_snapshot_format *format,
67 const char *hex, const char *prefix, 68 const char *hex, const char *prefix,
68 const char *filename) 69 const char *filename)
69{ 70{
70 struct archiver_args args; 71 struct archiver_args args;
71 struct commit *commit; 72 struct commit *commit;
72 unsigned char sha1[20]; 73 unsigned char sha1[20];
73 74
74 if(get_sha1(hex, sha1)) { 75 if(get_sha1(hex, sha1)) {
75 cgit_print_error(fmt("Bad object id: %s", hex)); 76 cgit_print_error(fmt("Bad object id: %s", hex));
76 return 1; 77 return 1;
77 } 78 }
78 commit = lookup_commit_reference(sha1); 79 commit = lookup_commit_reference(sha1);
79 if(!commit) { 80 if(!commit) {
80 cgit_print_error(fmt("Not a commit reference: %s", hex)); 81 cgit_print_error(fmt("Not a commit reference: %s", hex));
81 return 1; 82 return 1;
82 } 83 }
83 memset(&args, 0, sizeof(args)); 84 memset(&args, 0, sizeof(args));
84 args.base = fmt("%s/", prefix); 85 args.base = fmt("%s/", prefix);
85 args.tree = commit->tree; 86 args.tree = commit->tree;
86 args.time = commit->date; 87 args.time = commit->date;
87 ctx.page.mimetype = xstrdup(format->mimetype); 88 ctx.page.mimetype = xstrdup(format->mimetype);
88 ctx.page.filename = xstrdup(filename); 89 ctx.page.filename = xstrdup(filename);
89 cgit_print_http_headers(&ctx); 90 cgit_print_http_headers(&ctx);
90 format->write_func(&args); 91 format->write_func(&args);
91 return 0; 92 return 0;
92} 93}
93 94
94void cgit_print_snapshot(const char *head, const char *hex, const char *prefix, 95void cgit_print_snapshot(const char *head, const char *hex, const char *prefix,
95 const char *filename, int snapshots) 96 const char *filename, int snapshots)
96{ 97{
97 const struct cgit_snapshot_format* f; 98 const struct cgit_snapshot_format* f;
98 int sl, fnl; 99 int sl, fnl;
99 100
100 fnl = strlen(filename); 101 fnl = strlen(filename);
101 if (!hex) 102 if (!hex)
102 hex = head; 103 hex = head;
103 for (f = cgit_snapshot_formats; f->suffix; f++) { 104 for (f = cgit_snapshot_formats; f->suffix; f++) {
104 if (!(snapshots & f->bit)) 105 if (!(snapshots & f->bit))
105 continue; 106 continue;
106 sl = strlen(f->suffix); 107 sl = strlen(f->suffix);
107 if(fnl < sl || strcmp(&filename[fnl-sl], f->suffix)) 108 if(fnl < sl || strcmp(&filename[fnl-sl], f->suffix))
108 continue; 109 continue;
109 make_snapshot(f, hex, prefix, filename); 110 make_snapshot(f, hex, prefix, filename);
110 return; 111 return;
111 } 112 }
112 cgit_print_error(fmt("Unsupported snapshot format: %s", filename)); 113 cgit_print_error(fmt("Unsupported snapshot format: %s", filename));
113} 114}
diff --git a/ui-tag.c b/ui-tag.c
index 2998d02..ab2c66d 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -1,74 +1,75 @@
1/* ui-tag.c: display a tag 1/* ui-tag.c: display a tag
2 * 2 *
3 * Copyright (C) 2007 Lars Hjemli 3 * Copyright (C) 2007 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12static void print_tag_content(char *buf) 13static void print_tag_content(char *buf)
13{ 14{
14 char *p; 15 char *p;
15 16
16 if (!buf) 17 if (!buf)
17 return; 18 return;
18 19
19 html("<div class='commit-subject'>"); 20 html("<div class='commit-subject'>");
20 p = strchr(buf, '\n'); 21 p = strchr(buf, '\n');
21 if (p) 22 if (p)
22 *p = '\0'; 23 *p = '\0';
23 html_txt(buf); 24 html_txt(buf);
24 html("</div>"); 25 html("</div>");
25 if (p) { 26 if (p) {
26 html("<div class='commit-msg'>"); 27 html("<div class='commit-msg'>");
27 html_txt(++p); 28 html_txt(++p);
28 html("</div>"); 29 html("</div>");
29 } 30 }
30} 31}
31 32
32void cgit_print_tag(char *revname) 33void cgit_print_tag(char *revname)
33{ 34{
34 unsigned char sha1[20]; 35 unsigned char sha1[20];
35 struct object *obj; 36 struct object *obj;
36 struct tag *tag; 37 struct tag *tag;
37 struct taginfo *info; 38 struct taginfo *info;
38 39
39 if (get_sha1(revname, sha1)) { 40 if (get_sha1(revname, sha1)) {
40 cgit_print_error(fmt("Bad tag reference: %s", revname)); 41 cgit_print_error(fmt("Bad tag reference: %s", revname));
41 return; 42 return;
42 } 43 }
43 obj = parse_object(sha1); 44 obj = parse_object(sha1);
44 if (!obj) { 45 if (!obj) {
45 cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1))); 46 cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1)));
46 return; 47 return;
47 } 48 }
48 if (obj->type == OBJ_TAG) { 49 if (obj->type == OBJ_TAG) {
49 tag = lookup_tag(sha1); 50 tag = lookup_tag(sha1);
50 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { 51 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
51 cgit_print_error(fmt("Bad tag object: %s", revname)); 52 cgit_print_error(fmt("Bad tag object: %s", revname));
52 return; 53 return;
53 } 54 }
54 html("<table class='commit-info'>\n"); 55 html("<table class='commit-info'>\n");
55 htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n", 56 htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n",
56 revname, sha1_to_hex(sha1)); 57 revname, sha1_to_hex(sha1));
57 if (info->tagger_date > 0) { 58 if (info->tagger_date > 0) {
58 html("<tr><td>Tag date</td><td>"); 59 html("<tr><td>Tag date</td><td>");
59 cgit_print_date(info->tagger_date, FMT_LONGDATE); 60 cgit_print_date(info->tagger_date, FMT_LONGDATE);
60 html("</td></tr>\n"); 61 html("</td></tr>\n");
61 } 62 }
62 if (info->tagger) { 63 if (info->tagger) {
63 html("<tr><td>Tagged by</td><td>"); 64 html("<tr><td>Tagged by</td><td>");
64 html_txt(info->tagger); 65 html_txt(info->tagger);
65 html("</td></tr>\n"); 66 html("</td></tr>\n");
66 } 67 }
67 html("<tr><td>Tagged object</td><td>"); 68 html("<tr><td>Tagged object</td><td>");
68 cgit_object_link(tag->tagged); 69 cgit_object_link(tag->tagged);
69 html("</td></tr>\n"); 70 html("</td></tr>\n");
70 html("</table>\n"); 71 html("</table>\n");
71 print_tag_content(info->msg); 72 print_tag_content(info->msg);
72 } 73 }
73 return; 74 return;
74} 75}
diff --git a/ui-tree.c b/ui-tree.c
index 7912784..9be3140 100644
--- a/ui-tree.c
+++ b/ui-tree.c
@@ -1,138 +1,139 @@
1/* ui-tree.c: functions for tree output 1/* ui-tree.c: functions for tree output
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h"
11 12
12char *curr_rev; 13char *curr_rev;
13char *match_path; 14char *match_path;
14int header = 0; 15int header = 0;
15 16
16static void print_object(const unsigned char *sha1, char *path) 17static void print_object(const unsigned char *sha1, char *path)
17{ 18{
18 enum object_type type; 19 enum object_type type;
19 char *buf; 20 char *buf;
20 unsigned long size, lineno, start, idx; 21 unsigned long size, lineno, start, idx;
21 const char *linefmt = "<tr><td class='no'><a id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a></td><td class='txt'>"; 22 const char *linefmt = "<tr><td class='no'><a id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a></td><td class='txt'>";
22 23
23 type = sha1_object_info(sha1, &size); 24 type = sha1_object_info(sha1, &size);
24 if (type == OBJ_BAD) { 25 if (type == OBJ_BAD) {
25 cgit_print_error(fmt("Bad object name: %s", 26 cgit_print_error(fmt("Bad object name: %s",
26 sha1_to_hex(sha1))); 27 sha1_to_hex(sha1)));
27 return; 28 return;
28 } 29 }
29 30
30 buf = read_sha1_file(sha1, &type, &size); 31 buf = read_sha1_file(sha1, &type, &size);
31 if (!buf) { 32 if (!buf) {
32 cgit_print_error(fmt("Error reading object %s", 33 cgit_print_error(fmt("Error reading object %s",
33 sha1_to_hex(sha1))); 34 sha1_to_hex(sha1)));
34 return; 35 return;
35 } 36 }
36 37
37 html(" blob: <a href='"); 38 html(" blob: <a href='");
38 html_attr(cgit_pageurl(ctx.qry.repo, "blob", fmt("id=%s", sha1_to_hex(sha1)))); 39 html_attr(cgit_pageurl(ctx.qry.repo, "blob", fmt("id=%s", sha1_to_hex(sha1))));
39 htmlf("'>%s</a>",sha1_to_hex(sha1)); 40 htmlf("'>%s</a>",sha1_to_hex(sha1));
40 41
41 html("<table summary='blob content' class='blob'>\n"); 42 html("<table summary='blob content' class='blob'>\n");
42 idx = 0; 43 idx = 0;
43 start = 0; 44 start = 0;
44 lineno = 0; 45 lineno = 0;
45 while(idx < size) { 46 while(idx < size) {
46 if (buf[idx] == '\n') { 47 if (buf[idx] == '\n') {
47 buf[idx] = '\0'; 48 buf[idx] = '\0';
48 htmlf(linefmt, ++lineno); 49 htmlf(linefmt, ++lineno);
49 html_txt(buf + start); 50 html_txt(buf + start);
50 html("</td></tr>\n"); 51 html("</td></tr>\n");
51 start = idx + 1; 52 start = idx + 1;
52 } 53 }
53 idx++; 54 idx++;
54 } 55 }
55 htmlf(linefmt, ++lineno); 56 htmlf(linefmt, ++lineno);
56 html_txt(buf + start); 57 html_txt(buf + start);
57 html("</td></tr>\n"); 58 html("</td></tr>\n");
58 html("</table>\n"); 59 html("</table>\n");
59} 60}
60 61
61 62
62static int ls_item(const unsigned char *sha1, const char *base, int baselen, 63static int ls_item(const unsigned char *sha1, const char *base, int baselen,
63 const char *pathname, unsigned int mode, int stage) 64 const char *pathname, unsigned int mode, int stage)
64{ 65{
65 char *name; 66 char *name;
66 char *fullpath; 67 char *fullpath;
67 enum object_type type; 68 enum object_type type;
68 unsigned long size = 0; 69 unsigned long size = 0;
69 70
70 name = xstrdup(pathname); 71 name = xstrdup(pathname);
71 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", 72 fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "",
72 ctx.qry.path ? "/" : "", name); 73 ctx.qry.path ? "/" : "", name);
73 74
74 type = sha1_object_info(sha1, &size); 75 type = sha1_object_info(sha1, &size);
75 if (type == OBJ_BAD && !S_ISGITLINK(mode)) { 76 if (type == OBJ_BAD && !S_ISGITLINK(mode)) {
76 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", 77 htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
77 name, 78 name,
78 sha1_to_hex(sha1)); 79 sha1_to_hex(sha1));
79 return 0; 80 return 0;
80 } 81 }
81 82
82 html("<tr><td class='ls-mode'>"); 83 html("<tr><td class='ls-mode'>");
83 cgit_print_filemode(mode); 84 cgit_print_filemode(mode);
84 html("</td><td>"); 85 html("</td><td>");
85 if (S_ISGITLINK(mode)) { 86 if (S_ISGITLINK(mode)) {
86 htmlf("<a class='ls-mod' href='"); 87 htmlf("<a class='ls-mod' href='");
87 html_attr(fmt(ctx.repo->module_link, 88 html_attr(fmt(ctx.repo->module_link,
88 name, 89 name,
89 sha1_to_hex(sha1))); 90 sha1_to_hex(sha1)));
90 html("'>"); 91 html("'>");
91 html_txt(name); 92 html_txt(name);
92 html("</a>"); 93 html("</a>");
93 } else if (S_ISDIR(mode)) { 94 } else if (S_ISDIR(mode)) {
94 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, 95 cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
95 curr_rev, fullpath); 96 curr_rev, fullpath);
96 } else { 97 } else {
97 cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, 98 cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head,
98 curr_rev, fullpath); 99 curr_rev, fullpath);
99 } 100 }
100 htmlf("</td><td class='ls-size'>%li</td>", size); 101 htmlf("</td><td class='ls-size'>%li</td>", size);
101 102
102 html("<td>"); 103 html("<td>");
103 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, 104 cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev,
104 fullpath, 0, NULL, NULL); 105 fullpath, 0, NULL, NULL);
105 html("</td></tr>\n"); 106 html("</td></tr>\n");
106 free(name); 107 free(name);
107 return 0; 108 return 0;
108} 109}
109 110
110static void ls_head() 111static void ls_head()
111{ 112{
112 html("<table summary='tree listing' class='list'>\n"); 113 html("<table summary='tree listing' class='list'>\n");
113 html("<tr class='nohover'>"); 114 html("<tr class='nohover'>");
114 html("<th class='left'>Mode</th>"); 115 html("<th class='left'>Mode</th>");
115 html("<th class='left'>Name</th>"); 116 html("<th class='left'>Name</th>");
116 html("<th class='right'>Size</th>"); 117 html("<th class='right'>Size</th>");
117 html("<th/>"); 118 html("<th/>");
118 html("</tr>\n"); 119 html("</tr>\n");
119 header = 1; 120 header = 1;
120} 121}
121 122
122static void ls_tail() 123static void ls_tail()
123{ 124{
124 if (!header) 125 if (!header)
125 return; 126 return;
126 html("</table>\n"); 127 html("</table>\n");
127 header = 0; 128 header = 0;
128} 129}
129 130
130static void ls_tree(const unsigned char *sha1, char *path) 131static void ls_tree(const unsigned char *sha1, char *path)
131{ 132{
132 struct tree *tree; 133 struct tree *tree;
133 134
134 tree = parse_tree_indirect(sha1); 135 tree = parse_tree_indirect(sha1);
135 if (!tree) { 136 if (!tree) {
136 cgit_print_error(fmt("Not a tree object: %s", 137 cgit_print_error(fmt("Not a tree object: %s",
137 sha1_to_hex(sha1))); 138 sha1_to_hex(sha1)));
138 return; 139 return;