summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c4
-rw-r--r--cgit.h2
-rw-r--r--cgitrc.5.txt10
-rw-r--r--cmd.c2
-rw-r--r--shared.c1
-rw-r--r--ui-repolist.c9
-rw-r--r--ui-summary.c28
-rw-r--r--ui-summary.h2
8 files changed, 48 insertions, 10 deletions
diff --git a/cgit.c b/cgit.c
index dbec196..7b228af 100644
--- a/cgit.c
+++ b/cgit.c
@@ -55,154 +55,158 @@ void config_cb(const char *name, const char *value)
55 else if (!strcmp(name, "footer")) 55 else if (!strcmp(name, "footer"))
56 ctx.cfg.footer = xstrdup(value); 56 ctx.cfg.footer = xstrdup(value);
57 else if (!strcmp(name, "head-include")) 57 else if (!strcmp(name, "head-include"))
58 ctx.cfg.head_include = xstrdup(value); 58 ctx.cfg.head_include = xstrdup(value);
59 else if (!strcmp(name, "header")) 59 else if (!strcmp(name, "header"))
60 ctx.cfg.header = xstrdup(value); 60 ctx.cfg.header = xstrdup(value);
61 else if (!strcmp(name, "logo")) 61 else if (!strcmp(name, "logo"))
62 ctx.cfg.logo = xstrdup(value); 62 ctx.cfg.logo = xstrdup(value);
63 else if (!strcmp(name, "index-header")) 63 else if (!strcmp(name, "index-header"))
64 ctx.cfg.index_header = xstrdup(value); 64 ctx.cfg.index_header = xstrdup(value);
65 else if (!strcmp(name, "index-info")) 65 else if (!strcmp(name, "index-info"))
66 ctx.cfg.index_info = xstrdup(value); 66 ctx.cfg.index_info = xstrdup(value);
67 else if (!strcmp(name, "logo-link")) 67 else if (!strcmp(name, "logo-link"))
68 ctx.cfg.logo_link = xstrdup(value); 68 ctx.cfg.logo_link = xstrdup(value);
69 else if (!strcmp(name, "module-link")) 69 else if (!strcmp(name, "module-link"))
70 ctx.cfg.module_link = xstrdup(value); 70 ctx.cfg.module_link = xstrdup(value);
71 else if (!strcmp(name, "virtual-root")) { 71 else if (!strcmp(name, "virtual-root")) {
72 ctx.cfg.virtual_root = trim_end(value, '/'); 72 ctx.cfg.virtual_root = trim_end(value, '/');
73 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 73 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
74 ctx.cfg.virtual_root = ""; 74 ctx.cfg.virtual_root = "";
75 } else if (!strcmp(name, "nocache")) 75 } else if (!strcmp(name, "nocache"))
76 ctx.cfg.nocache = atoi(value); 76 ctx.cfg.nocache = atoi(value);
77 else if (!strcmp(name, "noplainemail")) 77 else if (!strcmp(name, "noplainemail"))
78 ctx.cfg.noplainemail = atoi(value); 78 ctx.cfg.noplainemail = atoi(value);
79 else if (!strcmp(name, "noheader")) 79 else if (!strcmp(name, "noheader"))
80 ctx.cfg.noheader = atoi(value); 80 ctx.cfg.noheader = atoi(value);
81 else if (!strcmp(name, "snapshots")) 81 else if (!strcmp(name, "snapshots"))
82 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 82 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
83 else if (!strcmp(name, "enable-index-links")) 83 else if (!strcmp(name, "enable-index-links"))
84 ctx.cfg.enable_index_links = atoi(value); 84 ctx.cfg.enable_index_links = atoi(value);
85 else if (!strcmp(name, "enable-log-filecount")) 85 else if (!strcmp(name, "enable-log-filecount"))
86 ctx.cfg.enable_log_filecount = atoi(value); 86 ctx.cfg.enable_log_filecount = atoi(value);
87 else if (!strcmp(name, "enable-log-linecount")) 87 else if (!strcmp(name, "enable-log-linecount"))
88 ctx.cfg.enable_log_linecount = atoi(value); 88 ctx.cfg.enable_log_linecount = atoi(value);
89 else if (!strcmp(name, "max-stats")) 89 else if (!strcmp(name, "max-stats"))
90 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 90 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
91 else if (!strcmp(name, "cache-size")) 91 else if (!strcmp(name, "cache-size"))
92 ctx.cfg.cache_size = atoi(value); 92 ctx.cfg.cache_size = atoi(value);
93 else if (!strcmp(name, "cache-root")) 93 else if (!strcmp(name, "cache-root"))
94 ctx.cfg.cache_root = xstrdup(value); 94 ctx.cfg.cache_root = xstrdup(value);
95 else if (!strcmp(name, "cache-root-ttl")) 95 else if (!strcmp(name, "cache-root-ttl"))
96 ctx.cfg.cache_root_ttl = atoi(value); 96 ctx.cfg.cache_root_ttl = atoi(value);
97 else if (!strcmp(name, "cache-repo-ttl")) 97 else if (!strcmp(name, "cache-repo-ttl"))
98 ctx.cfg.cache_repo_ttl = atoi(value); 98 ctx.cfg.cache_repo_ttl = atoi(value);
99 else if (!strcmp(name, "cache-static-ttl")) 99 else if (!strcmp(name, "cache-static-ttl"))
100 ctx.cfg.cache_static_ttl = atoi(value); 100 ctx.cfg.cache_static_ttl = atoi(value);
101 else if (!strcmp(name, "cache-dynamic-ttl")) 101 else if (!strcmp(name, "cache-dynamic-ttl"))
102 ctx.cfg.cache_dynamic_ttl = atoi(value); 102 ctx.cfg.cache_dynamic_ttl = atoi(value);
103 else if (!strcmp(name, "about-filter"))
104 ctx.cfg.about_filter = new_filter(value, 0);
103 else if (!strcmp(name, "commit-filter")) 105 else if (!strcmp(name, "commit-filter"))
104 ctx.cfg.commit_filter = new_filter(value, 0); 106 ctx.cfg.commit_filter = new_filter(value, 0);
105 else if (!strcmp(name, "embedded")) 107 else if (!strcmp(name, "embedded"))
106 ctx.cfg.embedded = atoi(value); 108 ctx.cfg.embedded = atoi(value);
107 else if (!strcmp(name, "max-message-length")) 109 else if (!strcmp(name, "max-message-length"))
108 ctx.cfg.max_msg_len = atoi(value); 110 ctx.cfg.max_msg_len = atoi(value);
109 else if (!strcmp(name, "max-repodesc-length")) 111 else if (!strcmp(name, "max-repodesc-length"))
110 ctx.cfg.max_repodesc_len = atoi(value); 112 ctx.cfg.max_repodesc_len = atoi(value);
111 else if (!strcmp(name, "max-repo-count")) 113 else if (!strcmp(name, "max-repo-count"))
112 ctx.cfg.max_repo_count = atoi(value); 114 ctx.cfg.max_repo_count = atoi(value);
113 else if (!strcmp(name, "max-commit-count")) 115 else if (!strcmp(name, "max-commit-count"))
114 ctx.cfg.max_commit_count = atoi(value); 116 ctx.cfg.max_commit_count = atoi(value);
115 else if (!strcmp(name, "source-filter")) 117 else if (!strcmp(name, "source-filter"))
116 ctx.cfg.source_filter = new_filter(value, 1); 118 ctx.cfg.source_filter = new_filter(value, 1);
117 else if (!strcmp(name, "summary-log")) 119 else if (!strcmp(name, "summary-log"))
118 ctx.cfg.summary_log = atoi(value); 120 ctx.cfg.summary_log = atoi(value);
119 else if (!strcmp(name, "summary-branches")) 121 else if (!strcmp(name, "summary-branches"))
120 ctx.cfg.summary_branches = atoi(value); 122 ctx.cfg.summary_branches = atoi(value);
121 else if (!strcmp(name, "summary-tags")) 123 else if (!strcmp(name, "summary-tags"))
122 ctx.cfg.summary_tags = atoi(value); 124 ctx.cfg.summary_tags = atoi(value);
123 else if (!strcmp(name, "agefile")) 125 else if (!strcmp(name, "agefile"))
124 ctx.cfg.agefile = xstrdup(value); 126 ctx.cfg.agefile = xstrdup(value);
125 else if (!strcmp(name, "renamelimit")) 127 else if (!strcmp(name, "renamelimit"))
126 ctx.cfg.renamelimit = atoi(value); 128 ctx.cfg.renamelimit = atoi(value);
127 else if (!strcmp(name, "robots")) 129 else if (!strcmp(name, "robots"))
128 ctx.cfg.robots = xstrdup(value); 130 ctx.cfg.robots = xstrdup(value);
129 else if (!strcmp(name, "clone-prefix")) 131 else if (!strcmp(name, "clone-prefix"))
130 ctx.cfg.clone_prefix = xstrdup(value); 132 ctx.cfg.clone_prefix = xstrdup(value);
131 else if (!strcmp(name, "local-time")) 133 else if (!strcmp(name, "local-time"))
132 ctx.cfg.local_time = atoi(value); 134 ctx.cfg.local_time = atoi(value);
133 else if (!prefixcmp(name, "mimetype.")) 135 else if (!prefixcmp(name, "mimetype."))
134 add_mimetype(name + 9, value); 136 add_mimetype(name + 9, value);
135 else if (!strcmp(name, "repo.group")) 137 else if (!strcmp(name, "repo.group"))
136 ctx.cfg.repo_group = xstrdup(value); 138 ctx.cfg.repo_group = xstrdup(value);
137 else if (!strcmp(name, "repo.url")) 139 else if (!strcmp(name, "repo.url"))
138 ctx.repo = cgit_add_repo(value); 140 ctx.repo = cgit_add_repo(value);
139 else if (!strcmp(name, "repo.name")) 141 else if (!strcmp(name, "repo.name"))
140 ctx.repo->name = xstrdup(value); 142 ctx.repo->name = xstrdup(value);
141 else if (ctx.repo && !strcmp(name, "repo.path")) 143 else if (ctx.repo && !strcmp(name, "repo.path"))
142 ctx.repo->path = trim_end(value, '/'); 144 ctx.repo->path = trim_end(value, '/');
143 else if (ctx.repo && !strcmp(name, "repo.clone-url")) 145 else if (ctx.repo && !strcmp(name, "repo.clone-url"))
144 ctx.repo->clone_url = xstrdup(value); 146 ctx.repo->clone_url = xstrdup(value);
145 else if (ctx.repo && !strcmp(name, "repo.desc")) 147 else if (ctx.repo && !strcmp(name, "repo.desc"))
146 ctx.repo->desc = xstrdup(value); 148 ctx.repo->desc = xstrdup(value);
147 else if (ctx.repo && !strcmp(name, "repo.owner")) 149 else if (ctx.repo && !strcmp(name, "repo.owner"))
148 ctx.repo->owner = xstrdup(value); 150 ctx.repo->owner = xstrdup(value);
149 else if (ctx.repo && !strcmp(name, "repo.defbranch")) 151 else if (ctx.repo && !strcmp(name, "repo.defbranch"))
150 ctx.repo->defbranch = xstrdup(value); 152 ctx.repo->defbranch = xstrdup(value);
151 else if (ctx.repo && !strcmp(name, "repo.snapshots")) 153 else if (ctx.repo && !strcmp(name, "repo.snapshots"))
152 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */ 154 ctx.repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); /* XXX: &? */
153 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount")) 155 else if (ctx.repo && !strcmp(name, "repo.enable-log-filecount"))
154 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 156 ctx.repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
155 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount")) 157 else if (ctx.repo && !strcmp(name, "repo.enable-log-linecount"))
156 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 158 ctx.repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
157 else if (ctx.repo && !strcmp(name, "repo.max-stats")) 159 else if (ctx.repo && !strcmp(name, "repo.max-stats"))
158 ctx.repo->max_stats = cgit_find_stats_period(value, NULL); 160 ctx.repo->max_stats = cgit_find_stats_period(value, NULL);
159 else if (ctx.repo && !strcmp(name, "repo.module-link")) 161 else if (ctx.repo && !strcmp(name, "repo.module-link"))
160 ctx.repo->module_link= xstrdup(value); 162 ctx.repo->module_link= xstrdup(value);
163 else if (ctx.repo && !strcmp(name, "repo.about-filter"))
164 ctx.repo->about_filter = new_filter(value, 0);
161 else if (ctx.repo && !strcmp(name, "repo.commit-filter")) 165 else if (ctx.repo && !strcmp(name, "repo.commit-filter"))
162 ctx.repo->commit_filter = new_filter(value, 0); 166 ctx.repo->commit_filter = new_filter(value, 0);
163 else if (ctx.repo && !strcmp(name, "repo.source-filter")) 167 else if (ctx.repo && !strcmp(name, "repo.source-filter"))
164 ctx.repo->source_filter = new_filter(value, 1); 168 ctx.repo->source_filter = new_filter(value, 1);
165 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) { 169 else if (ctx.repo && !strcmp(name, "repo.readme") && value != NULL) {
166 if (*value == '/') 170 if (*value == '/')
167 ctx.repo->readme = xstrdup(value); 171 ctx.repo->readme = xstrdup(value);
168 else 172 else
169 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value)); 173 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path, value));
170 } else if (!strcmp(name, "include")) 174 } else if (!strcmp(name, "include"))
171 parse_configfile(value, config_cb); 175 parse_configfile(value, config_cb);
172} 176}
173 177
174static void querystring_cb(const char *name, const char *value) 178static void querystring_cb(const char *name, const char *value)
175{ 179{
176 if (!strcmp(name,"r")) { 180 if (!strcmp(name,"r")) {
177 ctx.qry.repo = xstrdup(value); 181 ctx.qry.repo = xstrdup(value);
178 ctx.repo = cgit_get_repoinfo(value); 182 ctx.repo = cgit_get_repoinfo(value);
179 } else if (!strcmp(name, "p")) { 183 } else if (!strcmp(name, "p")) {
180 ctx.qry.page = xstrdup(value); 184 ctx.qry.page = xstrdup(value);
181 } else if (!strcmp(name, "url")) { 185 } else if (!strcmp(name, "url")) {
182 ctx.qry.url = xstrdup(value); 186 ctx.qry.url = xstrdup(value);
183 cgit_parse_url(value); 187 cgit_parse_url(value);
184 } else if (!strcmp(name, "qt")) { 188 } else if (!strcmp(name, "qt")) {
185 ctx.qry.grep = xstrdup(value); 189 ctx.qry.grep = xstrdup(value);
186 } else if (!strcmp(name, "q")) { 190 } else if (!strcmp(name, "q")) {
187 ctx.qry.search = xstrdup(value); 191 ctx.qry.search = xstrdup(value);
188 } else if (!strcmp(name, "h")) { 192 } else if (!strcmp(name, "h")) {
189 ctx.qry.head = xstrdup(value); 193 ctx.qry.head = xstrdup(value);
190 ctx.qry.has_symref = 1; 194 ctx.qry.has_symref = 1;
191 } else if (!strcmp(name, "id")) { 195 } else if (!strcmp(name, "id")) {
192 ctx.qry.sha1 = xstrdup(value); 196 ctx.qry.sha1 = xstrdup(value);
193 ctx.qry.has_sha1 = 1; 197 ctx.qry.has_sha1 = 1;
194 } else if (!strcmp(name, "id2")) { 198 } else if (!strcmp(name, "id2")) {
195 ctx.qry.sha2 = xstrdup(value); 199 ctx.qry.sha2 = xstrdup(value);
196 ctx.qry.has_sha1 = 1; 200 ctx.qry.has_sha1 = 1;
197 } else if (!strcmp(name, "ofs")) { 201 } else if (!strcmp(name, "ofs")) {
198 ctx.qry.ofs = atoi(value); 202 ctx.qry.ofs = atoi(value);
199 } else if (!strcmp(name, "path")) { 203 } else if (!strcmp(name, "path")) {
200 ctx.qry.path = trim_end(value, '/'); 204 ctx.qry.path = trim_end(value, '/');
201 } else if (!strcmp(name, "name")) { 205 } else if (!strcmp(name, "name")) {
202 ctx.qry.name = xstrdup(value); 206 ctx.qry.name = xstrdup(value);
203 } else if (!strcmp(name, "mimetype")) { 207 } else if (!strcmp(name, "mimetype")) {
204 ctx.qry.mimetype = xstrdup(value); 208 ctx.qry.mimetype = xstrdup(value);
205 } else if (!strcmp(name, "s")){ 209 } else if (!strcmp(name, "s")){
206 ctx.qry.sort = xstrdup(value); 210 ctx.qry.sort = xstrdup(value);
207 } else if (!strcmp(name, "showmsg")) { 211 } else if (!strcmp(name, "showmsg")) {
208 ctx.qry.showmsg = atoi(value); 212 ctx.qry.showmsg = atoi(value);
diff --git a/cgit.h b/cgit.h
index b8557ac..4c854ea 100644
--- a/cgit.h
+++ b/cgit.h
@@ -29,96 +29,97 @@
29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 29#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
30 30
31 31
32/* 32/*
33 * Limits used for relative dates 33 * Limits used for relative dates
34 */ 34 */
35#define TM_MIN 60 35#define TM_MIN 60
36#define TM_HOUR (TM_MIN * 60) 36#define TM_HOUR (TM_MIN * 60)
37#define TM_DAY (TM_HOUR * 24) 37#define TM_DAY (TM_HOUR * 24)
38#define TM_WEEK (TM_DAY * 7) 38#define TM_WEEK (TM_DAY * 7)
39#define TM_YEAR (TM_DAY * 365) 39#define TM_YEAR (TM_DAY * 365)
40#define TM_MONTH (TM_YEAR / 12.0) 40#define TM_MONTH (TM_YEAR / 12.0)
41 41
42 42
43/* 43/*
44 * Default encoding 44 * Default encoding
45 */ 45 */
46#define PAGE_ENCODING "UTF-8" 46#define PAGE_ENCODING "UTF-8"
47 47
48typedef void (*configfn)(const char *name, const char *value); 48typedef void (*configfn)(const char *name, const char *value);
49typedef void (*filepair_fn)(struct diff_filepair *pair); 49typedef void (*filepair_fn)(struct diff_filepair *pair);
50typedef void (*linediff_fn)(char *line, int len); 50typedef void (*linediff_fn)(char *line, int len);
51 51
52struct cgit_filter { 52struct cgit_filter {
53 char *cmd; 53 char *cmd;
54 char **argv; 54 char **argv;
55 int old_stdout; 55 int old_stdout;
56 int pipe_fh[2]; 56 int pipe_fh[2];
57 int pid; 57 int pid;
58 int exitstatus; 58 int exitstatus;
59}; 59};
60 60
61struct cgit_repo { 61struct cgit_repo {
62 char *url; 62 char *url;
63 char *name; 63 char *name;
64 char *path; 64 char *path;
65 char *desc; 65 char *desc;
66 char *owner; 66 char *owner;
67 char *defbranch; 67 char *defbranch;
68 char *group; 68 char *group;
69 char *module_link; 69 char *module_link;
70 char *readme; 70 char *readme;
71 char *clone_url; 71 char *clone_url;
72 int snapshots; 72 int snapshots;
73 int enable_log_filecount; 73 int enable_log_filecount;
74 int enable_log_linecount; 74 int enable_log_linecount;
75 int max_stats; 75 int max_stats;
76 time_t mtime; 76 time_t mtime;
77 struct cgit_filter *about_filter;
77 struct cgit_filter *commit_filter; 78 struct cgit_filter *commit_filter;
78 struct cgit_filter *source_filter; 79 struct cgit_filter *source_filter;
79}; 80};
80 81
81struct cgit_repolist { 82struct cgit_repolist {
82 int length; 83 int length;
83 int count; 84 int count;
84 struct cgit_repo *repos; 85 struct cgit_repo *repos;
85}; 86};
86 87
87struct commitinfo { 88struct commitinfo {
88 struct commit *commit; 89 struct commit *commit;
89 char *author; 90 char *author;
90 char *author_email; 91 char *author_email;
91 unsigned long author_date; 92 unsigned long author_date;
92 char *committer; 93 char *committer;
93 char *committer_email; 94 char *committer_email;
94 unsigned long committer_date; 95 unsigned long committer_date;
95 char *subject; 96 char *subject;
96 char *msg; 97 char *msg;
97 char *msg_encoding; 98 char *msg_encoding;
98}; 99};
99 100
100struct taginfo { 101struct taginfo {
101 char *tagger; 102 char *tagger;
102 char *tagger_email; 103 char *tagger_email;
103 unsigned long tagger_date; 104 unsigned long tagger_date;
104 char *msg; 105 char *msg;
105}; 106};
106 107
107struct refinfo { 108struct refinfo {
108 const char *refname; 109 const char *refname;
109 struct object *object; 110 struct object *object;
110 union { 111 union {
111 struct taginfo *tag; 112 struct taginfo *tag;
112 struct commitinfo *commit; 113 struct commitinfo *commit;
113 }; 114 };
114}; 115};
115 116
116struct reflist { 117struct reflist {
117 struct refinfo **refs; 118 struct refinfo **refs;
118 int alloc; 119 int alloc;
119 int count; 120 int count;
120}; 121};
121 122
122struct cgit_query { 123struct cgit_query {
123 int has_symref; 124 int has_symref;
124 int has_sha1; 125 int has_sha1;
@@ -143,96 +144,97 @@ struct cgit_query {
143 144
144struct cgit_config { 145struct cgit_config {
145 char *agefile; 146 char *agefile;
146 char *cache_root; 147 char *cache_root;
147 char *clone_prefix; 148 char *clone_prefix;
148 char *css; 149 char *css;
149 char *favicon; 150 char *favicon;
150 char *footer; 151 char *footer;
151 char *head_include; 152 char *head_include;
152 char *header; 153 char *header;
153 char *index_header; 154 char *index_header;
154 char *index_info; 155 char *index_info;
155 char *logo; 156 char *logo;
156 char *logo_link; 157 char *logo_link;
157 char *module_link; 158 char *module_link;
158 char *repo_group; 159 char *repo_group;
159 char *robots; 160 char *robots;
160 char *root_title; 161 char *root_title;
161 char *root_desc; 162 char *root_desc;
162 char *root_readme; 163 char *root_readme;
163 char *script_name; 164 char *script_name;
164 char *virtual_root; 165 char *virtual_root;
165 int cache_size; 166 int cache_size;
166 int cache_dynamic_ttl; 167 int cache_dynamic_ttl;
167 int cache_max_create_time; 168 int cache_max_create_time;
168 int cache_repo_ttl; 169 int cache_repo_ttl;
169 int cache_root_ttl; 170 int cache_root_ttl;
170 int cache_static_ttl; 171 int cache_static_ttl;
171 int embedded; 172 int embedded;
172 int enable_index_links; 173 int enable_index_links;
173 int enable_log_filecount; 174 int enable_log_filecount;
174 int enable_log_linecount; 175 int enable_log_linecount;
175 int local_time; 176 int local_time;
176 int max_repo_count; 177 int max_repo_count;
177 int max_commit_count; 178 int max_commit_count;
178 int max_lock_attempts; 179 int max_lock_attempts;
179 int max_msg_len; 180 int max_msg_len;
180 int max_repodesc_len; 181 int max_repodesc_len;
181 int max_stats; 182 int max_stats;
182 int nocache; 183 int nocache;
183 int noplainemail; 184 int noplainemail;
184 int noheader; 185 int noheader;
185 int renamelimit; 186 int renamelimit;
186 int snapshots; 187 int snapshots;
187 int summary_branches; 188 int summary_branches;
188 int summary_log; 189 int summary_log;
189 int summary_tags; 190 int summary_tags;
190 struct string_list mimetypes; 191 struct string_list mimetypes;
192 struct cgit_filter *about_filter;
191 struct cgit_filter *commit_filter; 193 struct cgit_filter *commit_filter;
192 struct cgit_filter *source_filter; 194 struct cgit_filter *source_filter;
193}; 195};
194 196
195struct cgit_page { 197struct cgit_page {
196 time_t modified; 198 time_t modified;
197 time_t expires; 199 time_t expires;
198 size_t size; 200 size_t size;
199 char *mimetype; 201 char *mimetype;
200 char *charset; 202 char *charset;
201 char *filename; 203 char *filename;
202 char *etag; 204 char *etag;
203 char *title; 205 char *title;
204 int status; 206 int status;
205 char *statusmsg; 207 char *statusmsg;
206}; 208};
207 209
208struct cgit_context { 210struct cgit_context {
209 struct cgit_query qry; 211 struct cgit_query qry;
210 struct cgit_config cfg; 212 struct cgit_config cfg;
211 struct cgit_repo *repo; 213 struct cgit_repo *repo;
212 struct cgit_page page; 214 struct cgit_page page;
213}; 215};
214 216
215struct cgit_snapshot_format { 217struct cgit_snapshot_format {
216 const char *suffix; 218 const char *suffix;
217 const char *mimetype; 219 const char *mimetype;
218 write_archive_fn_t write_func; 220 write_archive_fn_t write_func;
219 int bit; 221 int bit;
220}; 222};
221 223
222extern const char *cgit_version; 224extern const char *cgit_version;
223 225
224extern struct cgit_repolist cgit_repolist; 226extern struct cgit_repolist cgit_repolist;
225extern struct cgit_context ctx; 227extern struct cgit_context ctx;
226extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 228extern const struct cgit_snapshot_format cgit_snapshot_formats[];
227 229
228extern struct cgit_repo *cgit_add_repo(const char *url); 230extern struct cgit_repo *cgit_add_repo(const char *url);
229extern struct cgit_repo *cgit_get_repoinfo(const char *url); 231extern struct cgit_repo *cgit_get_repoinfo(const char *url);
230extern void cgit_repo_config_cb(const char *name, const char *value); 232extern void cgit_repo_config_cb(const char *name, const char *value);
231 233
232extern int chk_zero(int result, char *msg); 234extern int chk_zero(int result, char *msg);
233extern int chk_positive(int result, char *msg); 235extern int chk_positive(int result, char *msg);
234extern int chk_non_negative(int result, char *msg); 236extern int chk_non_negative(int result, char *msg);
235 237
236extern char *trim_end(const char *str, char c); 238extern char *trim_end(const char *str, char c);
237extern char *strlpart(char *txt, int maxlen); 239extern char *strlpart(char *txt, int maxlen);
238extern char *strrpart(char *txt, int maxlen); 240extern char *strrpart(char *txt, int maxlen);
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index dc63637..4d656fe 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -1,66 +1,73 @@
1CGITRC(5) 1CGITRC(5)
2======== 2========
3 3
4 4
5NAME 5NAME
6---- 6----
7cgitrc - runtime configuration for cgit 7cgitrc - runtime configuration for cgit
8 8
9 9
10SYNOPSIS 10SYNOPSIS
11-------- 11--------
12Cgitrc contains all runtime settings for cgit, including the list of git 12Cgitrc contains all runtime settings for cgit, including the list of git
13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank 13repositories, formatted as a line-separated list of NAME=VALUE pairs. Blank
14lines, and lines starting with '#', are ignored. 14lines, and lines starting with '#', are ignored.
15 15
16 16
17GLOBAL SETTINGS 17GLOBAL SETTINGS
18--------------- 18---------------
19about-filter::
20 Specifies a command which will be invoked to format the content of
21 about pages (both top-level and for each repository). The command will
22 get the content of the about-file on its STDIN, and the STDOUT from the
23 command will be included verbatim on the about page. Default value:
24 none.
25
19agefile:: 26agefile::
20 Specifies a path, relative to each repository path, which can be used 27 Specifies a path, relative to each repository path, which can be used
21 to specify the date and time of the youngest commit in the repository. 28 to specify the date and time of the youngest commit in the repository.
22 The first line in the file is used as input to the "parse_date" 29 The first line in the file is used as input to the "parse_date"
23 function in libgit. Recommended timestamp-format is "yyyy-mm-dd 30 function in libgit. Recommended timestamp-format is "yyyy-mm-dd
24 hh:mm:ss". Default value: "info/web/last-modified". 31 hh:mm:ss". Default value: "info/web/last-modified".
25 32
26cache-root:: 33cache-root::
27 Path used to store the cgit cache entries. Default value: 34 Path used to store the cgit cache entries. Default value:
28 "/var/cache/cgit". 35 "/var/cache/cgit".
29 36
30cache-dynamic-ttl:: 37cache-dynamic-ttl::
31 Number which specifies the time-to-live, in minutes, for the cached 38 Number which specifies the time-to-live, in minutes, for the cached
32 version of repository pages accessed without a fixed SHA1. Default 39 version of repository pages accessed without a fixed SHA1. Default
33 value: "5". 40 value: "5".
34 41
35cache-repo-ttl:: 42cache-repo-ttl::
36 Number which specifies the time-to-live, in minutes, for the cached 43 Number which specifies the time-to-live, in minutes, for the cached
37 version of the repository summary page. Default value: "5". 44 version of the repository summary page. Default value: "5".
38 45
39cache-root-ttl:: 46cache-root-ttl::
40 Number which specifies the time-to-live, in minutes, for the cached 47 Number which specifies the time-to-live, in minutes, for the cached
41 version of the repository index page. Default value: "5". 48 version of the repository index page. Default value: "5".
42 49
43cache-size:: 50cache-size::
44 The maximum number of entries in the cgit cache. Default value: "0" 51 The maximum number of entries in the cgit cache. Default value: "0"
45 (i.e. caching is disabled). 52 (i.e. caching is disabled).
46 53
47cache-static-ttl:: 54cache-static-ttl::
48 Number which specifies the time-to-live, in minutes, for the cached 55 Number which specifies the time-to-live, in minutes, for the cached
49 version of repository pages accessed with a fixed SHA1. Default value: 56 version of repository pages accessed with a fixed SHA1. Default value:
50 "5". 57 "5".
51 58
52clone-prefix:: 59clone-prefix::
53 Space-separated list of common prefixes which, when combined with a 60 Space-separated list of common prefixes which, when combined with a
54 repository url, generates valid clone urls for the repository. This 61 repository url, generates valid clone urls for the repository. This
55 setting is only used if `repo.clone-url` is unspecified. Default value: 62 setting is only used if `repo.clone-url` is unspecified. Default value:
56 none. 63 none.
57 64
58commit-filter:: 65commit-filter::
59 Specifies a command which will be invoked to format commit messages. 66 Specifies a command which will be invoked to format commit messages.
60 The command will get the message on its STDIN, and the STDOUT from the 67 The command will get the message on its STDIN, and the STDOUT from the
61 command will be included verbatim as the commit message, i.e. this can 68 command will be included verbatim as the commit message, i.e. this can
62 be used to implement bugtracker integration. Default value: none. 69 be used to implement bugtracker integration. Default value: none.
63 70
64css:: 71css::
65 Url which specifies the css document to include in all cgit pages. 72 Url which specifies the css document to include in all cgit pages.
66 Default value: "/cgit.css". 73 Default value: "/cgit.css".
@@ -197,96 +204,99 @@ root-readme::
197 The content of the file specified with this option will be included 204 The content of the file specified with this option will be included
198 verbatim below the "about" link on the repository index page. Default 205 verbatim below the "about" link on the repository index page. Default
199 value: none. 206 value: none.
200 207
201root-title:: 208root-title::
202 Text printed as heading on the repository index page. Default value: 209 Text printed as heading on the repository index page. Default value:
203 "Git Repository Browser". 210 "Git Repository Browser".
204 211
205snapshots:: 212snapshots::
206 Text which specifies the default (and allowed) set of snapshot formats 213 Text which specifies the default (and allowed) set of snapshot formats
207 supported by cgit. The value is a space-separated list of zero or more 214 supported by cgit. The value is a space-separated list of zero or more
208 of the following values: 215 of the following values:
209 "tar" uncompressed tar-file 216 "tar" uncompressed tar-file
210 "tar.gz"gzip-compressed tar-file 217 "tar.gz"gzip-compressed tar-file
211 "tar.bz2"bzip-compressed tar-file 218 "tar.bz2"bzip-compressed tar-file
212 "zip" zip-file 219 "zip" zip-file
213 Default value: none. 220 Default value: none.
214 221
215source-filter:: 222source-filter::
216 Specifies a command which will be invoked to format plaintext blobs 223 Specifies a command which will be invoked to format plaintext blobs
217 in the tree view. The command will get the blob content on its STDIN 224 in the tree view. The command will get the blob content on its STDIN
218 and the name of the blob as its only command line argument. The STDOUT 225 and the name of the blob as its only command line argument. The STDOUT
219 from the command will be included verbatim as the blob contents, i.e. 226 from the command will be included verbatim as the blob contents, i.e.
220 this can be used to implement e.g. syntax highlighting. Default value: 227 this can be used to implement e.g. syntax highlighting. Default value:
221 none. 228 none.
222 229
223summary-branches:: 230summary-branches::
224 Specifies the number of branches to display in the repository "summary" 231 Specifies the number of branches to display in the repository "summary"
225 view. Default value: "10". 232 view. Default value: "10".
226 233
227summary-log:: 234summary-log::
228 Specifies the number of log entries to display in the repository 235 Specifies the number of log entries to display in the repository
229 "summary" view. Default value: "10". 236 "summary" view. Default value: "10".
230 237
231summary-tags:: 238summary-tags::
232 Specifies the number of tags to display in the repository "summary" 239 Specifies the number of tags to display in the repository "summary"
233 view. Default value: "10". 240 view. Default value: "10".
234 241
235virtual-root:: 242virtual-root::
236 Url which, if specified, will be used as root for all cgit links. It 243 Url which, if specified, will be used as root for all cgit links. It
237 will also cause cgit to generate 'virtual urls', i.e. urls like 244 will also cause cgit to generate 'virtual urls', i.e. urls like
238 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 245 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
239 value: none. 246 value: none.
240 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 247 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
241 same kind of virtual urls, so this option will probably be deprecated. 248 same kind of virtual urls, so this option will probably be deprecated.
242 249
243REPOSITORY SETTINGS 250REPOSITORY SETTINGS
244------------------- 251-------------------
252repo.about-filter::
253 Override the default about-filter. Default value: <about-filter>.
254
245repo.clone-url:: 255repo.clone-url::
246 A list of space-separated urls which can be used to clone this repo. 256 A list of space-separated urls which can be used to clone this repo.
247 Default value: none. 257 Default value: none.
248 258
249repo.commit-filter:: 259repo.commit-filter::
250 Override the default commit-filter. Default value: <commit-filter>. 260 Override the default commit-filter. Default value: <commit-filter>.
251 261
252repo.defbranch:: 262repo.defbranch::
253 The name of the default branch for this repository. If no such branch 263 The name of the default branch for this repository. If no such branch
254 exists in the repository, the first branch name (when sorted) is used 264 exists in the repository, the first branch name (when sorted) is used
255 as default instead. Default value: "master". 265 as default instead. Default value: "master".
256 266
257repo.desc:: 267repo.desc::
258 The value to show as repository description. Default value: none. 268 The value to show as repository description. Default value: none.
259 269
260repo.enable-log-filecount:: 270repo.enable-log-filecount::
261 A flag which can be used to disable the global setting 271 A flag which can be used to disable the global setting
262 `enable-log-filecount'. Default value: none. 272 `enable-log-filecount'. Default value: none.
263 273
264repo.enable-log-linecount:: 274repo.enable-log-linecount::
265 A flag which can be used to disable the global setting 275 A flag which can be used to disable the global setting
266 `enable-log-linecount'. Default value: none. 276 `enable-log-linecount'. Default value: none.
267 277
268repo.max-stats:: 278repo.max-stats::
269 Override the default maximum statistics period. Valid values are equal 279 Override the default maximum statistics period. Valid values are equal
270 to the values specified for the global "max-stats" setting. Default 280 to the values specified for the global "max-stats" setting. Default
271 value: none. 281 value: none.
272 282
273repo.name:: 283repo.name::
274 The value to show as repository name. Default value: <repo.url>. 284 The value to show as repository name. Default value: <repo.url>.
275 285
276repo.owner:: 286repo.owner::
277 A value used to identify the owner of the repository. Default value: 287 A value used to identify the owner of the repository. Default value:
278 none. 288 none.
279 289
280repo.path:: 290repo.path::
281 An absolute path to the repository directory. For non-bare repositories 291 An absolute path to the repository directory. For non-bare repositories
282 this is the .git-directory. Default value: none. 292 this is the .git-directory. Default value: none.
283 293
284repo.readme:: 294repo.readme::
285 A path (relative to <repo.path>) which specifies a file to include 295 A path (relative to <repo.path>) which specifies a file to include
286 verbatim as the "About" page for this repo. Default value: none. 296 verbatim as the "About" page for this repo. Default value: none.
287 297
288repo.snapshots:: 298repo.snapshots::
289 A mask of allowed snapshot-formats for this repo, restricted by the 299 A mask of allowed snapshot-formats for this repo, restricted by the
290 "snapshots" global setting. Default value: <snapshots>. 300 "snapshots" global setting. Default value: <snapshots>.
291 301
292repo.source-filter:: 302repo.source-filter::
diff --git a/cmd.c b/cmd.c
index cf97da7..766f903 100644
--- a/cmd.c
+++ b/cmd.c
@@ -1,90 +1,90 @@
1/* cmd.c: the cgit command dispatcher 1/* cmd.c: the cgit command dispatcher
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 "cmd.h" 10#include "cmd.h"
11#include "cache.h" 11#include "cache.h"
12#include "ui-shared.h" 12#include "ui-shared.h"
13#include "ui-atom.h" 13#include "ui-atom.h"
14#include "ui-blob.h" 14#include "ui-blob.h"
15#include "ui-clone.h" 15#include "ui-clone.h"
16#include "ui-commit.h" 16#include "ui-commit.h"
17#include "ui-diff.h" 17#include "ui-diff.h"
18#include "ui-log.h" 18#include "ui-log.h"
19#include "ui-patch.h" 19#include "ui-patch.h"
20#include "ui-plain.h" 20#include "ui-plain.h"
21#include "ui-refs.h" 21#include "ui-refs.h"
22#include "ui-repolist.h" 22#include "ui-repolist.h"
23#include "ui-snapshot.h" 23#include "ui-snapshot.h"
24#include "ui-stats.h" 24#include "ui-stats.h"
25#include "ui-summary.h" 25#include "ui-summary.h"
26#include "ui-tag.h" 26#include "ui-tag.h"
27#include "ui-tree.h" 27#include "ui-tree.h"
28 28
29static void HEAD_fn(struct cgit_context *ctx) 29static void HEAD_fn(struct cgit_context *ctx)
30{ 30{
31 cgit_clone_head(ctx); 31 cgit_clone_head(ctx);
32} 32}
33 33
34static void atom_fn(struct cgit_context *ctx) 34static void atom_fn(struct cgit_context *ctx)
35{ 35{
36 cgit_print_atom(ctx->qry.head, ctx->qry.path, 10); 36 cgit_print_atom(ctx->qry.head, ctx->qry.path, 10);
37} 37}
38 38
39static void about_fn(struct cgit_context *ctx) 39static void about_fn(struct cgit_context *ctx)
40{ 40{
41 if (ctx->repo) 41 if (ctx->repo)
42 cgit_print_repo_readme(); 42 cgit_print_repo_readme(ctx->qry.path);
43 else 43 else
44 cgit_print_site_readme(); 44 cgit_print_site_readme();
45} 45}
46 46
47static void blob_fn(struct cgit_context *ctx) 47static void blob_fn(struct cgit_context *ctx)
48{ 48{
49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head); 49 cgit_print_blob(ctx->qry.sha1, ctx->qry.path, ctx->qry.head);
50} 50}
51 51
52static void commit_fn(struct cgit_context *ctx) 52static void commit_fn(struct cgit_context *ctx)
53{ 53{
54 cgit_print_commit(ctx->qry.sha1); 54 cgit_print_commit(ctx->qry.sha1);
55} 55}
56 56
57static void diff_fn(struct cgit_context *ctx) 57static void diff_fn(struct cgit_context *ctx)
58{ 58{
59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path); 59 cgit_print_diff(ctx->qry.sha1, ctx->qry.sha2, ctx->qry.path);
60} 60}
61 61
62static void info_fn(struct cgit_context *ctx) 62static void info_fn(struct cgit_context *ctx)
63{ 63{
64 cgit_clone_info(ctx); 64 cgit_clone_info(ctx);
65} 65}
66 66
67static void log_fn(struct cgit_context *ctx) 67static void log_fn(struct cgit_context *ctx)
68{ 68{
69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count, 69 cgit_print_log(ctx->qry.sha1, ctx->qry.ofs, ctx->cfg.max_commit_count,
70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1); 70 ctx->qry.grep, ctx->qry.search, ctx->qry.path, 1);
71} 71}
72 72
73static void ls_cache_fn(struct cgit_context *ctx) 73static void ls_cache_fn(struct cgit_context *ctx)
74{ 74{
75 ctx->page.mimetype = "text/plain"; 75 ctx->page.mimetype = "text/plain";
76 ctx->page.filename = "ls-cache.txt"; 76 ctx->page.filename = "ls-cache.txt";
77 cgit_print_http_headers(ctx); 77 cgit_print_http_headers(ctx);
78 cache_ls(ctx->cfg.cache_root); 78 cache_ls(ctx->cfg.cache_root);
79} 79}
80 80
81static void objects_fn(struct cgit_context *ctx) 81static void objects_fn(struct cgit_context *ctx)
82{ 82{
83 cgit_clone_objects(ctx); 83 cgit_clone_objects(ctx);
84} 84}
85 85
86static void repolist_fn(struct cgit_context *ctx) 86static void repolist_fn(struct cgit_context *ctx)
87{ 87{
88 cgit_print_repolist(); 88 cgit_print_repolist();
89} 89}
90 90
diff --git a/shared.c b/shared.c
index 783604b..911a55a 100644
--- a/shared.c
+++ b/shared.c
@@ -17,96 +17,97 @@ int chk_zero(int result, char *msg)
17 if (result != 0) 17 if (result != 0)
18 die("%s: %s", msg, strerror(errno)); 18 die("%s: %s", msg, strerror(errno));
19 return result; 19 return result;
20} 20}
21 21
22int chk_positive(int result, char *msg) 22int chk_positive(int result, char *msg)
23{ 23{
24 if (result <= 0) 24 if (result <= 0)
25 die("%s: %s", msg, strerror(errno)); 25 die("%s: %s", msg, strerror(errno));
26 return result; 26 return result;
27} 27}
28 28
29int chk_non_negative(int result, char *msg) 29int chk_non_negative(int result, char *msg)
30{ 30{
31 if (result < 0) 31 if (result < 0)
32 die("%s: %s",msg, strerror(errno)); 32 die("%s: %s",msg, strerror(errno));
33 return result; 33 return result;
34} 34}
35 35
36struct cgit_repo *cgit_add_repo(const char *url) 36struct cgit_repo *cgit_add_repo(const char *url)
37{ 37{
38 struct cgit_repo *ret; 38 struct cgit_repo *ret;
39 39
40 if (++cgit_repolist.count > cgit_repolist.length) { 40 if (++cgit_repolist.count > cgit_repolist.length) {
41 if (cgit_repolist.length == 0) 41 if (cgit_repolist.length == 0)
42 cgit_repolist.length = 8; 42 cgit_repolist.length = 8;
43 else 43 else
44 cgit_repolist.length *= 2; 44 cgit_repolist.length *= 2;
45 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 45 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
46 cgit_repolist.length * 46 cgit_repolist.length *
47 sizeof(struct cgit_repo)); 47 sizeof(struct cgit_repo));
48 } 48 }
49 49
50 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 50 ret = &cgit_repolist.repos[cgit_repolist.count-1];
51 ret->url = trim_end(url, '/'); 51 ret->url = trim_end(url, '/');
52 ret->name = ret->url; 52 ret->name = ret->url;
53 ret->path = NULL; 53 ret->path = NULL;
54 ret->desc = "[no description]"; 54 ret->desc = "[no description]";
55 ret->owner = NULL; 55 ret->owner = NULL;
56 ret->group = ctx.cfg.repo_group; 56 ret->group = ctx.cfg.repo_group;
57 ret->defbranch = "master"; 57 ret->defbranch = "master";
58 ret->snapshots = ctx.cfg.snapshots; 58 ret->snapshots = ctx.cfg.snapshots;
59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount; 59 ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount; 60 ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
61 ret->max_stats = ctx.cfg.max_stats; 61 ret->max_stats = ctx.cfg.max_stats;
62 ret->module_link = ctx.cfg.module_link; 62 ret->module_link = ctx.cfg.module_link;
63 ret->readme = NULL; 63 ret->readme = NULL;
64 ret->mtime = -1; 64 ret->mtime = -1;
65 ret->about_filter = ctx.cfg.about_filter;
65 ret->commit_filter = ctx.cfg.commit_filter; 66 ret->commit_filter = ctx.cfg.commit_filter;
66 ret->source_filter = ctx.cfg.source_filter; 67 ret->source_filter = ctx.cfg.source_filter;
67 return ret; 68 return ret;
68} 69}
69 70
70struct cgit_repo *cgit_get_repoinfo(const char *url) 71struct cgit_repo *cgit_get_repoinfo(const char *url)
71{ 72{
72 int i; 73 int i;
73 struct cgit_repo *repo; 74 struct cgit_repo *repo;
74 75
75 for (i=0; i<cgit_repolist.count; i++) { 76 for (i=0; i<cgit_repolist.count; i++) {
76 repo = &cgit_repolist.repos[i]; 77 repo = &cgit_repolist.repos[i];
77 if (!strcmp(repo->url, url)) 78 if (!strcmp(repo->url, url))
78 return repo; 79 return repo;
79 } 80 }
80 return NULL; 81 return NULL;
81} 82}
82 83
83void *cgit_free_commitinfo(struct commitinfo *info) 84void *cgit_free_commitinfo(struct commitinfo *info)
84{ 85{
85 free(info->author); 86 free(info->author);
86 free(info->author_email); 87 free(info->author_email);
87 free(info->committer); 88 free(info->committer);
88 free(info->committer_email); 89 free(info->committer_email);
89 free(info->subject); 90 free(info->subject);
90 free(info->msg); 91 free(info->msg);
91 free(info->msg_encoding); 92 free(info->msg_encoding);
92 free(info); 93 free(info);
93 return NULL; 94 return NULL;
94} 95}
95 96
96char *trim_end(const char *str, char c) 97char *trim_end(const char *str, char c)
97{ 98{
98 int len; 99 int len;
99 char *s, *t; 100 char *s, *t;
100 101
101 if (str == NULL) 102 if (str == NULL)
102 return NULL; 103 return NULL;
103 t = (char *)str; 104 t = (char *)str;
104 len = strlen(t); 105 len = strlen(t);
105 while(len > 0 && t[len - 1] == c) 106 while(len > 0 && t[len - 1] == c)
106 len--; 107 len--;
107 108
108 if (len == 0) 109 if (len == 0)
109 return NULL; 110 return NULL;
110 111
111 c = t[len]; 112 c = t[len];
112 t[len] = '\0'; 113 t[len] = '\0';
diff --git a/ui-repolist.c b/ui-repolist.c
index 2c13d50..25f076f 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -228,51 +228,56 @@ void cgit_print_repolist()
228 continue; 228 continue;
229 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count) 229 if (hits > ctx.qry.ofs + ctx.cfg.max_repo_count)
230 continue; 230 continue;
231 if (!header++) 231 if (!header++)
232 print_header(columns); 232 print_header(columns);
233 if (!sorted && 233 if (!sorted &&
234 ((last_group == NULL && ctx.repo->group != NULL) || 234 ((last_group == NULL && ctx.repo->group != NULL) ||
235 (last_group != NULL && ctx.repo->group == NULL) || 235 (last_group != NULL && ctx.repo->group == NULL) ||
236 (last_group != NULL && ctx.repo->group != NULL && 236 (last_group != NULL && ctx.repo->group != NULL &&
237 strcmp(ctx.repo->group, last_group)))) { 237 strcmp(ctx.repo->group, last_group)))) {
238 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>", 238 htmlf("<tr class='nohover'><td colspan='%d' class='repogroup'>",
239 columns); 239 columns);
240 html_txt(ctx.repo->group); 240 html_txt(ctx.repo->group);
241 html("</td></tr>"); 241 html("</td></tr>");
242 last_group = ctx.repo->group; 242 last_group = ctx.repo->group;
243 } 243 }
244 htmlf("<tr><td class='%s'>", 244 htmlf("<tr><td class='%s'>",
245 !sorted && ctx.repo->group ? "sublevel-repo" : "toplevel-repo"); 245 !sorted && ctx.repo->group ? "sublevel-repo" : "toplevel-repo");
246 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); 246 cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
247 html("</td><td>"); 247 html("</td><td>");
248 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL); 248 html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
249 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc); 249 html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
250 html_link_close(); 250 html_link_close();
251 html("</td><td>"); 251 html("</td><td>");
252 html_txt(ctx.repo->owner); 252 html_txt(ctx.repo->owner);
253 html("</td><td>"); 253 html("</td><td>");
254 print_modtime(ctx.repo); 254 print_modtime(ctx.repo);
255 html("</td>"); 255 html("</td>");
256 if (ctx.cfg.enable_index_links) { 256 if (ctx.cfg.enable_index_links) {
257 html("<td>"); 257 html("<td>");
258 cgit_summary_link("summary", NULL, "button", NULL); 258 cgit_summary_link("summary", NULL, "button", NULL);
259 cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 259 cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
260 0, NULL, NULL, ctx.qry.showmsg); 260 0, NULL, NULL, ctx.qry.showmsg);
261 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); 261 cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
262 html("</td>"); 262 html("</td>");
263 } 263 }
264 html("</tr>\n"); 264 html("</tr>\n");
265 } 265 }
266 html("</table>"); 266 html("</table>");
267 if (!hits) 267 if (!hits)
268 cgit_print_error("No repositories found"); 268 cgit_print_error("No repositories found");
269 else if (hits > ctx.cfg.max_repo_count) 269 else if (hits > ctx.cfg.max_repo_count)
270 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search); 270 print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search);
271 cgit_print_docend(); 271 cgit_print_docend();
272} 272}
273 273
274void cgit_print_site_readme() 274void cgit_print_site_readme()
275{ 275{
276 if (ctx.cfg.root_readme) 276 if (!ctx.cfg.root_readme)
277 html_include(ctx.cfg.root_readme); 277 return;
278 if (ctx.cfg.about_filter)
279 cgit_open_filter(ctx.cfg.about_filter);
280 html_include(ctx.cfg.root_readme);
281 if (ctx.cfg.about_filter)
282 cgit_close_filter(ctx.cfg.about_filter);
278} 283}
diff --git a/ui-summary.c b/ui-summary.c
index ede4a62..a2c018e 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -21,56 +21,72 @@ static void print_url(char *base, char *suffix)
21 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 21 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
22 html("<tr><th class='left' colspan='4'>Clone</th></tr>\n"); 22 html("<tr><th class='left' colspan='4'>Clone</th></tr>\n");
23 } 23 }
24 if (suffix && *suffix) 24 if (suffix && *suffix)
25 base = fmt("%s/%s", base, suffix); 25 base = fmt("%s/%s", base, suffix);
26 html("<tr><td colspan='4'><a href='"); 26 html("<tr><td colspan='4'><a href='");
27 html_url_path(base); 27 html_url_path(base);
28 html("'>"); 28 html("'>");
29 html_txt(base); 29 html_txt(base);
30 html("</a></td></tr>\n"); 30 html("</a></td></tr>\n");
31} 31}
32 32
33static void print_urls(char *txt, char *suffix) 33static void print_urls(char *txt, char *suffix)
34{ 34{
35 char *h = txt, *t, c; 35 char *h = txt, *t, c;
36 36
37 while (h && *h) { 37 while (h && *h) {
38 while (h && *h == ' ') 38 while (h && *h == ' ')
39 h++; 39 h++;
40 t = h; 40 t = h;
41 while (t && *t && *t != ' ') 41 while (t && *t && *t != ' ')
42 t++; 42 t++;
43 c = *t; 43 c = *t;
44 *t = 0; 44 *t = 0;
45 print_url(h, suffix); 45 print_url(h, suffix);
46 *t = c; 46 *t = c;
47 h = t; 47 h = t;
48 } 48 }
49} 49}
50 50
51void cgit_print_summary() 51void cgit_print_summary()
52{ 52{
53 html("<table summary='repository info' class='list nowrap'>"); 53 html("<table summary='repository info' class='list nowrap'>");
54 cgit_print_branches(ctx.cfg.summary_branches); 54 cgit_print_branches(ctx.cfg.summary_branches);
55 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 55 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
56 cgit_print_tags(ctx.cfg.summary_tags); 56 cgit_print_tags(ctx.cfg.summary_tags);
57 if (ctx.cfg.summary_log > 0) { 57 if (ctx.cfg.summary_log > 0) {
58 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 58 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
59 cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, 59 cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
60 NULL, NULL, 0); 60 NULL, NULL, 0);
61 } 61 }
62 if (ctx.repo->clone_url) 62 if (ctx.repo->clone_url)
63 print_urls(ctx.repo->clone_url, NULL); 63 print_urls(ctx.repo->clone_url, NULL);
64 else if (ctx.cfg.clone_prefix) 64 else if (ctx.cfg.clone_prefix)
65 print_urls(ctx.cfg.clone_prefix, ctx.repo->url); 65 print_urls(ctx.cfg.clone_prefix, ctx.repo->url);
66 html("</table>"); 66 html("</table>");
67} 67}
68 68
69void cgit_print_repo_readme() 69void cgit_print_repo_readme(char *path)
70{ 70{
71 if (ctx.repo->readme) { 71 char *slash, *tmp;
72 html("<div id='summary'>"); 72
73 html_include(ctx.repo->readme); 73 if (!ctx.repo->readme)
74 html("</div>"); 74 return;
75 } 75
76 if (path) {
77 slash = strrchr(ctx.repo->readme, '/');
78 if (!slash)
79 return;
80 tmp = xmalloc(slash - ctx.repo->readme + 1 + strlen(path) + 1);
81 strncpy(tmp, ctx.repo->readme, slash - ctx.repo->readme + 1);
82 strcpy(tmp + (slash - ctx.repo->readme + 1), path);
83 } else
84 tmp = ctx.repo->readme;
85 html("<div id='summary'>");
86 if (ctx.repo->about_filter)
87 cgit_open_filter(ctx.repo->about_filter);
88 html_include(tmp);
89 if (ctx.repo->about_filter)
90 cgit_close_filter(ctx.repo->about_filter);
91 html("</div>");
76} 92}
diff --git a/ui-summary.h b/ui-summary.h
index 3e13039..c01f560 100644
--- a/ui-summary.h
+++ b/ui-summary.h
@@ -1,7 +1,7 @@
1#ifndef UI_SUMMARY_H 1#ifndef UI_SUMMARY_H
2#define UI_SUMMARY_H 2#define UI_SUMMARY_H
3 3
4extern void cgit_print_summary(); 4extern void cgit_print_summary();
5extern void cgit_print_repo_readme(); 5extern void cgit_print_repo_readme(char *path);
6 6
7#endif /* UI_SUMMARY_H */ 7#endif /* UI_SUMMARY_H */