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 d6146e2..5666875 100644
--- a/cgit.c
+++ b/cgit.c
@@ -174,48 +174,50 @@ void config_cb(const char *name, const char *value)
174 else if (!strcmp(name, "embedded")) 174 else if (!strcmp(name, "embedded"))
175 ctx.cfg.embedded = atoi(value); 175 ctx.cfg.embedded = atoi(value);
176 else if (!strcmp(name, "max-atom-items")) 176 else if (!strcmp(name, "max-atom-items"))
177 ctx.cfg.max_atom_items = atoi(value); 177 ctx.cfg.max_atom_items = atoi(value);
178 else if (!strcmp(name, "max-message-length")) 178 else if (!strcmp(name, "max-message-length"))
179 ctx.cfg.max_msg_len = atoi(value); 179 ctx.cfg.max_msg_len = atoi(value);
180 else if (!strcmp(name, "max-repodesc-length")) 180 else if (!strcmp(name, "max-repodesc-length"))
181 ctx.cfg.max_repodesc_len = atoi(value); 181 ctx.cfg.max_repodesc_len = atoi(value);
182 else if (!strcmp(name, "max-blob-size")) 182 else if (!strcmp(name, "max-blob-size"))
183 ctx.cfg.max_blob_size = atoi(value); 183 ctx.cfg.max_blob_size = atoi(value);
184 else if (!strcmp(name, "max-repo-count")) 184 else if (!strcmp(name, "max-repo-count"))
185 ctx.cfg.max_repo_count = atoi(value); 185 ctx.cfg.max_repo_count = atoi(value);
186 else if (!strcmp(name, "max-commit-count")) 186 else if (!strcmp(name, "max-commit-count"))
187 ctx.cfg.max_commit_count = atoi(value); 187 ctx.cfg.max_commit_count = atoi(value);
188 else if (!strcmp(name, "project-list")) 188 else if (!strcmp(name, "project-list"))
189 ctx.cfg.project_list = xstrdup(expand_macros(value)); 189 ctx.cfg.project_list = xstrdup(expand_macros(value));
190 else if (!strcmp(name, "scan-path")) 190 else if (!strcmp(name, "scan-path"))
191 if (!ctx.cfg.nocache && ctx.cfg.cache_size) 191 if (!ctx.cfg.nocache && ctx.cfg.cache_size)
192 process_cached_repolist(expand_macros(value)); 192 process_cached_repolist(expand_macros(value));
193 else if (ctx.cfg.project_list) 193 else if (ctx.cfg.project_list)
194 scan_projects(expand_macros(value), 194 scan_projects(expand_macros(value),
195 ctx.cfg.project_list, repo_config); 195 ctx.cfg.project_list, repo_config);
196 else 196 else
197 scan_tree(expand_macros(value), repo_config); 197 scan_tree(expand_macros(value), repo_config);
198 else if (!strcmp(name, "section-from-path"))
199 ctx.cfg.section_from_path = atoi(value);
198 else if (!strcmp(name, "source-filter")) 200 else if (!strcmp(name, "source-filter"))
199 ctx.cfg.source_filter = new_filter(value, 1); 201 ctx.cfg.source_filter = new_filter(value, 1);
200 else if (!strcmp(name, "summary-log")) 202 else if (!strcmp(name, "summary-log"))
201 ctx.cfg.summary_log = atoi(value); 203 ctx.cfg.summary_log = atoi(value);
202 else if (!strcmp(name, "summary-branches")) 204 else if (!strcmp(name, "summary-branches"))
203 ctx.cfg.summary_branches = atoi(value); 205 ctx.cfg.summary_branches = atoi(value);
204 else if (!strcmp(name, "summary-tags")) 206 else if (!strcmp(name, "summary-tags"))
205 ctx.cfg.summary_tags = atoi(value); 207 ctx.cfg.summary_tags = atoi(value);
206 else if (!strcmp(name, "side-by-side-diffs")) 208 else if (!strcmp(name, "side-by-side-diffs"))
207 ctx.cfg.ssdiff = atoi(value); 209 ctx.cfg.ssdiff = atoi(value);
208 else if (!strcmp(name, "agefile")) 210 else if (!strcmp(name, "agefile"))
209 ctx.cfg.agefile = xstrdup(value); 211 ctx.cfg.agefile = xstrdup(value);
210 else if (!strcmp(name, "renamelimit")) 212 else if (!strcmp(name, "renamelimit"))
211 ctx.cfg.renamelimit = atoi(value); 213 ctx.cfg.renamelimit = atoi(value);
212 else if (!strcmp(name, "remove-suffix")) 214 else if (!strcmp(name, "remove-suffix"))
213 ctx.cfg.remove_suffix = atoi(value); 215 ctx.cfg.remove_suffix = atoi(value);
214 else if (!strcmp(name, "robots")) 216 else if (!strcmp(name, "robots"))
215 ctx.cfg.robots = xstrdup(value); 217 ctx.cfg.robots = xstrdup(value);
216 else if (!strcmp(name, "clone-prefix")) 218 else if (!strcmp(name, "clone-prefix"))
217 ctx.cfg.clone_prefix = xstrdup(value); 219 ctx.cfg.clone_prefix = xstrdup(value);
218 else if (!strcmp(name, "local-time")) 220 else if (!strcmp(name, "local-time"))
219 ctx.cfg.local_time = atoi(value); 221 ctx.cfg.local_time = atoi(value);
220 else if (!prefixcmp(name, "mimetype.")) 222 else if (!prefixcmp(name, "mimetype."))
221 add_mimetype(name + 9, value); 223 add_mimetype(name + 9, value);
diff --git a/cgit.h b/cgit.h
index 4090cd4..9b269cc 100644
--- a/cgit.h
+++ b/cgit.h
@@ -184,48 +184,49 @@ struct cgit_config {
184 int cache_static_ttl; 184 int cache_static_ttl;
185 int embedded; 185 int embedded;
186 int enable_filter_overrides; 186 int enable_filter_overrides;
187 int enable_gitweb_owner; 187 int enable_gitweb_owner;
188 int enable_index_links; 188 int enable_index_links;
189 int enable_log_filecount; 189 int enable_log_filecount;
190 int enable_log_linecount; 190 int enable_log_linecount;
191 int enable_remote_branches; 191 int enable_remote_branches;
192 int enable_subject_links; 192 int enable_subject_links;
193 int enable_tree_linenumbers; 193 int enable_tree_linenumbers;
194 int local_time; 194 int local_time;
195 int max_atom_items; 195 int max_atom_items;
196 int max_repo_count; 196 int max_repo_count;
197 int max_commit_count; 197 int max_commit_count;
198 int max_lock_attempts; 198 int max_lock_attempts;
199 int max_msg_len; 199 int max_msg_len;
200 int max_repodesc_len; 200 int max_repodesc_len;
201 int max_blob_size; 201 int max_blob_size;
202 int max_stats; 202 int max_stats;
203 int nocache; 203 int nocache;
204 int noplainemail; 204 int noplainemail;
205 int noheader; 205 int noheader;
206 int renamelimit; 206 int renamelimit;
207 int remove_suffix; 207 int remove_suffix;
208 int section_from_path;
208 int snapshots; 209 int snapshots;
209 int summary_branches; 210 int summary_branches;
210 int summary_log; 211 int summary_log;
211 int summary_tags; 212 int summary_tags;
212 int ssdiff; 213 int ssdiff;
213 struct string_list mimetypes; 214 struct string_list mimetypes;
214 struct cgit_filter *about_filter; 215 struct cgit_filter *about_filter;
215 struct cgit_filter *commit_filter; 216 struct cgit_filter *commit_filter;
216 struct cgit_filter *source_filter; 217 struct cgit_filter *source_filter;
217}; 218};
218 219
219struct cgit_page { 220struct cgit_page {
220 time_t modified; 221 time_t modified;
221 time_t expires; 222 time_t expires;
222 size_t size; 223 size_t size;
223 char *mimetype; 224 char *mimetype;
224 char *charset; 225 char *charset;
225 char *filename; 226 char *filename;
226 char *etag; 227 char *etag;
227 char *title; 228 char *title;
228 int status; 229 int status;
229 char *statusmsg; 230 char *statusmsg;
230}; 231};
231 232
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index c643fae..95782df 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -257,48 +257,54 @@ root-desc::
257 value: "a fast webinterface for the git dscm". 257 value: "a fast webinterface for the git dscm".
258 258
259root-readme:: 259root-readme::
260 The content of the file specified with this option will be included 260 The content of the file specified with this option will be included
261 verbatim below the "about" link on the repository index page. Default 261 verbatim below the "about" link on the repository index page. Default
262 value: none. 262 value: none.
263 263
264root-title:: 264root-title::
265 Text printed as heading on the repository index page. Default value: 265 Text printed as heading on the repository index page. Default value:
266 "Git Repository Browser". 266 "Git Repository Browser".
267 267
268scan-path:: 268scan-path::
269 A path which will be scanned for repositories. If caching is enabled, 269 A path which will be scanned for repositories. If caching is enabled,
270 the result will be cached as a cgitrc include-file in the cache 270 the result will be cached as a cgitrc include-file in the cache
271 directory. If project-list has been defined prior to scan-path, 271 directory. If project-list has been defined prior to scan-path,
272 scan-path loads only the directories listed in the file pointed to by 272 scan-path loads only the directories listed in the file pointed to by
273 project-list. Default value: none. See also: cache-scanrc-ttl, 273 project-list. Default value: none. See also: cache-scanrc-ttl,
274 project-list. 274 project-list.
275 275
276section:: 276section::
277 The name of the current repository section - all repositories defined 277 The name of the current repository section - all repositories defined
278 after this option will inherit the current section name. Default value: 278 after this option will inherit the current section name. Default value:
279 none. 279 none.
280 280
281section-from-path::
282 A number which, if specified before scan-path, specifies how many
283 path elements from each repo path to use as a default section name.
284 If negative, cgit will discard the specified number of path elements
285 above the repo directory. Default value: 0.
286
281side-by-side-diffs:: 287side-by-side-diffs::
282 If set to "1" shows side-by-side diffs instead of unidiffs per 288 If set to "1" shows side-by-side diffs instead of unidiffs per
283 default. Default value: "0". 289 default. Default value: "0".
284 290
285snapshots:: 291snapshots::
286 Text which specifies the default set of snapshot formats generated by 292 Text which specifies the default set of snapshot formats generated by
287 cgit. The value is a space-separated list of zero or more of the 293 cgit. The value is a space-separated list of zero or more of the
288 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none. 294 values "tar", "tar.gz", "tar.bz2" and "zip". Default value: none.
289 295
290source-filter:: 296source-filter::
291 Specifies a command which will be invoked to format plaintext blobs 297 Specifies a command which will be invoked to format plaintext blobs
292 in the tree view. The command will get the blob content on its STDIN 298 in the tree view. The command will get the blob content on its STDIN
293 and the name of the blob as its only command line argument. The STDOUT 299 and the name of the blob as its only command line argument. The STDOUT
294 from the command will be included verbatim as the blob contents, i.e. 300 from the command will be included verbatim as the blob contents, i.e.
295 this can be used to implement e.g. syntax highlighting. Default value: 301 this can be used to implement e.g. syntax highlighting. Default value:
296 none. 302 none.
297 303
298summary-branches:: 304summary-branches::
299 Specifies the number of branches to display in the repository "summary" 305 Specifies the number of branches to display in the repository "summary"
300 view. Default value: "10". 306 view. Default value: "10".
301 307
302summary-log:: 308summary-log::
303 Specifies the number of log entries to display in the repository 309 Specifies the number of log entries to display in the repository
304 "summary" view. Default value: "10". 310 "summary" view. Default value: "10".
diff --git a/scan-tree.c b/scan-tree.c
index e987824..6ba9193 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -40,100 +40,130 @@ static int is_git_dir(const char *path)
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 p = fmt("%s/README.html", path); 121 p = fmt("%s/README.html", path);
114 if (!stat(p, &st)) 122 if (!stat(p, &st))
115 repo->readme = "README.html"; 123 repo->readme = "README.html";
124 if (ctx.cfg.section_from_path) {
125 n = ctx.cfg.section_from_path;
126 if (n > 0) {
127 slash = rel;
128 while (slash && n && (slash = strchr(slash, '/')))
129 n--;
130 } else {
131 slash = rel + strlen(rel);
132 while (slash && n && (slash = xstrrchr(rel, slash, '/')))
133 n++;
134 }
135 if (slash && !n) {
136 *slash = '\0';
137 repo->section = xstrdup(rel);
138 *slash = '/';
139 if (!prefixcmp(repo->name, repo->section)) {
140 repo->name += strlen(repo->section);
141 if (*repo->name == '/')
142 repo->name++;
143 }
144 }
145 }
116 146
117 p = fmt("%s/cgitrc", path); 147 p = fmt("%s/cgitrc", path);
118 if (!stat(p, &st)) { 148 if (!stat(p, &st)) {
119 config_fn = fn; 149 config_fn = fn;
120 parse_configfile(xstrdup(p), &repo_config); 150 parse_configfile(xstrdup(p), &repo_config);
121 } 151 }
122} 152}
123 153
124static void scan_path(const char *base, const char *path, repo_config_fn fn) 154static void scan_path(const char *base, const char *path, repo_config_fn fn)
125{ 155{
126 DIR *dir; 156 DIR *dir;
127 struct dirent *ent; 157 struct dirent *ent;
128 char *buf; 158 char *buf;
129 struct stat st; 159 struct stat st;
130 160
131 if (is_git_dir(path)) { 161 if (is_git_dir(path)) {
132 add_repo(base, path, fn); 162 add_repo(base, path, fn);
133 return; 163 return;
134 } 164 }
135 if (is_git_dir(fmt("%s/.git", path))) { 165 if (is_git_dir(fmt("%s/.git", path))) {
136 add_repo(base, fmt("%s/.git", path), fn); 166 add_repo(base, fmt("%s/.git", path), fn);
137 return; 167 return;
138 } 168 }
139 dir = opendir(path); 169 dir = opendir(path);