summaryrefslogtreecommitdiffabout
Unidiff
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.c2
-rw-r--r--cgit.h1
-rw-r--r--cgitrc.5.txt6
-rw-r--r--scan-tree.c42
4 files changed, 45 insertions, 6 deletions
diff --git a/cgit.c b/cgit.c
index 04cef23..e1d2216 100644
--- a/cgit.c
+++ b/cgit.c
@@ -164,64 +164,66 @@ void config_cb(const char *name, const char *value)
164 else if (!strcmp(name, "cache-static-ttl")) 164 else if (!strcmp(name, "cache-static-ttl"))
165 ctx.cfg.cache_static_ttl = atoi(value); 165 ctx.cfg.cache_static_ttl = atoi(value);
166 else if (!strcmp(name, "cache-dynamic-ttl")) 166 else if (!strcmp(name, "cache-dynamic-ttl"))
167 ctx.cfg.cache_dynamic_ttl = atoi(value); 167 ctx.cfg.cache_dynamic_ttl = atoi(value);
168 else if (!strcmp(name, "about-filter")) 168 else if (!strcmp(name, "about-filter"))
169 ctx.cfg.about_filter = new_filter(value, 0); 169 ctx.cfg.about_filter = new_filter(value, 0);
170 else if (!strcmp(name, "commit-filter")) 170 else if (!strcmp(name, "commit-filter"))
171 ctx.cfg.commit_filter = new_filter(value, 0); 171 ctx.cfg.commit_filter = new_filter(value, 0);
172 else if (!strcmp(name, "embedded")) 172 else if (!strcmp(name, "embedded"))
173 ctx.cfg.embedded = atoi(value); 173 ctx.cfg.embedded = atoi(value);
174 else if (!strcmp(name, "max-atom-items")) 174 else if (!strcmp(name, "max-atom-items"))
175 ctx.cfg.max_atom_items = atoi(value); 175 ctx.cfg.max_atom_items = atoi(value);
176 else if (!strcmp(name, "max-message-length")) 176 else if (!strcmp(name, "max-message-length"))
177 ctx.cfg.max_msg_len = atoi(value); 177 ctx.cfg.max_msg_len = atoi(value);
178 else if (!strcmp(name, "max-repodesc-length")) 178 else if (!strcmp(name, "max-repodesc-length"))
179 ctx.cfg.max_repodesc_len = atoi(value); 179 ctx.cfg.max_repodesc_len = atoi(value);
180 else if (!strcmp(name, "max-blob-size")) 180 else if (!strcmp(name, "max-blob-size"))
181 ctx.cfg.max_blob_size = atoi(value); 181 ctx.cfg.max_blob_size = atoi(value);
182 else if (!strcmp(name, "max-repo-count")) 182 else if (!strcmp(name, "max-repo-count"))
183 ctx.cfg.max_repo_count = atoi(value); 183 ctx.cfg.max_repo_count = atoi(value);
184 else if (!strcmp(name, "max-commit-count")) 184 else if (!strcmp(name, "max-commit-count"))
185 ctx.cfg.max_commit_count = atoi(value); 185 ctx.cfg.max_commit_count = atoi(value);
186 else if (!strcmp(name, "project-list")) 186 else if (!strcmp(name, "project-list"))
187 ctx.cfg.project_list = xstrdup(expand_macros(value)); 187 ctx.cfg.project_list = xstrdup(expand_macros(value));
188 else if (!strcmp(name, "scan-path")) 188 else if (!strcmp(name, "scan-path"))
189 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 189 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
190 process_cached_repolist(expand_macros(value)); 190 process_cached_repolist(expand_macros(value));
191 else if (ctx.cfg.project_list) 191 else if (ctx.cfg.project_list)
192 scan_projects(expand_macros(value), 192 scan_projects(expand_macros(value),
193 ctx.cfg.project_list, repo_config); 193 ctx.cfg.project_list, repo_config);
194 else 194 else
195 scan_tree(expand_macros(value), repo_config); 195 scan_tree(expand_macros(value), repo_config);
196 else if (!strcmp(name, "section-from-path"))
197 ctx.cfg.section_from_path = atoi(value);
196 else if (!strcmp(name, "source-filter")) 198 else if (!strcmp(name, "source-filter"))
197 ctx.cfg.source_filter = new_filter(value, 1); 199 ctx.cfg.source_filter = new_filter(value, 1);
198 else if (!strcmp(name, "summary-log")) 200 else if (!strcmp(name, "summary-log"))
199 ctx.cfg.summary_log = atoi(value); 201 ctx.cfg.summary_log = atoi(value);
200 else if (!strcmp(name, "summary-branches")) 202 else if (!strcmp(name, "summary-branches"))
201 ctx.cfg.summary_branches = atoi(value); 203 ctx.cfg.summary_branches = atoi(value);
202 else if (!strcmp(name, "summary-tags")) 204 else if (!strcmp(name, "summary-tags"))
203 ctx.cfg.summary_tags = atoi(value); 205 ctx.cfg.summary_tags = atoi(value);
204 else if (!strcmp(name, "side-by-side-diffs")) 206 else if (!strcmp(name, "side-by-side-diffs"))
205 ctx.cfg.ssdiff = atoi(value); 207 ctx.cfg.ssdiff = atoi(value);
206 else if (!strcmp(name, "agefile")) 208 else if (!strcmp(name, "agefile"))
207 ctx.cfg.agefile = xstrdup(value); 209 ctx.cfg.agefile = xstrdup(value);
208 else if (!strcmp(name, "renamelimit")) 210 else if (!strcmp(name, "renamelimit"))
209 ctx.cfg.renamelimit = atoi(value); 211 ctx.cfg.renamelimit = atoi(value);
210 else if (!strcmp(name, "remove-suffix")) 212 else if (!strcmp(name, "remove-suffix"))
211 ctx.cfg.remove_suffix = atoi(value); 213 ctx.cfg.remove_suffix = atoi(value);
212 else if (!strcmp(name, "robots")) 214 else if (!strcmp(name, "robots"))
213 ctx.cfg.robots = xstrdup(value); 215 ctx.cfg.robots = xstrdup(value);
214 else if (!strcmp(name, "clone-prefix")) 216 else if (!strcmp(name, "clone-prefix"))
215 ctx.cfg.clone_prefix = xstrdup(value); 217 ctx.cfg.clone_prefix = xstrdup(value);
216 else if (!strcmp(name, "local-time")) 218 else if (!strcmp(name, "local-time"))
217 ctx.cfg.local_time = atoi(value); 219 ctx.cfg.local_time = atoi(value);
218 else if (!prefixcmp(name, "mimetype.")) 220 else if (!prefixcmp(name, "mimetype."))
219 add_mimetype(name + 9, value); 221 add_mimetype(name + 9, value);
220 else if (!strcmp(name, "include")) 222 else if (!strcmp(name, "include"))
221 parse_configfile(expand_macros(value), config_cb); 223 parse_configfile(expand_macros(value), config_cb);
222} 224}
223 225
224static void querystring_cb(const char *name, const char *value) 226static void querystring_cb(const char *name, const char *value)
225{ 227{
226 if (!value) 228 if (!value)
227 value = ""; 229 value = "";
diff --git a/cgit.h b/cgit.h
index 72fb1c7..f8076c5 100644
--- a/cgit.h
+++ b/cgit.h
@@ -177,64 +177,65 @@ struct cgit_config {
177 char *section; 177 char *section;
178 char *virtual_root; 178 char *virtual_root;
179 int cache_size; 179 int cache_size;
180 int cache_dynamic_ttl; 180 int cache_dynamic_ttl;
181 int cache_max_create_time; 181 int cache_max_create_time;
182 int cache_repo_ttl; 182 int cache_repo_ttl;
183 int cache_root_ttl; 183 int cache_root_ttl;
184 int cache_scanrc_ttl; 184 int cache_scanrc_ttl;
185 int cache_static_ttl; 185 int cache_static_ttl;
186 int embedded; 186 int embedded;
187 int enable_filter_overrides; 187 int enable_filter_overrides;
188 int enable_gitweb_owner; 188 int enable_gitweb_owner;
189 int enable_index_links; 189 int enable_index_links;
190 int enable_log_filecount; 190 int enable_log_filecount;
191 int enable_log_linecount; 191 int enable_log_linecount;
192 int enable_remote_branches; 192 int enable_remote_branches;
193 int enable_subject_links; 193 int enable_subject_links;
194 int enable_tree_linenumbers; 194 int enable_tree_linenumbers;
195 int local_time; 195 int local_time;
196 int max_atom_items; 196 int max_atom_items;
197 int max_repo_count; 197 int max_repo_count;
198 int max_commit_count; 198 int max_commit_count;
199 int max_lock_attempts; 199 int max_lock_attempts;
200 int max_msg_len; 200 int max_msg_len;
201 int max_repodesc_len; 201 int max_repodesc_len;
202 int max_blob_size; 202 int max_blob_size;
203 int max_stats; 203 int max_stats;
204 int nocache; 204 int nocache;
205 int noplainemail; 205 int noplainemail;
206 int noheader; 206 int noheader;
207 int renamelimit; 207 int renamelimit;
208 int remove_suffix; 208 int remove_suffix;
209 int section_from_path;
209 int snapshots; 210 int snapshots;
210 int summary_branches; 211 int summary_branches;
211 int summary_log; 212 int summary_log;
212 int summary_tags; 213 int summary_tags;
213 int ssdiff; 214 int ssdiff;
214 struct string_list mimetypes; 215 struct string_list mimetypes;
215 struct cgit_filter *about_filter; 216 struct cgit_filter *about_filter;
216 struct cgit_filter *commit_filter; 217 struct cgit_filter *commit_filter;
217 struct cgit_filter *source_filter; 218 struct cgit_filter *source_filter;
218}; 219};
219 220
220struct cgit_page { 221struct cgit_page {
221 time_t modified; 222 time_t modified;
222 time_t expires; 223 time_t expires;
223 size_t size; 224 size_t size;
224 char *mimetype; 225 char *mimetype;
225 char *charset; 226 char *charset;
226 char *filename; 227 char *filename;
227 char *etag; 228 char *etag;
228 char *title; 229 char *title;
229 int status; 230 int status;
230 char *statusmsg; 231 char *statusmsg;
231}; 232};
232 233
233struct cgit_environment { 234struct cgit_environment {
234 char *cgit_config; 235 char *cgit_config;
235 char *http_host; 236 char *http_host;
236 char *https; 237 char *https;
237 char *no_http; 238 char *no_http;
238 char *path_info; 239 char *path_info;
239 char *query_string; 240 char *query_string;
240 char *request_method; 241 char *request_method;
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 187031a..ce78d41 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -253,64 +253,70 @@ repo.group::
253 supported in cgit-1.0. 253 supported in cgit-1.0.
254 254
255robots:: 255robots::
256 Text used as content for the "robots" meta-tag. Default value: 256 Text used as content for the "robots" meta-tag. Default value:
257 "index, nofollow". 257 "index, nofollow".
258 258
259root-desc:: 259root-desc::
260 Text printed below the heading on the repository index page. Default 260 Text printed below the heading on the repository index page. Default
261 value: "a fast webinterface for the git dscm". 261 value: "a fast webinterface for the git dscm".
262 262
263root-readme:: 263root-readme::
264 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
265 verbatim below the "about" link on the repository index page. Default 265 verbatim below the "about" link on the repository index page. Default
266 value: none. 266 value: none.
267 267
268root-title:: 268root-title::
269 Text printed as heading on the repository index page. Default value: 269 Text printed as heading on the repository index page. Default value:
270 "Git Repository Browser". 270 "Git Repository Browser".
271 271
272scan-path:: 272scan-path::
273 A path which will be scanned for repositories. If caching is enabled, 273 A path which will be scanned for repositories. If caching is enabled,
274 the result will be cached as a cgitrc include-file in the cache 274 the result will be cached as a cgitrc include-file in the cache
275 directory. If project-list has been defined prior to scan-path, 275 directory. If project-list has been defined prior to scan-path,
276 scan-path loads only the directories listed in the file pointed to by 276 scan-path loads only the directories listed in the file pointed to by
277 project-list. Default value: none. See also: cache-scanrc-ttl, 277 project-list. Default value: none. See also: cache-scanrc-ttl,
278 project-list. 278 project-list.
279 279
280section:: 280section::
281 The name of the current repository section - all repositories defined 281 The name of the current repository section - all repositories defined
282 after this option will inherit the current section name. Default value: 282 after this option will inherit the current section name. Default value:
283 none. 283 none.
284 284
285section-from-path::
286 A number which, if specified before scan-path, specifies how many
287 path elements from each repo path to use as a default section name.
288 If negative, cgit will discard the specified number of path elements
289 above the repo directory. Default value: 0.
290
285side-by-side-diffs:: 291side-by-side-diffs::
286 If set to "1" shows side-by-side diffs instead of unidiffs per 292 If set to "1" shows side-by-side diffs instead of unidiffs per
287 default. Default value: "0". 293 default. Default value: "0".
288 294
289snapshots:: 295snapshots::
290 Text which specifies the default set of snapshot formats generated by 296 Text which specifies the default set of snapshot formats generated by
291 cgit. The value is a space-separated list of zero or more of the 297 cgit. The value is a space-separated list of zero or more of the
292 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 298 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
293 299
294source-filter:: 300source-filter::
295 Specifies a command which will be invoked to format plaintext blobs 301 Specifies a command which will be invoked to format plaintext blobs
296 in the tree view. The command will get the blob content on its STDIN 302 in the tree view. The command will get the blob content on its STDIN
297 and the name of the blob as its only command line argument. The STDOUT 303 and the name of the blob as its only command line argument. The STDOUT
298 from the command will be included verbatim as the blob contents, i.e. 304 from the command will be included verbatim as the blob contents, i.e.
299 this can be used to implement e.g. syntax highlighting. Default value: 305 this can be used to implement e.g. syntax highlighting. Default value:
300 none. 306 none.
301 307
302summary-branches:: 308summary-branches::
303 Specifies the number of branches to display in the repository "summary" 309 Specifies the number of branches to display in the repository "summary"
304 view. Default value: "10". 310 view. Default value: "10".
305 311
306summary-log:: 312summary-log::
307 Specifies the number of log entries to display in the repository 313 Specifies the number of log entries to display in the repository
308 "summary" view. Default value: "10". 314 "summary" view. Default value: "10".
309 315
310summary-tags:: 316summary-tags::
311 Specifies the number of tags to display in the repository "summary" 317 Specifies the number of tags to display in the repository "summary"
312 view. Default value: "10". 318 view. Default value: "10".
313 319
314virtual-root:: 320virtual-root::
315 Url which, if specified, will be used as root for all cgit links. It 321 Url which, if specified, will be used as root for all cgit links. It
316 will also cause cgit to generate 'virtual urls', i.e. urls like 322 will also cause cgit to generate 'virtual urls', i.e. urls like
diff --git a/scan-tree.c b/scan-tree.c
index 780d405..b5b50f3 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -32,118 +32,148 @@ static int is_git_dir(const char *path)
32 if (!S_ISDIR(st.st_mode)) 32 if (!S_ISDIR(st.st_mode))
33 return 0; 33 return 0;
34 34
35 sprintf(buf, "%s/HEAD", path); 35 sprintf(buf, "%s/HEAD", path);
36 if (stat(buf, &st)) { 36 if (stat(buf, &st)) {
37 if (errno != ENOENT) 37 if (errno != ENOENT)
38 fprintf(stderr, "Error checking path %s: %s (%d)\n", 38 fprintf(stderr, "Error checking path %s: %s (%d)\n",
39 path, strerror(errno), errno); 39 path, strerror(errno), errno);
40 return 0; 40 return 0;
41 } 41 }
42 if (!S_ISREG(st.st_mode)) 42 if (!S_ISREG(st.st_mode))
43 return 0; 43 return 0;
44 44
45 return 1; 45 return 1;
46} 46}
47 47
48struct cgit_repo *repo; 48struct cgit_repo *repo;
49repo_config_fn config_fn; 49repo_config_fn config_fn;
50char *owner; 50char *owner;
51 51
52static void repo_config(const char *name, const char *value) 52static void repo_config(const char *name, const char *value)
53{ 53{
54 config_fn(repo, name, value); 54 config_fn(repo, name, value);
55} 55}
56 56
57static int git_owner_config(const char *key, const char *value, void *cb) 57static int git_owner_config(const char *key, const char *value, void *cb)
58{ 58{
59 if (!strcmp(key, "gitweb.owner")) 59 if (!strcmp(key, "gitweb.owner"))
60 owner = xstrdup(value); 60 owner = xstrdup(value);
61 return 0; 61 return 0;
62} 62}
63 63
64static char *xstrrchr(char *s, char *from, int c)
65{
66 while (from >= s && *from != c)
67 from--;
68 return from < s ? NULL : from;
69}
70
64static void add_repo(const char *base, const char *path, repo_config_fn fn) 71static void add_repo(const char *base, const char *path, repo_config_fn fn)
65{ 72{
66 struct stat st; 73 struct stat st;
67 struct passwd *pwd; 74 struct passwd *pwd;
68 char *p; 75 char *rel, *p, *slash;
76 int n;
69 size_t size; 77 size_t size;
70 78
71 if (stat(path, &st)) { 79 if (stat(path, &st)) {
72 fprintf(stderr, "Error accessing %s: %s (%d)\n", 80 fprintf(stderr, "Error accessing %s: %s (%d)\n",
73 path, strerror(errno), errno); 81 path, strerror(errno), errno);
74 return; 82 return;
75 } 83 }
76 if (!stat(fmt("%s/noweb", path), &st)) 84 if (!stat(fmt("%s/noweb", path), &st))
77 return; 85 return;
78 86
79 owner = NULL; 87 owner = NULL;
80 if (ctx.cfg.enable_gitweb_owner) 88 if (ctx.cfg.enable_gitweb_owner)
81 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL); 89 git_config_from_file(git_owner_config, fmt("%s/config", path), NULL);
82 if (base == path) 90 if (base == path)
83 p = fmt("%s", path); 91 rel = xstrdup(fmt("%s", path));
84 else 92 else
85 p = fmt("%s", path + strlen(base) + 1); 93 rel = xstrdup(fmt("%s", path + strlen(base) + 1));
86 94
87 if (!strcmp(p + strlen(p) - 5, "/.git")) 95 if (!strcmp(rel + strlen(rel) - 5, "/.git"))
88 p[strlen(p) - 5] = '\0'; 96 rel[strlen(rel) - 5] = '\0';
89 97
90 repo = cgit_add_repo(xstrdup(p)); 98 repo = cgit_add_repo(rel);
91 if (ctx.cfg.remove_suffix) 99 if (ctx.cfg.remove_suffix)
92 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git")) 100 if ((p = strrchr(repo->url, '.')) && !strcmp(p, ".git"))
93 *p = '\0'; 101 *p = '\0';
94 repo->name = repo->url; 102 repo->name = repo->url;
95 repo->path = xstrdup(path); 103 repo->path = xstrdup(path);
96 while (!owner) { 104 while (!owner) {
97 if ((pwd = getpwuid(st.st_uid)) == NULL) { 105 if ((pwd = getpwuid(st.st_uid)) == NULL) {
98 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", 106 fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n",
99 path, strerror(errno), errno); 107 path, strerror(errno), errno);
100 break; 108 break;
101 } 109 }
102 if (pwd->pw_gecos) 110 if (pwd->pw_gecos)
103 if ((p = strchr(pwd->pw_gecos, ','))) 111 if ((p = strchr(pwd->pw_gecos, ',')))
104 *p = '\0'; 112 *p = '\0';
105 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name); 113 owner = xstrdup(pwd->pw_gecos ? pwd->pw_gecos : pwd->pw_name);
106 } 114 }
107 repo->owner = owner; 115 repo->owner = owner;
108 116
109 p = fmt("%s/description", path); 117 p = fmt("%s/description", path);
110 if (!stat(p, &st)) 118 if (!stat(p, &st))
111 readfile(p, &repo->desc, &size); 119 readfile(p, &repo->desc, &size);
112 120
113 if (!repo->readme) { 121 if (!repo->readme) {
114 p = fmt("%s/README.html", path); 122 p = fmt("%s/README.html", path);
115 if (!stat(p, &st)) 123 if (!stat(p, &st))
116 repo->readme = "README.html"; 124 repo->readme = "README.html";
117 } 125 }
126 if (ctx.cfg.section_from_path) {
127 n = ctx.cfg.section_from_path;
128 if (n > 0) {
129 slash = rel;
130 while (slash && n && (slash = strchr(slash, '/')))
131 n--;
132 } else {
133 slash = rel + strlen(rel);
134 while (slash && n && (slash = xstrrchr(rel, slash, '/')))
135 n++;
136 }
137 if (slash && !n) {
138 *slash = '\0';
139 repo->section = xstrdup(rel);
140 *slash = '/';
141 if (!prefixcmp(repo->name, repo->section)) {
142 repo->name += strlen(repo->section);
143 if (*repo->name == '/')
144 repo->name++;
145 }
146 }
147 }
118 148
119 p = fmt("%s/cgitrc", path); 149 p = fmt("%s/cgitrc", path);
120 if (!stat(p, &st)) { 150 if (!stat(p, &st)) {
121 config_fn = fn; 151 config_fn = fn;
122 parse_configfile(xstrdup(p), &repo_config); 152 parse_configfile(xstrdup(p), &repo_config);
123 } 153 }
124} 154}
125 155
126static void scan_path(const char *base, const char *path, repo_config_fn fn) 156static void scan_path(const char *base, const char *path, repo_config_fn fn)
127{ 157{
128 DIR *dir; 158 DIR *dir;
129 struct dirent *ent; 159 struct dirent *ent;
130 char *buf; 160 char *buf;
131 struct stat st; 161 struct stat st;
132 162
133 if (is_git_dir(path)) { 163 if (is_git_dir(path)) {
134 add_repo(base, path, fn); 164 add_repo(base, path, fn);
135 return; 165 return;
136 } 166 }
137 if (is_git_dir(fmt("%s/.git", path))) { 167 if (is_git_dir(fmt("%s/.git", path))) {
138 add_repo(base, fmt("%s/.git", path), fn); 168 add_repo(base, fmt("%s/.git", path), fn);
139 return; 169 return;
140 } 170 }
141 dir = opendir(path); 171 dir = opendir(path);
142 if (!dir) { 172 if (!dir) {
143 fprintf(stderr, "Error opening directory %s: %s (%d)\n", 173 fprintf(stderr, "Error opening directory %s: %s (%d)\n",
144 path, strerror(errno), errno); 174 path, strerror(errno), errno);
145 return; 175 return;
146 } 176 }
147 while((ent = readdir(dir)) != NULL) { 177 while((ent = readdir(dir)) != NULL) {
148 if (ent->d_name[0] == '.') { 178 if (ent->d_name[0] == '.') {
149 if (ent->d_name[1] == '\0') 179 if (ent->d_name[1] == '\0')