summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2010-08-21 13:08:01 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2010-08-22 11:35:47 (UTC)
commit515edb0da3b9156e07e269621d7474cdea82acaf (patch) (unidiff)
treee5fed5e83f29166c23216437386e10070e1d258d
parent6d7552bc072599313ef423d69156d824c075572a (diff)
downloadcgit-515edb0da3b9156e07e269621d7474cdea82acaf.zip
cgit-515edb0da3b9156e07e269621d7474cdea82acaf.tar.gz
cgit-515edb0da3b9156e07e269621d7474cdea82acaf.tar.bz2
Add support for "readme" option
The value of this option is used as the default value for repo.readme. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c8
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt6
-rw-r--r--scan-tree.c8
-rw-r--r--shared.c2
-rw-r--r--ui-summary.c42
6 files changed, 43 insertions, 24 deletions
diff --git a/cgit.c b/cgit.c
index d6146e2..04cef23 100644
--- a/cgit.c
+++ b/cgit.c
@@ -43,89 +43,87 @@ struct cgit_filter *new_filter(const char *cmd, int extra_args)
43 43
44static void process_cached_repolist(const char *path); 44static void process_cached_repolist(const char *path);
45 45
46void repo_config(struct cgit_repo *repo, const char *name, const char *value) 46void repo_config(struct cgit_repo *repo, const char *name, const char *value)
47{ 47{
48 if (!strcmp(name, "name")) 48 if (!strcmp(name, "name"))
49 repo->name = xstrdup(value); 49 repo->name = xstrdup(value);
50 else if (!strcmp(name, "clone-url")) 50 else if (!strcmp(name, "clone-url"))
51 repo->clone_url = xstrdup(value); 51 repo->clone_url = xstrdup(value);
52 else if (!strcmp(name, "desc")) 52 else if (!strcmp(name, "desc"))
53 repo->desc = xstrdup(value); 53 repo->desc = xstrdup(value);
54 else if (!strcmp(name, "owner")) 54 else if (!strcmp(name, "owner"))
55 repo->owner = xstrdup(value); 55 repo->owner = xstrdup(value);
56 else if (!strcmp(name, "defbranch")) 56 else if (!strcmp(name, "defbranch"))
57 repo->defbranch = xstrdup(value); 57 repo->defbranch = xstrdup(value);
58 else if (!strcmp(name, "snapshots")) 58 else if (!strcmp(name, "snapshots"))
59 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value); 59 repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
60 else if (!strcmp(name, "enable-log-filecount")) 60 else if (!strcmp(name, "enable-log-filecount"))
61 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value); 61 repo->enable_log_filecount = ctx.cfg.enable_log_filecount * atoi(value);
62 else if (!strcmp(name, "enable-log-linecount")) 62 else if (!strcmp(name, "enable-log-linecount"))
63 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value); 63 repo->enable_log_linecount = ctx.cfg.enable_log_linecount * atoi(value);
64 else if (!strcmp(name, "enable-remote-branches")) 64 else if (!strcmp(name, "enable-remote-branches"))
65 repo->enable_remote_branches = atoi(value); 65 repo->enable_remote_branches = atoi(value);
66 else if (!strcmp(name, "enable-subject-links")) 66 else if (!strcmp(name, "enable-subject-links"))
67 repo->enable_subject_links = atoi(value); 67 repo->enable_subject_links = atoi(value);
68 else if (!strcmp(name, "max-stats")) 68 else if (!strcmp(name, "max-stats"))
69 repo->max_stats = cgit_find_stats_period(value, NULL); 69 repo->max_stats = cgit_find_stats_period(value, NULL);
70 else if (!strcmp(name, "module-link")) 70 else if (!strcmp(name, "module-link"))
71 repo->module_link= xstrdup(value); 71 repo->module_link= xstrdup(value);
72 else if (!strcmp(name, "section")) 72 else if (!strcmp(name, "section"))
73 repo->section = xstrdup(value); 73 repo->section = xstrdup(value);
74 else if (!strcmp(name, "readme") && value != NULL) { 74 else if (!strcmp(name, "readme") && value != NULL) {
75 char *colon; 75 repo->readme = xstrdup(value);
76 if (*value == '/' || ((colon = strchr(value, ':')) != NULL && colon != value && *(colon + 1) != '\0'))
77 repo->readme = xstrdup(value);
78 else
79 repo->readme = xstrdup(fmt("%s/%s", repo->path, value));
80 } else if (ctx.cfg.enable_filter_overrides) { 76 } else if (ctx.cfg.enable_filter_overrides) {
81 if (!strcmp(name, "about-filter")) 77 if (!strcmp(name, "about-filter"))
82 repo->about_filter = new_filter(value, 0); 78 repo->about_filter = new_filter(value, 0);
83 else if (!strcmp(name, "commit-filter")) 79 else if (!strcmp(name, "commit-filter"))
84 repo->commit_filter = new_filter(value, 0); 80 repo->commit_filter = new_filter(value, 0);
85 else if (!strcmp(name, "source-filter")) 81 else if (!strcmp(name, "source-filter"))
86 repo->source_filter = new_filter(value, 1); 82 repo->source_filter = new_filter(value, 1);
87 } 83 }
88} 84}
89 85
90void config_cb(const char *name, const char *value) 86void config_cb(const char *name, const char *value)
91{ 87{
92 if (!strcmp(name, "section") || !strcmp(name, "repo.group")) 88 if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
93 ctx.cfg.section = xstrdup(value); 89 ctx.cfg.section = xstrdup(value);
94 else if (!strcmp(name, "repo.url")) 90 else if (!strcmp(name, "repo.url"))
95 ctx.repo = cgit_add_repo(value); 91 ctx.repo = cgit_add_repo(value);
96 else if (ctx.repo && !strcmp(name, "repo.path")) 92 else if (ctx.repo && !strcmp(name, "repo.path"))
97 ctx.repo->path = trim_end(value, '/'); 93 ctx.repo->path = trim_end(value, '/');
98 else if (ctx.repo && !prefixcmp(name, "repo.")) 94 else if (ctx.repo && !prefixcmp(name, "repo."))
99 repo_config(ctx.repo, name + 5, value); 95 repo_config(ctx.repo, name + 5, value);
96 else if (!strcmp(name, "readme"))
97 ctx.cfg.readme = xstrdup(value);
100 else if (!strcmp(name, "root-title")) 98 else if (!strcmp(name, "root-title"))
101 ctx.cfg.root_title = xstrdup(value); 99 ctx.cfg.root_title = xstrdup(value);
102 else if (!strcmp(name, "root-desc")) 100 else if (!strcmp(name, "root-desc"))
103 ctx.cfg.root_desc = xstrdup(value); 101 ctx.cfg.root_desc = xstrdup(value);
104 else if (!strcmp(name, "root-readme")) 102 else if (!strcmp(name, "root-readme"))
105 ctx.cfg.root_readme = xstrdup(value); 103 ctx.cfg.root_readme = xstrdup(value);
106 else if (!strcmp(name, "css")) 104 else if (!strcmp(name, "css"))
107 ctx.cfg.css = xstrdup(value); 105 ctx.cfg.css = xstrdup(value);
108 else if (!strcmp(name, "favicon")) 106 else if (!strcmp(name, "favicon"))
109 ctx.cfg.favicon = xstrdup(value); 107 ctx.cfg.favicon = xstrdup(value);
110 else if (!strcmp(name, "footer")) 108 else if (!strcmp(name, "footer"))
111 ctx.cfg.footer = xstrdup(value); 109 ctx.cfg.footer = xstrdup(value);
112 else if (!strcmp(name, "head-include")) 110 else if (!strcmp(name, "head-include"))
113 ctx.cfg.head_include = xstrdup(value); 111 ctx.cfg.head_include = xstrdup(value);
114 else if (!strcmp(name, "header")) 112 else if (!strcmp(name, "header"))
115 ctx.cfg.header = xstrdup(value); 113 ctx.cfg.header = xstrdup(value);
116 else if (!strcmp(name, "logo")) 114 else if (!strcmp(name, "logo"))
117 ctx.cfg.logo = xstrdup(value); 115 ctx.cfg.logo = xstrdup(value);
118 else if (!strcmp(name, "index-header")) 116 else if (!strcmp(name, "index-header"))
119 ctx.cfg.index_header = xstrdup(value); 117 ctx.cfg.index_header = xstrdup(value);
120 else if (!strcmp(name, "index-info")) 118 else if (!strcmp(name, "index-info"))
121 ctx.cfg.index_info = xstrdup(value); 119 ctx.cfg.index_info = xstrdup(value);
122 else if (!strcmp(name, "logo-link")) 120 else if (!strcmp(name, "logo-link"))
123 ctx.cfg.logo_link = xstrdup(value); 121 ctx.cfg.logo_link = xstrdup(value);
124 else if (!strcmp(name, "module-link")) 122 else if (!strcmp(name, "module-link"))
125 ctx.cfg.module_link = xstrdup(value); 123 ctx.cfg.module_link = xstrdup(value);
126 else if (!strcmp(name, "virtual-root")) { 124 else if (!strcmp(name, "virtual-root")) {
127 ctx.cfg.virtual_root = trim_end(value, '/'); 125 ctx.cfg.virtual_root = trim_end(value, '/');
128 if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) 126 if (!ctx.cfg.virtual_root && (!strcmp(value, "/")))
129 ctx.cfg.virtual_root = ""; 127 ctx.cfg.virtual_root = "";
130 } else if (!strcmp(name, "nocache")) 128 } else if (!strcmp(name, "nocache"))
131 ctx.cfg.nocache = atoi(value); 129 ctx.cfg.nocache = atoi(value);
diff --git a/cgit.h b/cgit.h
index 4090cd4..72fb1c7 100644
--- a/cgit.h
+++ b/cgit.h
@@ -139,64 +139,65 @@ struct cgit_query {
139 char *sha2; 139 char *sha2;
140 char *path; 140 char *path;
141 char *name; 141 char *name;
142 char *mimetype; 142 char *mimetype;
143 char *url; 143 char *url;
144 char *period; 144 char *period;
145 int ofs; 145 int ofs;
146 int nohead; 146 int nohead;
147 char *sort; 147 char *sort;
148 int showmsg; 148 int showmsg;
149 int ssdiff; 149 int ssdiff;
150 int show_all; 150 int show_all;
151 int context; 151 int context;
152 int ignorews; 152 int ignorews;
153 char *vpath; 153 char *vpath;
154}; 154};
155 155
156struct cgit_config { 156struct cgit_config {
157 char *agefile; 157 char *agefile;
158 char *cache_root; 158 char *cache_root;
159 char *clone_prefix; 159 char *clone_prefix;
160 char *css; 160 char *css;
161 char *favicon; 161 char *favicon;
162 char *footer; 162 char *footer;
163 char *head_include; 163 char *head_include;
164 char *header; 164 char *header;
165 char *index_header; 165 char *index_header;
166 char *index_info; 166 char *index_info;
167 char *logo; 167 char *logo;
168 char *logo_link; 168 char *logo_link;
169 char *module_link; 169 char *module_link;
170 char *project_list; 170 char *project_list;
171 char *readme;
171 char *robots; 172 char *robots;
172 char *root_title; 173 char *root_title;
173 char *root_desc; 174 char *root_desc;
174 char *root_readme; 175 char *root_readme;
175 char *script_name; 176 char *script_name;
176 char *section; 177 char *section;
177 char *virtual_root; 178 char *virtual_root;
178 int cache_size; 179 int cache_size;
179 int cache_dynamic_ttl; 180 int cache_dynamic_ttl;
180 int cache_max_create_time; 181 int cache_max_create_time;
181 int cache_repo_ttl; 182 int cache_repo_ttl;
182 int cache_root_ttl; 183 int cache_root_ttl;
183 int cache_scanrc_ttl; 184 int cache_scanrc_ttl;
184 int cache_static_ttl; 185 int cache_static_ttl;
185 int embedded; 186 int embedded;
186 int enable_filter_overrides; 187 int enable_filter_overrides;
187 int enable_gitweb_owner; 188 int enable_gitweb_owner;
188 int enable_index_links; 189 int enable_index_links;
189 int enable_log_filecount; 190 int enable_log_filecount;
190 int enable_log_linecount; 191 int enable_log_linecount;
191 int enable_remote_branches; 192 int enable_remote_branches;
192 int enable_subject_links; 193 int enable_subject_links;
193 int enable_tree_linenumbers; 194 int enable_tree_linenumbers;
194 int local_time; 195 int local_time;
195 int max_atom_items; 196 int max_atom_items;
196 int max_repo_count; 197 int max_repo_count;
197 int max_commit_count; 198 int max_commit_count;
198 int max_lock_attempts; 199 int max_lock_attempts;
199 int max_msg_len; 200 int max_msg_len;
200 int max_repodesc_len; 201 int max_repodesc_len;
201 int max_blob_size; 202 int max_blob_size;
202 int max_stats; 203 int max_stats;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index c643fae..187031a 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -205,64 +205,68 @@ max-stats::
205 Set the default maximum statistics period. Valid values are "week", 205 Set the default maximum statistics period. Valid values are "week",
206 "month", "quarter" and "year". If unspecified, statistics are 206 "month", "quarter" and "year". If unspecified, statistics are
207 disabled. Default value: none. See also: "repo.max-stats". 207 disabled. Default value: none. See also: "repo.max-stats".
208 208
209mimetype.<ext>:: 209mimetype.<ext>::
210 Set the mimetype for the specified filename extension. This is used 210 Set the mimetype for the specified filename extension. This is used
211 by the `plain` command when returning blob content. 211 by the `plain` command when returning blob content.
212 212
213module-link:: 213module-link::
214 Text which will be used as the formatstring for a hyperlink when a 214 Text which will be used as the formatstring for a hyperlink when a
215 submodule is printed in a directory listing. The arguments for the 215 submodule is printed in a directory listing. The arguments for the
216 formatstring are the path and SHA1 of the submodule commit. Default 216 formatstring are the path and SHA1 of the submodule commit. Default
217 value: "./?repo=%s&page=commit&id=%s" 217 value: "./?repo=%s&page=commit&id=%s"
218 218
219nocache:: 219nocache::
220 If set to the value "1" caching will be disabled. This settings is 220 If set to the value "1" caching will be disabled. This settings is
221 deprecated, and will not be honored starting with cgit-1.0. Default 221 deprecated, and will not be honored starting with cgit-1.0. Default
222 value: "0". 222 value: "0".
223 223
224noplainemail:: 224noplainemail::
225 If set to "1" showing full author email adresses will be disabled. 225 If set to "1" showing full author email adresses will be disabled.
226 Default value: "0". 226 Default value: "0".
227 227
228noheader:: 228noheader::
229 Flag which, when set to "1", will make cgit omit the standard header 229 Flag which, when set to "1", will make cgit omit the standard header
230 on all pages. Default value: none. See also: "embedded". 230 on all pages. Default value: none. See also: "embedded".
231 231
232project-list:: 232project-list::
233 A list of subdirectories inside of scan-path, relative to it, that 233 A list of subdirectories inside of scan-path, relative to it, that
234 should loaded as git repositories. This must be defined prior to 234 should loaded as git repositories. This must be defined prior to
235 scan-path. Default value: none. See also: scan-path. 235 scan-path. Default value: none. See also: scan-path.
236 236
237readme::
238 Text which will be used as default value for "repo.readme". Default
239 value: none.
240
237remove-suffix:: 241remove-suffix::
238 If set to "1" and scan-path is enabled, if any repositories are found 242 If set to "1" and scan-path is enabled, if any repositories are found
239 with a suffix of ".git", this suffix will be removed for the url and 243 with a suffix of ".git", this suffix will be removed for the url and
240 name. Default value: "0". See also: scan-path. 244 name. Default value: "0". See also: scan-path.
241 245
242renamelimit:: 246renamelimit::
243 Maximum number of files to consider when detecting renames. The value 247 Maximum number of files to consider when detecting renames. The value
244 "-1" uses the compiletime value in git (for further info, look at 248 "-1" uses the compiletime value in git (for further info, look at
245 `man git-diff`). Default value: "-1". 249 `man git-diff`). Default value: "-1".
246 250
247repo.group:: 251repo.group::
248 Legacy alias for "section". This option is deprecated and will not be 252 Legacy alias for "section". This option is deprecated and will not be
249 supported in cgit-1.0. 253 supported in cgit-1.0.
250 254
251robots:: 255robots::
252 Text used as content for the "robots" meta-tag. Default value: 256 Text used as content for the "robots" meta-tag. Default value:
253 "index, nofollow". 257 "index, nofollow".
254 258
255root-desc:: 259root-desc::
256 Text printed below the heading on the repository index page. Default 260 Text printed below the heading on the repository index page. Default
257 value: "a fast webinterface for the git dscm". 261 value: "a fast webinterface for the git dscm".
258 262
259root-readme:: 263root-readme::
260 The content of the file specified with this option will be included 264 The content of the file specified with this option will be included
261 verbatim below the "about" link on the repository index page. Default 265 verbatim below the "about" link on the repository index page. Default
262 value: none. 266 value: none.
263 267
264root-title:: 268root-title::
265 Text printed as heading on the repository index page. Default value: 269 Text printed as heading on the repository index page. Default value:
266 "Git Repository Browser". 270 "Git Repository Browser".
267 271
268scan-path:: 272scan-path::
@@ -344,65 +348,65 @@ repo.enable-log-filecount::
344repo.enable-log-linecount:: 348repo.enable-log-linecount::
345 A flag which can be used to disable the global setting 349 A flag which can be used to disable the global setting
346 `enable-log-linecount'. Default value: none. 350 `enable-log-linecount'. Default value: none.
347 351
348repo.enable-remote-branches:: 352repo.enable-remote-branches::
349 Flag which, when set to "1", will make cgit display remote branches 353 Flag which, when set to "1", will make cgit display remote branches
350 in the summary and refs views. Default value: <enable-remote-branches>. 354 in the summary and refs views. Default value: <enable-remote-branches>.
351 355
352repo.enable-subject-links:: 356repo.enable-subject-links::
353 A flag which can be used to override the global setting 357 A flag which can be used to override the global setting
354 `enable-subject-links'. Default value: none. 358 `enable-subject-links'. Default value: none.
355 359
356repo.max-stats:: 360repo.max-stats::
357 Override the default maximum statistics period. Valid values are equal 361 Override the default maximum statistics period. Valid values are equal
358 to the values specified for the global "max-stats" setting. Default 362 to the values specified for the global "max-stats" setting. Default
359 value: none. 363 value: none.
360 364
361repo.name:: 365repo.name::
362 The value to show as repository name. Default value: <repo.url>. 366 The value to show as repository name. Default value: <repo.url>.
363 367
364repo.owner:: 368repo.owner::
365 A value used to identify the owner of the repository. Default value: 369 A value used to identify the owner of the repository. Default value:
366 none. 370 none.
367 371
368repo.path:: 372repo.path::
369 An absolute path to the repository directory. For non-bare repositories 373 An absolute path to the repository directory. For non-bare repositories
370 this is the .git-directory. Default value: none. 374 this is the .git-directory. Default value: none.
371 375
372repo.readme:: 376repo.readme::
373 A path (relative to <repo.path>) which specifies a file to include 377 A path (relative to <repo.path>) which specifies a file to include
374 verbatim as the "About" page for this repo. You may also specify a 378 verbatim as the "About" page for this repo. You may also specify a
375 git refspec by head or by hash by prepending the refspec followed by 379 git refspec by head or by hash by prepending the refspec followed by
376 a colon. For example, "master:docs/readme.mkd" Default value: none. 380 a colon. For example, "master:docs/readme.mkd" Default value: <readme>.
377 381
378repo.snapshots:: 382repo.snapshots::
379 A mask of allowed snapshot-formats for this repo, restricted by the 383 A mask of allowed snapshot-formats for this repo, restricted by the
380 "snapshots" global setting. Default value: <snapshots>. 384 "snapshots" global setting. Default value: <snapshots>.
381 385
382repo.section:: 386repo.section::
383 Override the current section name for this repository. Default value: 387 Override the current section name for this repository. Default value:
384 none. 388 none.
385 389
386repo.source-filter:: 390repo.source-filter::
387 Override the default source-filter. Default value: none. See also: 391 Override the default source-filter. Default value: none. See also:
388 "enable-filter-overrides". 392 "enable-filter-overrides".
389 393
390repo.url:: 394repo.url::
391 The relative url used to access the repository. This must be the first 395 The relative url used to access the repository. This must be the first
392 setting specified for each repo. Default value: none. 396 setting specified for each repo. Default value: none.
393 397
394 398
395REPOSITORY-SPECIFIC CGITRC FILE 399REPOSITORY-SPECIFIC CGITRC FILE
396------------------------------- 400-------------------------------
397When the option "scan-path" is used to auto-discover git repositories, cgit 401When the option "scan-path" is used to auto-discover git repositories, cgit
398will try to parse the file "cgitrc" within any found repository. Such a 402will try to parse the file "cgitrc" within any found repository. Such a
399repo-specific config file may contain any of the repo-specific options 403repo-specific config file may contain any of the repo-specific options
400described above, except "repo.url" and "repo.path". Additionally, the "filter" 404described above, except "repo.url" and "repo.path". Additionally, the "filter"
401options are only acknowledged in repo-specific config files when 405options are only acknowledged in repo-specific config files when
402"enable-filter-overrides" is set to "1". 406"enable-filter-overrides" is set to "1".
403 407
404Note: the "repo." prefix is dropped from the option names in repo-specific 408Note: the "repo." prefix is dropped from the option names in repo-specific
405config files, e.g. "repo.desc" becomes "desc". 409config files, e.g. "repo.desc" becomes "desc".
406 410
407 411
408EXAMPLE CGITRC FILE 412EXAMPLE CGITRC FILE
diff --git a/scan-tree.c b/scan-tree.c
index e987824..780d405 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -81,67 +81,69 @@ static void add_repo(const char *base, const char *path, repo_config_fn fn)
81 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL); 81 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
82 if (base == path) 82 if (base == path)
83 p = fmt("%s", path); 83 p = fmt("%s", path);
84 else 84 else
85 p = fmt("%s", path + strlen(base) + 1); 85 p = fmt("%s", path + strlen(base) + 1);
86 86
87 if (!strcmp(p + strlen(p) - 5, "/.git")) 87 if (!strcmp(p + strlen(p) - 5, "/.git"))
88 p[strlen(p) - 5] = '\0'; 88 p[strlen(p) - 5] = '\0';
89 89
90 repo = cgit_add_repo(xstrdup(p)); 90 repo = cgit_add_repo(xstrdup(p));
91 if (ctx.cfg.remove_suffix) 91 if (ctx.cfg.remove_suffix)
92 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) 92 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
93 *p = '\0'; 93 *p = '\0';
94 repo->name = repo->url; 94 repo->name = repo->url;
95 repo->path = xstrdup(path); 95 repo->path = xstrdup(path);
96 while (!owner) { 96 while (!owner) {
97 if ((pwd = getpwuid(st.st_uid)) == NULL) { 97 if ((pwd = getpwuid(st.st_uid)) == NULL) {
98 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 98 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
99 path, strerror(errno), errno); 99 path, strerror(errno), errno);
100 break; 100 break;
101 } 101 }
102 if (pwd->pw_gecos) 102 if (pwd->pw_gecos)
103 if ((p = strchr(pwd->pw_gecos, ','))) 103 if ((p = strchr(pwd->pw_gecos, ',')))
104 *p = '\0'; 104 *p = '\0';
105 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); 105 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
106 } 106 }
107 repo->owner = owner; 107 repo->owner = owner;
108 108
109 p = fmt("%s/description", path); 109 p = fmt("%s/description", path);
110 if (!stat(p, &st)) 110 if (!stat(p, &st))
111 readfile(p, &repo->desc, &size); 111 readfile(p, &repo->desc, &size);
112 112
113 p = fmt("%s/README.html", path); 113 if (!repo->readme) {
114 if (!stat(p, &st)) 114 p = fmt("%s/README.html", path);
115 repo->readme = "README.html"; 115 if (!stat(p, &st))
116 repo->readme = "README.html";
117 }
116 118
117 p = fmt("%s/cgitrc", path); 119 p = fmt("%s/cgitrc", path);
118 if (!stat(p, &st)) { 120 if (!stat(p, &st)) {
119 config_fn = fn; 121 config_fn = fn;
120 parse_configfile(xstrdup(p), &repo_config); 122 parse_configfile(xstrdup(p), &repo_config);
121 } 123 }
122} 124}
123 125
124static void scan_path(const char *base, const char *path, repo_config_fn fn) 126static void scan_path(const char *base, const char *path, repo_config_fn fn)
125{ 127{
126 DIR *dir; 128 DIR *dir;
127 struct dirent *ent; 129 struct dirent *ent;
128 char *buf; 130 char *buf;
129 struct stat st; 131 struct stat st;
130 132
131 if (is_git_dir(path)) { 133 if (is_git_dir(path)) {
132 add_repo(base, path, fn); 134 add_repo(base, path, fn);
133 return; 135 return;
134 } 136 }
135 if (is_git_dir(fmt("%s/.git", path))) { 137 if (is_git_dir(fmt("%s/.git", path))) {
136 add_repo(base, fmt("%s/.git", path), fn); 138 add_repo(base, fmt("%s/.git", path), fn);
137 return; 139 return;
138 } 140 }
139 dir = opendir(path); 141 dir = opendir(path);
140 if (!dir) { 142 if (!dir) {
141 fprintf(stderr, "Error opening directory %s: %s (%d)\n", 143 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
142 path, strerror(errno), errno); 144 path, strerror(errno), errno);
143 return; 145 return;
144 } 146 }
145 while((ent = readdir(dir)) != NULL) { 147 while((ent = readdir(dir)) != NULL) {
146 if (ent->d_name[0] == '.') { 148 if (ent->d_name[0] == '.') {
147 if (ent->d_name[1] == '\0') 149 if (ent->d_name[1] == '\0')
diff --git a/shared.c b/shared.c
index b42c2a2..72ac140 100644
--- a/shared.c
+++ b/shared.c
@@ -33,65 +33,65 @@ int chk_non_negative(int result, char *msg)
33} 33}
34 34
35struct cgit_repo *cgit_add_repo(const char *url) 35struct cgit_repo *cgit_add_repo(const char *url)
36{ 36{
37 struct cgit_repo *ret; 37 struct cgit_repo *ret;
38 38
39 if (++cgit_repolist.count > cgit_repolist.length) { 39 if (++cgit_repolist.count > cgit_repolist.length) {
40 if (cgit_repolist.length == 0) 40 if (cgit_repolist.length == 0)
41 cgit_repolist.length = 8; 41 cgit_repolist.length = 8;
42 else 42 else
43 cgit_repolist.length *= 2; 43 cgit_repolist.length *= 2;
44 cgit_repolist.repos = xrealloc(cgit_repolist.repos, 44 cgit_repolist.repos = xrealloc(cgit_repolist.repos,
45 cgit_repolist.length * 45 cgit_repolist.length *
46 sizeof(struct cgit_repo)); 46 sizeof(struct cgit_repo));
47 } 47 }
48 48
49 ret = &cgit_repolist.repos[cgit_repolist.count-1]; 49 ret = &cgit_repolist.repos[cgit_repolist.count-1];
50 memset(ret, 0, sizeof(struct cgit_repo)); 50 memset(ret, 0, sizeof(struct cgit_repo));
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->section = ctx.cfg.section; 56 ret->section = ctx.cfg.section;
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->enable_remote_branches = ctx.cfg.enable_remote_branches; 61 ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
62 ret->enable_subject_links = ctx.cfg.enable_subject_links; 62 ret->enable_subject_links = ctx.cfg.enable_subject_links;
63 ret->max_stats = ctx.cfg.max_stats; 63 ret->max_stats = ctx.cfg.max_stats;
64 ret->module_link = ctx.cfg.module_link; 64 ret->module_link = ctx.cfg.module_link;
65 ret->readme = NULL; 65 ret->readme = ctx.cfg.readme;
66 ret->mtime = -1; 66 ret->mtime = -1;
67 ret->about_filter = ctx.cfg.about_filter; 67 ret->about_filter = ctx.cfg.about_filter;
68 ret->commit_filter = ctx.cfg.commit_filter; 68 ret->commit_filter = ctx.cfg.commit_filter;
69 ret->source_filter = ctx.cfg.source_filter; 69 ret->source_filter = ctx.cfg.source_filter;
70 return ret; 70 return ret;
71} 71}
72 72
73struct cgit_repo *cgit_get_repoinfo(const char *url) 73struct cgit_repo *cgit_get_repoinfo(const char *url)
74{ 74{
75 int i; 75 int i;
76 struct cgit_repo *repo; 76 struct cgit_repo *repo;
77 77
78 for (i=0; i<cgit_repolist.count; i++) { 78 for (i=0; i<cgit_repolist.count; i++) {
79 repo = &cgit_repolist.repos[i]; 79 repo = &cgit_repolist.repos[i];
80 if (!strcmp(repo->url, url)) 80 if (!strcmp(repo->url, url))
81 return repo; 81 return repo;
82 } 82 }
83 return NULL; 83 return NULL;
84} 84}
85 85
86void *cgit_free_commitinfo(struct commitinfo *info) 86void *cgit_free_commitinfo(struct commitinfo *info)
87{ 87{
88 free(info->author); 88 free(info->author);
89 free(info->author_email); 89 free(info->author_email);
90 free(info->committer); 90 free(info->committer);
91 free(info->committer_email); 91 free(info->committer_email);
92 free(info->subject); 92 free(info->subject);
93 free(info->msg); 93 free(info->msg);
94 free(info->msg_encoding); 94 free(info->msg_encoding);
95 free(info); 95 free(info);
96 return NULL; 96 return NULL;
97} 97}
diff --git a/ui-summary.c b/ui-summary.c
index 02f191e..b203bcc 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -41,70 +41,84 @@ static void print_urls(char *txt, char *suffix)
41 h++; 41 h++;
42 t = h; 42 t = h;
43 while (t && *t && *t != ' ') 43 while (t && *t && *t != ' ')
44 t++; 44 t++;
45 c = *t; 45 c = *t;
46 *t = 0; 46 *t = 0;
47 print_url(h, suffix); 47 print_url(h, suffix);
48 *t = c; 48 *t = c;
49 h = t; 49 h = t;
50 } 50 }
51} 51}
52 52
53void cgit_print_summary() 53void cgit_print_summary()
54{ 54{
55 html("<table summary='repository info' class='list nowrap'>"); 55 html("<table summary='repository info' class='list nowrap'>");
56 cgit_print_branches(ctx.cfg.summary_branches); 56 cgit_print_branches(ctx.cfg.summary_branches);
57 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 57 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
58 cgit_print_tags(ctx.cfg.summary_tags); 58 cgit_print_tags(ctx.cfg.summary_tags);
59 if (ctx.cfg.summary_log > 0) { 59 if (ctx.cfg.summary_log > 0) {
60 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>"); 60 html("<tr class='nohover'><td colspan='4'>&nbsp;</td></tr>");
61 cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL, 61 cgit_print_log(ctx.qry.head, 0, ctx.cfg.summary_log, NULL,
62 NULL, NULL, 0); 62 NULL, NULL, 0);
63 } 63 }
64 if (ctx.repo->clone_url) 64 if (ctx.repo->clone_url)
65 print_urls(ctx.repo->clone_url, NULL); 65 print_urls(ctx.repo->clone_url, NULL);
66 else if (ctx.cfg.clone_prefix) 66 else if (ctx.cfg.clone_prefix)
67 print_urls(ctx.cfg.clone_prefix, ctx.repo->url); 67 print_urls(ctx.cfg.clone_prefix, ctx.repo->url);
68 html("</table>"); 68 html("</table>");
69} 69}
70 70
71void cgit_print_repo_readme(char *path) 71void cgit_print_repo_readme(char *path)
72{ 72{
73 char *slash, *tmp, *colon, *ref = 0; 73 char *slash, *tmp, *colon, *ref;
74 74
75 if (!ctx.repo->readme) 75 if (!ctx.repo->readme || !(*ctx.repo->readme))
76 return; 76 return;
77 77
78 ref = NULL;
79
80 /* Check if the readme is tracked in the git repo. */
81 colon = strchr(ctx.repo->readme, ':');
82 if (colon && strlen(colon) > 1) {
83 *colon = '\0';
84 ref = ctx.repo->readme;
85 ctx.repo->readme = colon + 1;
86 if (!(*ctx.repo->readme))
87 return;
88 }
89
90 /* Prepend repo path to relative readme path unless tracked. */
91 if (!ref && *ctx.repo->readme != '/')
92 ctx.repo->readme = xstrdup(fmt("%s/%s", ctx.repo->path,
93 ctx.repo->readme));
94
95 /* If a subpath is specified for the about page, make it relative
96 * to the directory containing the configured readme.
97 */
78 if (path) { 98 if (path) {
79 slash = strrchr(ctx.repo->readme, '/'); 99 slash = strrchr(ctx.repo->readme, '/');
80 if (!slash) { 100 if (!slash) {
81 slash = strchr(ctx.repo->readme, ':'); 101 if (!colon)
82 if (!slash)
83 return; 102 return;
103 slash = colon;
84 } 104 }
85 tmp = xmalloc(slash - ctx.repo->readme + 1 + strlen(path) + 1); 105 tmp = xmalloc(slash - ctx.repo->readme + 1 + strlen(path) + 1);
86 strncpy(tmp, ctx.repo->readme, slash - ctx.repo->readme + 1); 106 strncpy(tmp, ctx.repo->readme, slash - ctx.repo->readme + 1);
87 strcpy(tmp + (slash - ctx.repo->readme + 1), path); 107 strcpy(tmp + (slash - ctx.repo->readme + 1), path);
88 } else 108 } else
89 tmp = ctx.repo->readme; 109 tmp = ctx.repo->readme;
90 colon = strchr(tmp, ':'); 110
91 if (colon && strlen(colon) > 1) { 111 /* Print the calculated readme, either from the git repo or from the
92 *colon = '\0'; 112 * filesystem, while applying the about-filter.
93 ref = tmp; 113 */
94 tmp = colon + 1;
95 while ((*tmp == '/' || *tmp == ':') && *tmp != '\0')
96 ++tmp;
97 if (!(*tmp))
98 return;
99 }
100 html("<div id='summary'>"); 114 html("<div id='summary'>");
101 if (ctx.repo->about_filter) 115 if (ctx.repo->about_filter)
102 cgit_open_filter(ctx.repo->about_filter); 116 cgit_open_filter(ctx.repo->about_filter);
103 if (ref) 117 if (ref)
104 cgit_print_file(tmp, ref); 118 cgit_print_file(tmp, ref);
105 else 119 else
106 html_include(tmp); 120 html_include(tmp);
107 if (ctx.repo->about_filter) 121 if (ctx.repo->about_filter)
108 cgit_close_filter(ctx.repo->about_filter); 122 cgit_close_filter(ctx.repo->about_filter);
109 html("</div>"); 123 html("</div>");
110} 124}