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
@@ -132,128 +132,130 @@ void config_cb(const char *name, const char *value)
132 else if (!strcmp(name, "noheader")) 132 else if (!strcmp(name, "noheader"))
133 ctx.cfg.noheader = atoi(value); 133 ctx.cfg.noheader = atoi(value);
134 else if (!strcmp(name, "snapshots")) 134 else if (!strcmp(name, "snapshots"))
135 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); 135 ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
136 else if (!strcmp(name, "enable-filter-overrides")) 136 else if (!strcmp(name, "enable-filter-overrides"))
137 ctx.cfg.enable_filter_overrides = atoi(value); 137 ctx.cfg.enable_filter_overrides = atoi(value);
138 else if (!strcmp(name, "enable-gitweb-owner")) 138 else if (!strcmp(name, "enable-gitweb-owner"))
139 ctx.cfg.enable_gitweb_owner = atoi(value); 139 ctx.cfg.enable_gitweb_owner = atoi(value);
140 else if (!strcmp(name, "enable-index-links")) 140 else if (!strcmp(name, "enable-index-links"))
141 ctx.cfg.enable_index_links = atoi(value); 141 ctx.cfg.enable_index_links = atoi(value);
142 else if (!strcmp(name, "enable-log-filecount")) 142 else if (!strcmp(name, "enable-log-filecount"))
143 ctx.cfg.enable_log_filecount = atoi(value); 143 ctx.cfg.enable_log_filecount = atoi(value);
144 else if (!strcmp(name, "enable-log-linecount")) 144 else if (!strcmp(name, "enable-log-linecount"))
145 ctx.cfg.enable_log_linecount = atoi(value); 145 ctx.cfg.enable_log_linecount = atoi(value);
146 else if (!strcmp(name, "enable-remote-branches")) 146 else if (!strcmp(name, "enable-remote-branches"))
147 ctx.cfg.enable_remote_branches = atoi(value); 147 ctx.cfg.enable_remote_branches = atoi(value);
148 else if (!strcmp(name, "enable-subject-links")) 148 else if (!strcmp(name, "enable-subject-links"))
149 ctx.cfg.enable_subject_links = atoi(value); 149 ctx.cfg.enable_subject_links = atoi(value);
150 else if (!strcmp(name, "enable-tree-linenumbers")) 150 else if (!strcmp(name, "enable-tree-linenumbers"))
151 ctx.cfg.enable_tree_linenumbers = atoi(value); 151 ctx.cfg.enable_tree_linenumbers = atoi(value);
152 else if (!strcmp(name, "max-stats")) 152 else if (!strcmp(name, "max-stats"))
153 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); 153 ctx.cfg.max_stats = cgit_find_stats_period(value, NULL);
154 else if (!strcmp(name, "cache-size")) 154 else if (!strcmp(name, "cache-size"))
155 ctx.cfg.cache_size = atoi(value); 155 ctx.cfg.cache_size = atoi(value);
156 else if (!strcmp(name, "cache-root")) 156 else if (!strcmp(name, "cache-root"))
157 ctx.cfg.cache_root = xstrdup(expand_macros(value)); 157 ctx.cfg.cache_root = xstrdup(expand_macros(value));
158 else if (!strcmp(name, "cache-root-ttl")) 158 else if (!strcmp(name, "cache-root-ttl"))
159 ctx.cfg.cache_root_ttl = atoi(value); 159 ctx.cfg.cache_root_ttl = atoi(value);
160 else if (!strcmp(name, "cache-repo-ttl")) 160 else if (!strcmp(name, "cache-repo-ttl"))
161 ctx.cfg.cache_repo_ttl = atoi(value); 161 ctx.cfg.cache_repo_ttl = atoi(value);
162 else if (!strcmp(name, "cache-scanrc-ttl")) 162 else if (!strcmp(name, "cache-scanrc-ttl"))
163 ctx.cfg.cache_scanrc_ttl = atoi(value); 163 ctx.cfg.cache_scanrc_ttl = atoi(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 = "";
228 230
229 if (!strcmp(name,"r")) { 231 if (!strcmp(name,"r")) {
230 ctx.qry.repo = xstrdup(value); 232 ctx.qry.repo = xstrdup(value);
231 ctx.repo = cgit_get_repoinfo(value); 233 ctx.repo = cgit_get_repoinfo(value);
232 } else if (!strcmp(name, "p")) { 234 } else if (!strcmp(name, "p")) {
233 ctx.qry.page = xstrdup(value); 235 ctx.qry.page = xstrdup(value);
234 } else if (!strcmp(name, "url")) { 236 } else if (!strcmp(name, "url")) {
235 if (*value == '/') 237 if (*value == '/')
236 value++; 238 value++;
237 ctx.qry.url = xstrdup(value); 239 ctx.qry.url = xstrdup(value);
238 cgit_parse_url(value); 240 cgit_parse_url(value);
239 } else if (!strcmp(name, "qt")) { 241 } else if (!strcmp(name, "qt")) {
240 ctx.qry.grep = xstrdup(value); 242 ctx.qry.grep = xstrdup(value);
241 } else if (!strcmp(name, "q")) { 243 } else if (!strcmp(name, "q")) {
242 ctx.qry.search = xstrdup(value); 244 ctx.qry.search = xstrdup(value);
243 } else if (!strcmp(name, "h")) { 245 } else if (!strcmp(name, "h")) {
244 ctx.qry.head = xstrdup(value); 246 ctx.qry.head = xstrdup(value);
245 ctx.qry.has_symref = 1; 247 ctx.qry.has_symref = 1;
246 } else if (!strcmp(name, "id")) { 248 } else if (!strcmp(name, "id")) {
247 ctx.qry.sha1 = xstrdup(value); 249 ctx.qry.sha1 = xstrdup(value);
248 ctx.qry.has_sha1 = 1; 250 ctx.qry.has_sha1 = 1;
249 } else if (!strcmp(name, "id2")) { 251 } else if (!strcmp(name, "id2")) {
250 ctx.qry.sha2 = xstrdup(value); 252 ctx.qry.sha2 = xstrdup(value);
251 ctx.qry.has_sha1 = 1; 253 ctx.qry.has_sha1 = 1;
252 } else if (!strcmp(name, "ofs")) { 254 } else if (!strcmp(name, "ofs")) {
253 ctx.qry.ofs = atoi(value); 255 ctx.qry.ofs = atoi(value);
254 } else if (!strcmp(name, "path")) { 256 } else if (!strcmp(name, "path")) {
255 ctx.qry.path = trim_end(value, '/'); 257 ctx.qry.path = trim_end(value, '/');
256 } else if (!strcmp(name, "name")) { 258 } else if (!strcmp(name, "name")) {
257 ctx.qry.name = xstrdup(value); 259 ctx.qry.name = xstrdup(value);
258 } else if (!strcmp(name, "mimetype")) { 260 } else if (!strcmp(name, "mimetype")) {
259 ctx.qry.mimetype = xstrdup(value); 261 ctx.qry.mimetype = xstrdup(value);
diff --git a/cgit.h b/cgit.h
index 72fb1c7..f8076c5 100644
--- a/cgit.h
+++ b/cgit.h
@@ -145,128 +145,129 @@ struct cgit_query {
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 *readme;
172 char *robots; 172 char *robots;
173 char *root_title; 173 char *root_title;
174 char *root_desc; 174 char *root_desc;
175 char *root_readme; 175 char *root_readme;
176 char *script_name; 176 char *script_name;
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;
241 char *script_name; 242 char *script_name;
242 char *server_name; 243 char *server_name;
243 char *server_port; 244 char *server_port;
244}; 245};
245 246
246struct cgit_context { 247struct cgit_context {
247 struct cgit_environment env; 248 struct cgit_environment env;
248 struct cgit_query qry; 249 struct cgit_query qry;
249 struct cgit_config cfg; 250 struct cgit_config cfg;
250 struct cgit_repo *repo; 251 struct cgit_repo *repo;
251 struct cgit_page page; 252 struct cgit_page page;
252}; 253};
253 254
254struct cgit_snapshot_format { 255struct cgit_snapshot_format {
255 const char *suffix; 256 const char *suffix;
256 const char *mimetype; 257 const char *mimetype;
257 write_archive_fn_t write_func; 258 write_archive_fn_t write_func;
258 int bit; 259 int bit;
259}; 260};
260 261
261extern const char *cgit_version; 262extern const char *cgit_version;
262 263
263extern struct cgit_repolist cgit_repolist; 264extern struct cgit_repolist cgit_repolist;
264extern struct cgit_context ctx; 265extern struct cgit_context ctx;
265extern const struct cgit_snapshot_format cgit_snapshot_formats[]; 266extern const struct cgit_snapshot_format cgit_snapshot_formats[];
266 267
267extern struct cgit_repo *cgit_add_repo(const char *url); 268extern struct cgit_repo *cgit_add_repo(const char *url);
268extern struct cgit_repo *cgit_get_repoinfo(const char *url); 269extern struct cgit_repo *cgit_get_repoinfo(const char *url);
269extern void cgit_repo_config_cb(const char *name, const char *value); 270extern void cgit_repo_config_cb(const char *name, const char *value);
270 271
271extern int chk_zero(int result, char *msg); 272extern int chk_zero(int result, char *msg);
272extern int chk_positive(int result, char *msg); 273extern int chk_positive(int result, char *msg);
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 187031a..ce78d41 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -221,128 +221,134 @@ nocache::
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:: 237readme::
238 Text which will be used as default value for "repo.readme". Default 238 Text which will be used as default value for "repo.readme". Default
239 value: none. 239 value: none.
240 240
241remove-suffix:: 241remove-suffix::
242 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
243 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
244 name. Default value: "0". See also: scan-path. 244 name. Default value: "0". See also: scan-path.
245 245
246renamelimit:: 246renamelimit::
247 Maximum number of files to consider when detecting renames. The value 247 Maximum number of files to consider when detecting renames. The value
248 "-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
249 `man git-diff`). Default value: "-1". 249 `man git-diff`). Default value: "-1".
250 250
251repo.group:: 251repo.group::
252 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
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
317 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default 323 '/cgit/tree/README' as opposed to '?r=cgit&p=tree&path=README'. Default
318 value: none. 324 value: none.
319 NOTE: cgit has recently learned how to use PATH_INFO to achieve the 325 NOTE: cgit has recently learned how to use PATH_INFO to achieve the
320 same kind of virtual urls, so this option will probably be deprecated. 326 same kind of virtual urls, so this option will probably be deprecated.
321 327
322REPOSITORY SETTINGS 328REPOSITORY SETTINGS
323------------------- 329-------------------
324repo.about-filter:: 330repo.about-filter::
325 Override the default about-filter. Default value: none. See also: 331 Override the default about-filter. Default value: none. See also:
326 "enable-filter-overrides". 332 "enable-filter-overrides".
327 333
328repo.clone-url:: 334repo.clone-url::
329 A list of space-separated urls which can be used to clone this repo. 335 A list of space-separated urls which can be used to clone this repo.
330 Default value: none. 336 Default value: none.
331 337
332repo.commit-filter:: 338repo.commit-filter::
333 Override the default commit-filter. Default value: none. See also: 339 Override the default commit-filter. Default value: none. See also:
334 "enable-filter-overrides". 340 "enable-filter-overrides".
335 341
336repo.defbranch:: 342repo.defbranch::
337 The name of the default branch for this repository. If no such branch 343 The name of the default branch for this repository. If no such branch
338 exists in the repository, the first branch name (when sorted) is used 344 exists in the repository, the first branch name (when sorted) is used
339 as default instead. Default value: "master". 345 as default instead. Default value: "master".
340 346
341repo.desc:: 347repo.desc::
342 The value to show as repository description. Default value: none. 348 The value to show as repository description. Default value: none.
343 349
344repo.enable-log-filecount:: 350repo.enable-log-filecount::
345 A flag which can be used to disable the global setting 351 A flag which can be used to disable the global setting
346 `enable-log-filecount'. Default value: none. 352 `enable-log-filecount'. Default value: none.
347 353
348repo.enable-log-linecount:: 354repo.enable-log-linecount::
diff --git a/scan-tree.c b/scan-tree.c
index 780d405..b5b50f3 100644
--- a/scan-tree.c
+++ b/scan-tree.c
@@ -1,181 +1,211 @@
1/* scan-tree.c 1/* scan-tree.c
2 * 2 *
3 * Copyright (C) 2008-2009 Lars Hjemli 3 * Copyright (C) 2008-2009 Lars Hjemli
4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com> 4 * Copyright (C) 2010 Jason A. Donenfeld <Jason@zx2c4.com>
5 * 5 *
6 * Licensed under GNU General Public License v2 6 * Licensed under GNU General Public License v2
7 * (see COPYING for full license text) 7 * (see COPYING for full license text)
8 */ 8 */
9 9
10#include "cgit.h" 10#include "cgit.h"
11#include "configfile.h" 11#include "configfile.h"
12#include "html.h" 12#include "html.h"
13 13
14#define MAX_PATH 4096 14#define MAX_PATH 4096
15 15
16/* return 1 if path contains a objects/ directory and a HEAD file */ 16/* return 1 if path contains a objects/ directory and a HEAD file */
17static int is_git_dir(const char *path) 17static int is_git_dir(const char *path)
18{ 18{
19 struct stat st; 19 struct stat st;
20 static char buf[MAX_PATH]; 20 static char buf[MAX_PATH];
21 21
22 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) { 22 if (snprintf(buf, MAX_PATH, "%s/objects", path) >= MAX_PATH) {
23 fprintf(stderr, "Insanely long path: %s\n", path); 23 fprintf(stderr, "Insanely long path: %s\n", path);
24 return 0; 24 return 0;
25 } 25 }
26 if (stat(buf, &st)) { 26 if (stat(buf, &st)) {
27 if (errno != ENOENT) 27 if (errno != ENOENT)
28 fprintf(stderr, "Error checking path %s: %s (%d)\n", 28 fprintf(stderr, "Error checking path %s: %s (%d)\n",
29 path, strerror(errno), errno); 29 path, strerror(errno), errno);
30 return 0; 30 return 0;
31 } 31 }
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')
150 continue; 180 continue;
151 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0') 181 if (ent->d_name[1] == '.' && ent->d_name[2] == '\0')
152 continue; 182 continue;
153 } 183 }
154 buf = malloc(strlen(path) + strlen(ent->d_name) + 2); 184 buf = malloc(strlen(path) + strlen(ent->d_name) + 2);
155 if (!buf) { 185 if (!buf) {
156 fprintf(stderr, "Alloc error on %s: %s (%d)\n", 186 fprintf(stderr, "Alloc error on %s: %s (%d)\n",
157 path, strerror(errno), errno); 187 path, strerror(errno), errno);
158 exit(1); 188 exit(1);
159 } 189 }
160 sprintf(buf, "%s/%s", path, ent->d_name); 190 sprintf(buf, "%s/%s", path, ent->d_name);
161 if (stat(buf, &st)) { 191 if (stat(buf, &st)) {
162 fprintf(stderr, "Error checking path %s: %s (%d)\n", 192 fprintf(stderr, "Error checking path %s: %s (%d)\n",
163 buf, strerror(errno), errno); 193 buf, strerror(errno), errno);
164 free(buf); 194 free(buf);
165 continue; 195 continue;
166 } 196 }
167 if (S_ISDIR(st.st_mode)) 197 if (S_ISDIR(st.st_mode))
168 scan_path(base, buf, fn); 198 scan_path(base, buf, fn);
169 free(buf); 199 free(buf);
170 } 200 }
171 closedir(dir); 201 closedir(dir);
172} 202}
173 203
174#define lastc(s) s[strlen(s) - 1] 204#define lastc(s) s[strlen(s) - 1]
175 205
176void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn) 206void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn)
177{ 207{
178 char line[MAX_PATH * 2], *z; 208 char line[MAX_PATH * 2], *z;
179 FILE *projects; 209 FILE *projects;
180 int err; 210 int err;
181 211